[clang-format] Annotate constructor/destructor names
Annotate constructor/destructor names as FunctionDeclarationName. Fixes #63046. Differential Revision: https://reviews.llvm.org/D157963
This commit is contained in:
@@ -3097,6 +3097,76 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Returns the name of a function with no return type, e.g. a constructor or
|
||||
// destructor.
|
||||
static FormatToken *getFunctionName(const AnnotatedLine &Line) {
|
||||
for (FormatToken *Tok = Line.getFirstNonComment(), *Name = nullptr; Tok;
|
||||
Tok = Tok->getNextNonComment()) {
|
||||
// Skip C++11 attributes both before and after the function name.
|
||||
if (Tok->is(tok::l_square) && Tok->is(TT_AttributeSquare)) {
|
||||
Tok = Tok->MatchingParen;
|
||||
if (!Tok)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the name is followed by a pair of parentheses.
|
||||
if (Name)
|
||||
return Tok->is(tok::l_paren) && Tok->MatchingParen ? Name : nullptr;
|
||||
|
||||
// Skip keywords that may precede the constructor/destructor name.
|
||||
if (Tok->isOneOf(tok::kw_friend, tok::kw_inline, tok::kw_virtual,
|
||||
tok::kw_constexpr, tok::kw_consteval, tok::kw_explicit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// A qualified name may start from the global namespace.
|
||||
if (Tok->is(tok::coloncolon)) {
|
||||
Tok = Tok->Next;
|
||||
if (!Tok)
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip to the unqualified part of the name.
|
||||
while (Tok->startsSequence(tok::identifier, tok::coloncolon)) {
|
||||
assert(Tok->Next);
|
||||
Tok = Tok->Next->Next;
|
||||
if (!Tok)
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip the `~` if a destructor name.
|
||||
if (Tok->is(tok::tilde)) {
|
||||
Tok = Tok->Next;
|
||||
if (!Tok)
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure the name is not already annotated, e.g. as NamespaceMacro.
|
||||
if (Tok->isNot(tok::identifier) || Tok->isNot(TT_Unknown))
|
||||
break;
|
||||
|
||||
Name = Tok;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Checks if Tok is a constructor/destructor name qualified by its class name.
|
||||
static bool isCtorOrDtorName(const FormatToken *Tok) {
|
||||
assert(Tok && Tok->is(tok::identifier));
|
||||
const auto *Prev = Tok->Previous;
|
||||
|
||||
if (Prev && Prev->is(tok::tilde))
|
||||
Prev = Prev->Previous;
|
||||
|
||||
if (!Prev || !Prev->endsSequence(tok::coloncolon, tok::identifier))
|
||||
return false;
|
||||
|
||||
assert(Prev->Previous);
|
||||
return Prev->Previous->TokenText == Tok->TokenText;
|
||||
}
|
||||
|
||||
void TokenAnnotator::annotate(AnnotatedLine &Line) {
|
||||
for (auto &Child : Line.Children)
|
||||
annotate(*Child);
|
||||
@@ -3117,6 +3187,14 @@ void TokenAnnotator::annotate(AnnotatedLine &Line) {
|
||||
ExpressionParser ExprParser(Style, Keywords, Line);
|
||||
ExprParser.parse();
|
||||
|
||||
if (Style.isCpp()) {
|
||||
auto *Tok = getFunctionName(Line);
|
||||
if (Tok && ((!Scopes.empty() && Scopes.back() == ST_Class) ||
|
||||
Line.endsWith(TT_FunctionLBrace) || isCtorOrDtorName(Tok))) {
|
||||
Tok->setFinalizedType(TT_FunctionDeclarationName);
|
||||
}
|
||||
}
|
||||
|
||||
if (Line.startsWith(TT_ObjCMethodSpecifier))
|
||||
Line.Type = LT_ObjCMethodDecl;
|
||||
else if (Line.startsWith(TT_ObjCDecl))
|
||||
@@ -3133,6 +3211,10 @@ void TokenAnnotator::annotate(AnnotatedLine &Line) {
|
||||
static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
|
||||
const AnnotatedLine &Line) {
|
||||
assert(Current.Previous);
|
||||
|
||||
if (Current.is(TT_FunctionDeclarationName))
|
||||
return true;
|
||||
|
||||
if (!Current.Tok.getIdentifierInfo())
|
||||
return false;
|
||||
|
||||
@@ -3313,9 +3395,11 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
|
||||
bool LineIsFunctionDeclaration = false;
|
||||
for (FormatToken *Tok = Current, *AfterLastAttribute = nullptr; Tok;
|
||||
Tok = Tok->Next) {
|
||||
if (Tok->Previous->EndsCppAttributeGroup)
|
||||
AfterLastAttribute = Tok;
|
||||
if (isFunctionDeclarationName(Style.isCpp(), *Tok, Line)) {
|
||||
LineIsFunctionDeclaration = true;
|
||||
Tok->setType(TT_FunctionDeclarationName);
|
||||
Tok->setFinalizedType(TT_FunctionDeclarationName);
|
||||
if (AfterLastAttribute &&
|
||||
mustBreakAfterAttributes(*AfterLastAttribute, Style)) {
|
||||
AfterLastAttribute->MustBreakBefore = true;
|
||||
@@ -3323,8 +3407,6 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (Tok->Previous->EndsCppAttributeGroup)
|
||||
AfterLastAttribute = Tok;
|
||||
}
|
||||
|
||||
if (Style.isCpp() && !LineIsFunctionDeclaration) {
|
||||
|
||||
@@ -16542,7 +16542,7 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) {
|
||||
|
||||
verifyFormat("int f();", SpaceFuncDef);
|
||||
verifyFormat("void f (int a, T b) {}", SpaceFuncDef);
|
||||
verifyFormat("A::A() : a(1) {}", SpaceFuncDef);
|
||||
verifyFormat("A::A () : a(1) {}", SpaceFuncDef);
|
||||
verifyFormat("void f() __attribute__((asdf));", SpaceFuncDef);
|
||||
verifyFormat("#define A(x) x", SpaceFuncDef);
|
||||
verifyFormat("#define A (x) x", SpaceFuncDef);
|
||||
@@ -16567,7 +16567,7 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) {
|
||||
// verifyFormat("T A::operator() () {}", SpaceFuncDef);
|
||||
verifyFormat("auto lambda = [] () { return 0; };", SpaceFuncDef);
|
||||
verifyFormat("int x = int(y);", SpaceFuncDef);
|
||||
verifyFormat("M(std::size_t R, std::size_t C) : C(C), data(R) {}",
|
||||
verifyFormat("M (std::size_t R, std::size_t C) : C(C), data(R) {}",
|
||||
SpaceFuncDef);
|
||||
|
||||
FormatStyle SpaceIfMacros = getLLVMStyle();
|
||||
@@ -26242,18 +26242,18 @@ TEST_F(FormatTest, BreakAfterAttributes) {
|
||||
FormatStyle Style = getLLVMStyle();
|
||||
EXPECT_EQ(Style.BreakAfterAttributes, FormatStyle::ABS_Never);
|
||||
|
||||
const StringRef Code("[[nodiscard]] inline int f(int &i);\n"
|
||||
"[[foo([[]])]] [[nodiscard]]\n"
|
||||
"int g(int &i);\n"
|
||||
"[[nodiscard]]\n"
|
||||
"inline int f(int &i) {\n"
|
||||
" i = 1;\n"
|
||||
" return 0;\n"
|
||||
"}\n"
|
||||
"[[foo([[]])]] [[nodiscard]] int g(int &i) {\n"
|
||||
" i = 0;\n"
|
||||
" return 1;\n"
|
||||
"}");
|
||||
constexpr StringRef Code("[[nodiscard]] inline int f(int &i);\n"
|
||||
"[[foo([[]])]] [[nodiscard]]\n"
|
||||
"int g(int &i);\n"
|
||||
"[[nodiscard]]\n"
|
||||
"inline int f(int &i) {\n"
|
||||
" i = 1;\n"
|
||||
" return 0;\n"
|
||||
"}\n"
|
||||
"[[foo([[]])]] [[nodiscard]] int g(int &i) {\n"
|
||||
" i = 0;\n"
|
||||
" return 1;\n"
|
||||
"}");
|
||||
|
||||
verifyFormat("[[nodiscard]] inline int f(int &i);\n"
|
||||
"[[foo([[]])]] [[nodiscard]] int g(int &i);\n"
|
||||
@@ -26267,6 +26267,9 @@ TEST_F(FormatTest, BreakAfterAttributes) {
|
||||
"}",
|
||||
Code, Style);
|
||||
|
||||
Style.BreakAfterAttributes = FormatStyle::ABS_Leave;
|
||||
verifyNoChange(Code, Style);
|
||||
|
||||
Style.BreakAfterAttributes = FormatStyle::ABS_Always;
|
||||
verifyFormat("[[nodiscard]]\n"
|
||||
"inline int f(int &i);\n"
|
||||
@@ -26284,8 +26287,73 @@ TEST_F(FormatTest, BreakAfterAttributes) {
|
||||
"}",
|
||||
Code, Style);
|
||||
|
||||
Style.BreakAfterAttributes = FormatStyle::ABS_Leave;
|
||||
verifyNoChange(Code, Style);
|
||||
constexpr StringRef CtorDtorCode("struct Foo {\n"
|
||||
" [[deprecated]] Foo();\n"
|
||||
" [[deprecated]] Foo() {}\n"
|
||||
" [[deprecated]] ~Foo();\n"
|
||||
" [[deprecated]] ~Foo() {}\n"
|
||||
" [[deprecated]] void f();\n"
|
||||
" [[deprecated]] void f() {}\n"
|
||||
"};\n"
|
||||
"[[deprecated]] Bar::Bar() {}\n"
|
||||
"[[deprecated]] Bar::~Bar() {}\n"
|
||||
"[[deprecated]] void g() {}");
|
||||
verifyFormat("struct Foo {\n"
|
||||
" [[deprecated]]\n"
|
||||
" Foo();\n"
|
||||
" [[deprecated]]\n"
|
||||
" Foo() {}\n"
|
||||
" [[deprecated]]\n"
|
||||
" ~Foo();\n"
|
||||
" [[deprecated]]\n"
|
||||
" ~Foo() {}\n"
|
||||
" [[deprecated]]\n"
|
||||
" void f();\n"
|
||||
" [[deprecated]]\n"
|
||||
" void f() {}\n"
|
||||
"};\n"
|
||||
"[[deprecated]]\n"
|
||||
"Bar::Bar() {}\n"
|
||||
"[[deprecated]]\n"
|
||||
"Bar::~Bar() {}\n"
|
||||
"[[deprecated]]\n"
|
||||
"void g() {}",
|
||||
CtorDtorCode, Style);
|
||||
|
||||
Style.BreakBeforeBraces = FormatStyle::BS_Linux;
|
||||
verifyFormat("struct Foo {\n"
|
||||
" [[deprecated]]\n"
|
||||
" Foo();\n"
|
||||
" [[deprecated]]\n"
|
||||
" Foo()\n"
|
||||
" {\n"
|
||||
" }\n"
|
||||
" [[deprecated]]\n"
|
||||
" ~Foo();\n"
|
||||
" [[deprecated]]\n"
|
||||
" ~Foo()\n"
|
||||
" {\n"
|
||||
" }\n"
|
||||
" [[deprecated]]\n"
|
||||
" void f();\n"
|
||||
" [[deprecated]]\n"
|
||||
" void f()\n"
|
||||
" {\n"
|
||||
" }\n"
|
||||
"};\n"
|
||||
"[[deprecated]]\n"
|
||||
"Bar::Bar()\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
"[[deprecated]]\n"
|
||||
"Bar::~Bar()\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
"[[deprecated]]\n"
|
||||
"void g()\n"
|
||||
"{\n"
|
||||
"}",
|
||||
CtorDtorCode, Style);
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, InsertNewlineAtEOF) {
|
||||
|
||||
@@ -1589,6 +1589,54 @@ TEST_F(TokenAnnotatorTest, UnderstandsFunctionDeclarationNames) {
|
||||
Tokens = annotate("void f [[noreturn]] () {}");
|
||||
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[1], tok::identifier, TT_FunctionDeclarationName);
|
||||
|
||||
Tokens = annotate("class Foo { public: Foo(); };");
|
||||
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[5], tok::identifier, TT_FunctionDeclarationName);
|
||||
|
||||
Tokens = annotate("class Foo { public: ~Foo(); };");
|
||||
ASSERT_EQ(Tokens.size(), 13u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[6], tok::identifier, TT_FunctionDeclarationName);
|
||||
|
||||
Tokens = annotate("struct Foo { [[deprecated]] Foo() {} };");
|
||||
ASSERT_EQ(Tokens.size(), 16u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[8], tok::identifier, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[11], tok::l_brace, TT_FunctionLBrace);
|
||||
|
||||
Tokens = annotate("struct Foo { [[deprecated]] ~Foo() {} };");
|
||||
ASSERT_EQ(Tokens.size(), 17u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[9], tok::identifier, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[12], tok::l_brace, TT_FunctionLBrace);
|
||||
|
||||
Tokens = annotate("struct Foo { Foo() [[deprecated]] {} };");
|
||||
ASSERT_EQ(Tokens.size(), 16u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[3], tok::identifier, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[11], tok::l_brace, TT_FunctionLBrace);
|
||||
|
||||
Tokens = annotate("struct Foo { ~Foo() [[deprecated]] {} };");
|
||||
ASSERT_EQ(Tokens.size(), 17u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[4], tok::identifier, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[12], tok::l_brace, TT_FunctionLBrace);
|
||||
|
||||
Tokens = annotate("struct Foo { [[deprecated]] explicit Foo() {} };");
|
||||
ASSERT_EQ(Tokens.size(), 17u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[9], tok::identifier, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[12], tok::l_brace, TT_FunctionLBrace);
|
||||
|
||||
Tokens = annotate("struct Foo { virtual [[deprecated]] ~Foo() {} };");
|
||||
ASSERT_EQ(Tokens.size(), 18u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[10], tok::identifier, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_FunctionLBrace);
|
||||
|
||||
Tokens = annotate("Foo::Foo() {}");
|
||||
ASSERT_EQ(Tokens.size(), 8u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[2], tok::identifier, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[5], tok::l_brace, TT_FunctionLBrace);
|
||||
|
||||
Tokens = annotate("Foo::~Foo() {}");
|
||||
ASSERT_EQ(Tokens.size(), 9u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[3], tok::identifier, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_FunctionLBrace);
|
||||
}
|
||||
|
||||
TEST_F(TokenAnnotatorTest, UnderstandsC11GenericSelection) {
|
||||
|
||||
Reference in New Issue
Block a user