[clang-format] Correctly annotate function names before attributes

Fixes #58827.

Differential Revision: https://reviews.llvm.org/D137486
This commit is contained in:
Owen Pan 2022-11-05 04:09:13 -07:00
parent a9f9520907
commit 96e23906b5
2 changed files with 45 additions and 28 deletions

View File

@ -71,6 +71,38 @@ static bool isKeywordWithCondition(const FormatToken &Tok) {
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 @@ private:
}
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 @@ static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
}
if (!Next->is(tok::identifier))
return false;
} else if (isCppAttribute(IsCpp, *Next)) {
Next = Next->MatchingParen;
} else if (Next->is(tok::l_paren)) {
break;
} else {

View File

@ -1047,6 +1047,16 @@ TEST_F(TokenAnnotatorTest, UnderstandsFunctionAnnotations) {
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));