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,68 @@ return false; } + const FormatToken *parseCpp11Attribute(const FormatToken &Tok, + bool NamespaceAllowed) { + if (!Tok.isOneOf(tok::identifier, tok::ellipsis)) + return nullptr; + const FormatToken *AttrTok = Tok.Next; + if (!AttrTok) + return nullptr; + if (NamespaceAllowed && + AttrTok->startsSequence(tok::coloncolon, tok::identifier)) { + AttrTok = AttrTok->Next->Next; + } + if (!AttrTok) + return nullptr; + if (AttrTok->is(tok::l_paren)) { + const FormatToken *ParamToken = AttrTok->Next; + while (ParamToken && ParamToken->isNot(tok::r_paren)) + ParamToken = ParamToken->Next; + if (!ParamToken) + return nullptr; + AttrTok = ParamToken->Next; + } + return AttrTok; + } + + // Look for [[ ... ]] which is a valid C++11 attribute specifier but + // never a valid Objective-C or Objective-C++ method invocation. + bool parseCpp11AttributeSpecifier(const FormatToken &Tok) { + if (!Style.isCpp()) + return false; + if (!Tok.startsSequence(tok::l_square, tok::l_square)) + return false; + const FormatToken *AttrTok = Tok.Next->Next; + if (!AttrTok) + return false; + bool NamespaceAllowed; + // C++17 '[[using namespace: foo, bar(baz, blech)]]' + if (AttrTok->startsSequence(tok::kw_using, tok::identifier, tok::colon)) { + AttrTok = AttrTok->Next->Next->Next; + NamespaceAllowed = false; + } else { + // C++11 '[[namespace::foo, namespace::bar(baz, blech)]]' + NamespaceAllowed = true; + } + while (AttrTok) { + AttrTok = parseCpp11Attribute(*AttrTok, NamespaceAllowed); + if (!AttrTok || AttrTok->isNot(tok::comma)) + break; + AttrTok = AttrTok->Next; + } + if (!AttrTok) + return false; + return AttrTok->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 +394,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 @@ -12080,6 +12080,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