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) \ 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: 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