Skip to content

Commit 0e63f1a

Browse files
committed
[clang-format] Annotate constructor/destructor names
Annotate constructor/destructor names as FunctionDeclarationName. Fixes llvm#63046. Differential Revision: https://reviews.llvm.org/D157963
1 parent 91c4db0 commit 0e63f1a

File tree

3 files changed

+217
-19
lines changed

3 files changed

+217
-19
lines changed

Diff for: clang/lib/Format/TokenAnnotator.cpp

+85-3
Original file line numberDiff line numberDiff line change
@@ -3097,6 +3097,76 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) {
30973097
return Result;
30983098
}
30993099

3100+
// Returns the name of a function with no return type, e.g. a constructor or
3101+
// destructor.
3102+
static FormatToken *getFunctionName(const AnnotatedLine &Line) {
3103+
for (FormatToken *Tok = Line.getFirstNonComment(), *Name = nullptr; Tok;
3104+
Tok = Tok->getNextNonComment()) {
3105+
// Skip C++11 attributes both before and after the function name.
3106+
if (Tok->is(tok::l_square) && Tok->is(TT_AttributeSquare)) {
3107+
Tok = Tok->MatchingParen;
3108+
if (!Tok)
3109+
break;
3110+
continue;
3111+
}
3112+
3113+
// Make sure the name is followed by a pair of parentheses.
3114+
if (Name)
3115+
return Tok->is(tok::l_paren) && Tok->MatchingParen ? Name : nullptr;
3116+
3117+
// Skip keywords that may precede the constructor/destructor name.
3118+
if (Tok->isOneOf(tok::kw_friend, tok::kw_inline, tok::kw_virtual,
3119+
tok::kw_constexpr, tok::kw_consteval, tok::kw_explicit)) {
3120+
continue;
3121+
}
3122+
3123+
// A qualified name may start from the global namespace.
3124+
if (Tok->is(tok::coloncolon)) {
3125+
Tok = Tok->Next;
3126+
if (!Tok)
3127+
break;
3128+
}
3129+
3130+
// Skip to the unqualified part of the name.
3131+
while (Tok->startsSequence(tok::identifier, tok::coloncolon)) {
3132+
assert(Tok->Next);
3133+
Tok = Tok->Next->Next;
3134+
if (!Tok)
3135+
break;
3136+
}
3137+
3138+
// Skip the `~` if a destructor name.
3139+
if (Tok->is(tok::tilde)) {
3140+
Tok = Tok->Next;
3141+
if (!Tok)
3142+
break;
3143+
}
3144+
3145+
// Make sure the name is not already annotated, e.g. as NamespaceMacro.
3146+
if (Tok->isNot(tok::identifier) || Tok->isNot(TT_Unknown))
3147+
break;
3148+
3149+
Name = Tok;
3150+
}
3151+
3152+
return nullptr;
3153+
}
3154+
3155+
// Checks if Tok is a constructor/destructor name qualified by its class name.
3156+
static bool isCtorOrDtorName(const FormatToken *Tok) {
3157+
assert(Tok && Tok->is(tok::identifier));
3158+
const auto *Prev = Tok->Previous;
3159+
3160+
if (Prev && Prev->is(tok::tilde))
3161+
Prev = Prev->Previous;
3162+
3163+
if (!Prev || !Prev->endsSequence(tok::coloncolon, tok::identifier))
3164+
return false;
3165+
3166+
assert(Prev->Previous);
3167+
return Prev->Previous->TokenText == Tok->TokenText;
3168+
}
3169+
31003170
void TokenAnnotator::annotate(AnnotatedLine &Line) {
31013171
for (auto &Child : Line.Children)
31023172
annotate(*Child);
@@ -3117,6 +3187,14 @@ void TokenAnnotator::annotate(AnnotatedLine &Line) {
31173187
ExpressionParser ExprParser(Style, Keywords, Line);
31183188
ExprParser.parse();
31193189

3190+
if (Style.isCpp()) {
3191+
auto *Tok = getFunctionName(Line);
3192+
if (Tok && ((!Scopes.empty() && Scopes.back() == ST_Class) ||
3193+
Line.endsWith(TT_FunctionLBrace) || isCtorOrDtorName(Tok))) {
3194+
Tok->setFinalizedType(TT_FunctionDeclarationName);
3195+
}
3196+
}
3197+
31203198
if (Line.startsWith(TT_ObjCMethodSpecifier))
31213199
Line.Type = LT_ObjCMethodDecl;
31223200
else if (Line.startsWith(TT_ObjCDecl))
@@ -3133,6 +3211,10 @@ void TokenAnnotator::annotate(AnnotatedLine &Line) {
31333211
static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
31343212
const AnnotatedLine &Line) {
31353213
assert(Current.Previous);
3214+
3215+
if (Current.is(TT_FunctionDeclarationName))
3216+
return true;
3217+
31363218
if (!Current.Tok.getIdentifierInfo())
31373219
return false;
31383220

@@ -3313,18 +3395,18 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
33133395
bool LineIsFunctionDeclaration = false;
33143396
for (FormatToken *Tok = Current, *AfterLastAttribute = nullptr; Tok;
33153397
Tok = Tok->Next) {
3398+
if (Tok->Previous->EndsCppAttributeGroup)
3399+
AfterLastAttribute = Tok;
33163400
if (isFunctionDeclarationName(Style.isCpp(), *Tok, Line)) {
33173401
LineIsFunctionDeclaration = true;
3318-
Tok->setType(TT_FunctionDeclarationName);
3402+
Tok->setFinalizedType(TT_FunctionDeclarationName);
33193403
if (AfterLastAttribute &&
33203404
mustBreakAfterAttributes(*AfterLastAttribute, Style)) {
33213405
AfterLastAttribute->MustBreakBefore = true;
33223406
Line.ReturnTypeWrapped = true;
33233407
}
33243408
break;
33253409
}
3326-
if (Tok->Previous->EndsCppAttributeGroup)
3327-
AfterLastAttribute = Tok;
33283410
}
33293411

33303412
if (Style.isCpp() && !LineIsFunctionDeclaration) {

Diff for: clang/unittests/Format/FormatTest.cpp

+84-16
Original file line numberDiff line numberDiff line change
@@ -16542,7 +16542,7 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) {
1654216542

1654316543
verifyFormat("int f();", SpaceFuncDef);
1654416544
verifyFormat("void f (int a, T b) {}", SpaceFuncDef);
16545-
verifyFormat("A::A() : a(1) {}", SpaceFuncDef);
16545+
verifyFormat("A::A () : a(1) {}", SpaceFuncDef);
1654616546
verifyFormat("void f() __attribute__((asdf));", SpaceFuncDef);
1654716547
verifyFormat("#define A(x) x", SpaceFuncDef);
1654816548
verifyFormat("#define A (x) x", SpaceFuncDef);
@@ -16567,7 +16567,7 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) {
1656716567
// verifyFormat("T A::operator() () {}", SpaceFuncDef);
1656816568
verifyFormat("auto lambda = [] () { return 0; };", SpaceFuncDef);
1656916569
verifyFormat("int x = int(y);", SpaceFuncDef);
16570-
verifyFormat("M(std::size_t R, std::size_t C) : C(C), data(R) {}",
16570+
verifyFormat("M (std::size_t R, std::size_t C) : C(C), data(R) {}",
1657116571
SpaceFuncDef);
1657216572

1657316573
FormatStyle SpaceIfMacros = getLLVMStyle();
@@ -26242,18 +26242,18 @@ TEST_F(FormatTest, BreakAfterAttributes) {
2624226242
FormatStyle Style = getLLVMStyle();
2624326243
EXPECT_EQ(Style.BreakAfterAttributes, FormatStyle::ABS_Never);
2624426244

26245-
const StringRef Code("[[nodiscard]] inline int f(int &i);\n"
26246-
"[[foo([[]])]] [[nodiscard]]\n"
26247-
"int g(int &i);\n"
26248-
"[[nodiscard]]\n"
26249-
"inline int f(int &i) {\n"
26250-
" i = 1;\n"
26251-
" return 0;\n"
26252-
"}\n"
26253-
"[[foo([[]])]] [[nodiscard]] int g(int &i) {\n"
26254-
" i = 0;\n"
26255-
" return 1;\n"
26256-
"}");
26245+
constexpr StringRef Code("[[nodiscard]] inline int f(int &i);\n"
26246+
"[[foo([[]])]] [[nodiscard]]\n"
26247+
"int g(int &i);\n"
26248+
"[[nodiscard]]\n"
26249+
"inline int f(int &i) {\n"
26250+
" i = 1;\n"
26251+
" return 0;\n"
26252+
"}\n"
26253+
"[[foo([[]])]] [[nodiscard]] int g(int &i) {\n"
26254+
" i = 0;\n"
26255+
" return 1;\n"
26256+
"}");
2625726257

2625826258
verifyFormat("[[nodiscard]] inline int f(int &i);\n"
2625926259
"[[foo([[]])]] [[nodiscard]] int g(int &i);\n"
@@ -26267,6 +26267,9 @@ TEST_F(FormatTest, BreakAfterAttributes) {
2626726267
"}",
2626826268
Code, Style);
2626926269

26270+
Style.BreakAfterAttributes = FormatStyle::ABS_Leave;
26271+
verifyNoChange(Code, Style);
26272+
2627026273
Style.BreakAfterAttributes = FormatStyle::ABS_Always;
2627126274
verifyFormat("[[nodiscard]]\n"
2627226275
"inline int f(int &i);\n"
@@ -26284,8 +26287,73 @@ TEST_F(FormatTest, BreakAfterAttributes) {
2628426287
"}",
2628526288
Code, Style);
2628626289

26287-
Style.BreakAfterAttributes = FormatStyle::ABS_Leave;
26288-
verifyNoChange(Code, Style);
26290+
constexpr StringRef CtorDtorCode("struct Foo {\n"
26291+
" [[deprecated]] Foo();\n"
26292+
" [[deprecated]] Foo() {}\n"
26293+
" [[deprecated]] ~Foo();\n"
26294+
" [[deprecated]] ~Foo() {}\n"
26295+
" [[deprecated]] void f();\n"
26296+
" [[deprecated]] void f() {}\n"
26297+
"};\n"
26298+
"[[deprecated]] Bar::Bar() {}\n"
26299+
"[[deprecated]] Bar::~Bar() {}\n"
26300+
"[[deprecated]] void g() {}");
26301+
verifyFormat("struct Foo {\n"
26302+
" [[deprecated]]\n"
26303+
" Foo();\n"
26304+
" [[deprecated]]\n"
26305+
" Foo() {}\n"
26306+
" [[deprecated]]\n"
26307+
" ~Foo();\n"
26308+
" [[deprecated]]\n"
26309+
" ~Foo() {}\n"
26310+
" [[deprecated]]\n"
26311+
" void f();\n"
26312+
" [[deprecated]]\n"
26313+
" void f() {}\n"
26314+
"};\n"
26315+
"[[deprecated]]\n"
26316+
"Bar::Bar() {}\n"
26317+
"[[deprecated]]\n"
26318+
"Bar::~Bar() {}\n"
26319+
"[[deprecated]]\n"
26320+
"void g() {}",
26321+
CtorDtorCode, Style);
26322+
26323+
Style.BreakBeforeBraces = FormatStyle::BS_Linux;
26324+
verifyFormat("struct Foo {\n"
26325+
" [[deprecated]]\n"
26326+
" Foo();\n"
26327+
" [[deprecated]]\n"
26328+
" Foo()\n"
26329+
" {\n"
26330+
" }\n"
26331+
" [[deprecated]]\n"
26332+
" ~Foo();\n"
26333+
" [[deprecated]]\n"
26334+
" ~Foo()\n"
26335+
" {\n"
26336+
" }\n"
26337+
" [[deprecated]]\n"
26338+
" void f();\n"
26339+
" [[deprecated]]\n"
26340+
" void f()\n"
26341+
" {\n"
26342+
" }\n"
26343+
"};\n"
26344+
"[[deprecated]]\n"
26345+
"Bar::Bar()\n"
26346+
"{\n"
26347+
"}\n"
26348+
"[[deprecated]]\n"
26349+
"Bar::~Bar()\n"
26350+
"{\n"
26351+
"}\n"
26352+
"[[deprecated]]\n"
26353+
"void g()\n"
26354+
"{\n"
26355+
"}",
26356+
CtorDtorCode, Style);
2628926357
}
2629026358

2629126359
TEST_F(FormatTest, InsertNewlineAtEOF) {

Diff for: clang/unittests/Format/TokenAnnotatorTest.cpp

+48
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,54 @@ TEST_F(TokenAnnotatorTest, UnderstandsFunctionDeclarationNames) {
15891589
Tokens = annotate("void f [[noreturn]] () {}");
15901590
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
15911591
EXPECT_TOKEN(Tokens[1], tok::identifier, TT_FunctionDeclarationName);
1592+
1593+
Tokens = annotate("class Foo { public: Foo(); };");
1594+
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
1595+
EXPECT_TOKEN(Tokens[5], tok::identifier, TT_FunctionDeclarationName);
1596+
1597+
Tokens = annotate("class Foo { public: ~Foo(); };");
1598+
ASSERT_EQ(Tokens.size(), 13u) << Tokens;
1599+
EXPECT_TOKEN(Tokens[6], tok::identifier, TT_FunctionDeclarationName);
1600+
1601+
Tokens = annotate("struct Foo { [[deprecated]] Foo() {} };");
1602+
ASSERT_EQ(Tokens.size(), 16u) << Tokens;
1603+
EXPECT_TOKEN(Tokens[8], tok::identifier, TT_FunctionDeclarationName);
1604+
EXPECT_TOKEN(Tokens[11], tok::l_brace, TT_FunctionLBrace);
1605+
1606+
Tokens = annotate("struct Foo { [[deprecated]] ~Foo() {} };");
1607+
ASSERT_EQ(Tokens.size(), 17u) << Tokens;
1608+
EXPECT_TOKEN(Tokens[9], tok::identifier, TT_FunctionDeclarationName);
1609+
EXPECT_TOKEN(Tokens[12], tok::l_brace, TT_FunctionLBrace);
1610+
1611+
Tokens = annotate("struct Foo { Foo() [[deprecated]] {} };");
1612+
ASSERT_EQ(Tokens.size(), 16u) << Tokens;
1613+
EXPECT_TOKEN(Tokens[3], tok::identifier, TT_FunctionDeclarationName);
1614+
EXPECT_TOKEN(Tokens[11], tok::l_brace, TT_FunctionLBrace);
1615+
1616+
Tokens = annotate("struct Foo { ~Foo() [[deprecated]] {} };");
1617+
ASSERT_EQ(Tokens.size(), 17u) << Tokens;
1618+
EXPECT_TOKEN(Tokens[4], tok::identifier, TT_FunctionDeclarationName);
1619+
EXPECT_TOKEN(Tokens[12], tok::l_brace, TT_FunctionLBrace);
1620+
1621+
Tokens = annotate("struct Foo { [[deprecated]] explicit Foo() {} };");
1622+
ASSERT_EQ(Tokens.size(), 17u) << Tokens;
1623+
EXPECT_TOKEN(Tokens[9], tok::identifier, TT_FunctionDeclarationName);
1624+
EXPECT_TOKEN(Tokens[12], tok::l_brace, TT_FunctionLBrace);
1625+
1626+
Tokens = annotate("struct Foo { virtual [[deprecated]] ~Foo() {} };");
1627+
ASSERT_EQ(Tokens.size(), 18u) << Tokens;
1628+
EXPECT_TOKEN(Tokens[10], tok::identifier, TT_FunctionDeclarationName);
1629+
EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_FunctionLBrace);
1630+
1631+
Tokens = annotate("Foo::Foo() {}");
1632+
ASSERT_EQ(Tokens.size(), 8u) << Tokens;
1633+
EXPECT_TOKEN(Tokens[2], tok::identifier, TT_FunctionDeclarationName);
1634+
EXPECT_TOKEN(Tokens[5], tok::l_brace, TT_FunctionLBrace);
1635+
1636+
Tokens = annotate("Foo::~Foo() {}");
1637+
ASSERT_EQ(Tokens.size(), 9u) << Tokens;
1638+
EXPECT_TOKEN(Tokens[3], tok::identifier, TT_FunctionDeclarationName);
1639+
EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_FunctionLBrace);
15921640
}
15931641

15941642
TEST_F(TokenAnnotatorTest, UnderstandsC11GenericSelection) {

0 commit comments

Comments
 (0)