benhamilton created this revision.
benhamilton added reviewers: krasimir, jolesiak.
Herald added a subscriber: cfe-commits.

Previously, clang-format would detect C++11 and C++17 attribute
specifiers like the following as Objective-C method invocations:

  [[noreturn]];
  [[clang::fallthrough]];
  [[noreturn, deprecated("so sorry")]];
  [[using gsl: suppress("type")]];

To fix this, I ported part of the logic from
tools/clang/lib/Parse/ParseTentative.cpp into TokenAnnotator.cpp so we
can explicitly parse and identify C++11 attribute specifiers.

This allows the guessLanguage() and getStyle() APIs to correctly
guess files containing the C++11 attribute specifiers as C++,
not Objective-C.

Test Plan: New tests added. Ran tests with:

  make -j12 FormatTests && ./tools/clang/unittests/Format/FormatTests


Repository:
  rC Clang

https://reviews.llvm.org/D43902

Files:
  lib/Format/FormatToken.h
  lib/Format/TokenAnnotator.cpp
  unittests/Format/FormatTest.cpp

Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -11969,6 +11969,36 @@
   EXPECT_EQ(FormatStyle::LK_ObjC, guessLanguage("foo", "@interface Foo\n@end\n"));
 }
 
+TEST_F(FormatTest, GuessLanguageWithCpp11AttributeSpecifiers) {
+  EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[noreturn]];"));
+  EXPECT_EQ(FormatStyle::LK_ObjC,
+            guessLanguage("foo.h", "array[[calculator getIndex]];"));
+  EXPECT_EQ(FormatStyle::LK_Cpp,
+            guessLanguage("foo.h", "[[noreturn, deprecated(\"so sorry\")]];"));
+  EXPECT_EQ(
+      FormatStyle::LK_Cpp,
+      guessLanguage("foo.h", "[[noreturn, deprecated(\"gone, sorry\")]];"));
+  EXPECT_EQ(FormatStyle::LK_ObjC,
+            guessLanguage("foo.h", "[[noreturn foo] bar];"));
+  EXPECT_EQ(FormatStyle::LK_Cpp,
+            guessLanguage("foo.h", "[[clang::fallthrough]];"));
+  EXPECT_EQ(FormatStyle::LK_ObjC,
+            guessLanguage("foo.h", "[[clang:fallthrough] foo];"));
+  EXPECT_EQ(FormatStyle::LK_Cpp,
+            guessLanguage("foo.h", "[[gsl::suppress(\"type\")]];"));
+  EXPECT_EQ(FormatStyle::LK_Cpp,
+            guessLanguage("foo.h", "[[using clang: fallthrough]];"));
+  EXPECT_EQ(FormatStyle::LK_ObjC,
+            guessLanguage("foo.h", "[[abusing clang:fallthrough] bar];"));
+  EXPECT_EQ(FormatStyle::LK_Cpp,
+            guessLanguage("foo.h", "[[using gsl: suppress(\"type\")]];"));
+  EXPECT_EQ(
+      FormatStyle::LK_Cpp,
+      guessLanguage("foo.h",
+                    "[[clang::callable_when(\"unconsumed\", \"unknown\")]]"));
+  EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[foo::bar, ...]]"));
+}
+
 } // end namespace
 } // end namespace format
 } // end namespace clang
Index: lib/Format/TokenAnnotator.cpp
===================================================================
--- lib/Format/TokenAnnotator.cpp
+++ lib/Format/TokenAnnotator.cpp
@@ -320,13 +320,65 @@
     return false;
   }
 
+  const FormatToken *parseCpp11Attribute(const FormatToken *Tok,
+                                         bool NamespaceAllowed) {
+    if (!Tok || !Tok->isOneOf(tok::identifier, tok::ellipsis)) return Tok;
+    Tok = Tok->Next;
+    if (!Tok) return nullptr;
+    if (NamespaceAllowed &&
+        Tok->startsSequence(tok::coloncolon, tok::identifier)) {
+      Tok = Tok->Next->Next;
+    }
+    if (!Tok) return nullptr;
+    if (Tok->is(tok::l_paren)) {
+      const FormatToken *ParamToken = Tok->Next;
+      while (ParamToken && ParamToken->isNot(tok::r_paren))
+        ParamToken = ParamToken->Next;
+      if (!ParamToken || ParamToken->isNot(tok::r_paren)) return nullptr;
+      Tok = ParamToken->Next;
+    }
+    return Tok;
+  }
+
+  // Look for [[ ... ]] which is a valid C++11 attribute specifier but
+  // never a valid Objective-C or Objective-C++ method invocation.
+  bool parseCpp11AttributeSpecifier(FormatToken *Tok) {
+    if (!Style.isCpp()) return false;
+    if (!Tok || !Tok->startsSequence(tok::l_square, tok::l_square))
+      return false;
+    const FormatToken *AttributeToken = Tok->Next->Next;
+    if (!AttributeToken) return false;
+    // C++17 '[[using namespace: foo, bar(baz, blech)]]'
+    if (AttributeToken->startsSequence(tok::kw_using, tok::identifier,
+                                       tok::colon)) {
+      AttributeToken = AttributeToken->Next->Next->Next;
+      while (AttributeToken) {
+        AttributeToken =
+            parseCpp11Attribute(AttributeToken, /*NamespaceAllowed=*/false);
+        if (!AttributeToken || AttributeToken->isNot(tok::comma)) break;
+        AttributeToken = AttributeToken->Next;
+      }
+    } else {
+      // C++11 '[[namespace::foo, namespace::bar(baz, blech)]]'
+      while (AttributeToken) {
+        AttributeToken =
+            parseCpp11Attribute(AttributeToken, /*NamespaceAllowed=*/true);
+        if (!AttributeToken || AttributeToken->isNot(tok::comma)) break;
+        AttributeToken = AttributeToken->Next;
+      }
+    }
+    if (!AttributeToken) return false;
+    return AttributeToken->startsSequence(tok::r_square, tok::r_square);
+  }
+
   bool parseSquare() {
     if (!CurrentToken)
       return false;
 
     // A '[' could be an index subscript (after an identifier or after
     // ')' or ']'), it could be the start of an Objective-C method
-    // expression, or it could the start of an Objective-C array literal.
+    // expression, it could the start of an Objective-C array literal,
+    // or it could be a C++ attribute specifier [[foo::bar]].
     FormatToken *Left = CurrentToken->Previous;
     Left->ParentBracket = Contexts.back().ContextKind;
     FormatToken *Parent = Left->getPreviousNonComment();
@@ -339,6 +391,12 @@
         (Contexts.back().CanBeExpression || Contexts.back().IsExpression ||
          Contexts.back().InTemplateArgument);
 
+    if (parseCpp11AttributeSpecifier(Left)) {
+      CurrentToken->Type = TT_AttributeSpecifier;
+      next();
+      return true;
+    }
+
     bool StartsObjCMethodExpr =
         !CppArrayTemplates && Style.isCpp() &&
         Contexts.back().CanBeExpression && Left->isNot(TT_LambdaLSquare) &&
Index: lib/Format/FormatToken.h
===================================================================
--- lib/Format/FormatToken.h
+++ lib/Format/FormatToken.h
@@ -30,6 +30,7 @@
   TYPE(ArrayInitializerLSquare)                                                \
   TYPE(ArraySubscriptLSquare)                                                  \
   TYPE(AttributeParen)                                                         \
+  TYPE(AttributeSpecifier)                                                     \
   TYPE(BinaryOperator)                                                         \
   TYPE(BitFieldColon)                                                          \
   TYPE(BlockComment)                                                           \
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to