diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -71,6 +71,38 @@ tok::kw_constexpr, tok::kw_catch); } +/// Returns \c true if the token starts a C++ attribute, \c false otherwise. +static bool isCppAttribute(bool IsCpp, const FormatToken &Tok) { + if (!IsCpp || !Tok.startsSequence(tok::l_square, tok::l_square)) + return false; + // The first square bracket is part of an ObjC array literal + if (Tok.Previous && Tok.Previous->is(tok::at)) + return false; + const FormatToken *AttrTok = Tok.Next->Next; + if (!AttrTok) + return false; + // C++17 '[[using ns: foo, bar(baz, blech)]]' + // We assume nobody will name an ObjC variable 'using'. + if (AttrTok->startsSequence(tok::kw_using, tok::identifier, tok::colon)) + return true; + if (AttrTok->isNot(tok::identifier)) + return false; + while (AttrTok && !AttrTok->startsSequence(tok::r_square, tok::r_square)) { + // ObjC message send. We assume nobody will use : in a C++11 attribute + // specifier parameter, although this is technically valid: + // [[foo(:)]]. + if (AttrTok->is(tok::colon) || + AttrTok->startsSequence(tok::identifier, tok::identifier) || + AttrTok->startsSequence(tok::r_paren, tok::identifier)) { + return false; + } + if (AttrTok->is(tok::ellipsis)) + return true; + AttrTok = AttrTok->Next; + } + return AttrTok && AttrTok->startsSequence(tok::r_square, tok::r_square); +} + /// A parser that gathers additional information about tokens. /// /// The \c TokenAnnotator tries to match parenthesis and square brakets and @@ -538,34 +570,7 @@ } bool isCpp11AttributeSpecifier(const FormatToken &Tok) { - if (!Style.isCpp() || !Tok.startsSequence(tok::l_square, tok::l_square)) - return false; - // The first square bracket is part of an ObjC array literal - if (Tok.Previous && Tok.Previous->is(tok::at)) - return false; - const FormatToken *AttrTok = Tok.Next->Next; - if (!AttrTok) - return false; - // C++17 '[[using ns: foo, bar(baz, blech)]]' - // We assume nobody will name an ObjC variable 'using'. - if (AttrTok->startsSequence(tok::kw_using, tok::identifier, tok::colon)) - return true; - if (AttrTok->isNot(tok::identifier)) - return false; - while (AttrTok && !AttrTok->startsSequence(tok::r_square, tok::r_square)) { - // ObjC message send. We assume nobody will use : in a C++11 attribute - // specifier parameter, although this is technically valid: - // [[foo(:)]]. - if (AttrTok->is(tok::colon) || - AttrTok->startsSequence(tok::identifier, tok::identifier) || - AttrTok->startsSequence(tok::r_paren, tok::identifier)) { - return false; - } - if (AttrTok->is(tok::ellipsis)) - return true; - AttrTok = AttrTok->Next; - } - return AttrTok && AttrTok->startsSequence(tok::r_square, tok::r_square); + return isCppAttribute(Style.isCpp(), Tok); } bool parseSquare() { @@ -2844,6 +2849,8 @@ } if (!Next->is(tok::identifier)) return false; + } else if (isCppAttribute(IsCpp, *Next)) { + Next = Next->MatchingParen; } else if (Next->is(tok::l_paren)) { break; } else { diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -1047,6 +1047,16 @@ EXPECT_TOKEN(Tokens[8], tok::r_paren, TT_Unknown); } +TEST_F(TokenAnnotatorTest, UnderstandsFunctionDeclarationNames) { + auto Tokens = annotate("void f [[noreturn]] ();"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::identifier, TT_FunctionDeclarationName); + + Tokens = annotate("void f [[noreturn]] () {}"); + ASSERT_EQ(Tokens.size(), 12u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::identifier, TT_FunctionDeclarationName); +} + TEST_F(TokenAnnotatorTest, UnderstandsVerilogOperators) { auto Annotate = [this](llvm::StringRef Code) { return annotate(Code, getLLVMStyle(FormatStyle::LK_Verilog));