diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index b6b4172818d17..97df0b447b4ba 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -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) { diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 5fc0ed03a5aad..f5794ffac1a4a 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -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) { diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index ae2084923de00..a20a38ae070eb 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -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) {