diff --git a/clang/lib/Format/NamespaceEndCommentsFixer.cpp b/clang/lib/Format/NamespaceEndCommentsFixer.cpp --- a/clang/lib/Format/NamespaceEndCommentsFixer.cpp +++ b/clang/lib/Format/NamespaceEndCommentsFixer.cpp @@ -55,30 +55,72 @@ Tok = Tok->getNextNonComment(); } - // Use the string after `namespace` as a name candidate until `{` or `::` or - // `(`. If the name is empty, use the candicate. std::string FirstNSName; // For `namespace [[foo]] A::B::inline C {` or // `namespace MACRO1 MACRO2 A::B::inline C {`, returns "A::B::inline C". // Peek for the first '::' (or '{' or '(')) and then return all tokens from // one token before that up until the '{'. A '(' might be a macro with // arguments. - const FormatToken *FirstNSTok = Tok; + const FormatToken *FirstNSTok = nullptr; while (Tok && !Tok->isOneOf(tok::l_brace, tok::coloncolon, tok::l_paren)) { - FirstNSName += FirstNSTok->TokenText; + if (FirstNSTok) + FirstNSName += FirstNSTok->TokenText; FirstNSTok = Tok; Tok = Tok->getNextNonComment(); } - Tok = FirstNSTok; - while (Tok && !Tok->is(tok::l_brace)) { + bool IsPrevColoncolon = false; + bool HasColoncolon = false; + bool IsPrevInline = false; + bool NameFinished = false; + if (FirstNSTok) + Tok = FirstNSTok; + FirstNSTok = nullptr; + // Add everything from '(' to ')'. + auto AddMacro = [&name](const FormatToken *Tok) { + if (!Tok->is(tok::l_paren)) + return Tok; name += Tok->TokenText; - if (Tok->is(tok::kw_inline)) - name += " "; + Tok = Tok->getNextNonComment(); + for (int NestLevel = 1; Tok && NestLevel > 0;) { + if (Tok->is(tok::l_paren)) + ++NestLevel; + else if (Tok->is(tok::r_paren)) + --NestLevel; + name += Tok->TokenText; + Tok = Tok->getNextNonComment(); + } + return Tok; + }; + // If we found '::' in name, then it's the name. Otherwise, we can't tell + // which one is name. For example, `namespace A B {`. + while (Tok && !Tok->is(tok::l_brace)) { + if (FirstNSTok) { + if (!IsPrevInline && HasColoncolon && !IsPrevColoncolon) { + if (FirstNSTok->is(tok::l_paren)) { + FirstNSTok = Tok = AddMacro(FirstNSTok); + continue; + } + if (!FirstNSTok->is(tok::coloncolon)) { + NameFinished = true; + break; + } + } + name += FirstNSTok->TokenText; + IsPrevColoncolon = FirstNSTok->is(tok::coloncolon); + HasColoncolon |= IsPrevColoncolon; + if (FirstNSTok->is(tok::kw_inline)) { + name += " "; + IsPrevInline = true; + } + } + FirstNSTok = Tok; Tok = Tok->getNextNonComment(); } - if (name.empty()) - name = FirstNSName; + if (!NameFinished && FirstNSTok && !FirstNSTok->is(tok::l_brace)) + name += FirstNSTok->TokenText; + if (!FirstNSName.empty() && !HasColoncolon) + name = FirstNSName + " " + name; } return name; } diff --git a/clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp b/clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp --- a/clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp +++ b/clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp @@ -189,6 +189,26 @@ "int i;\n" "int j;\n" "}")); + EXPECT_EQ("#define M(x) x##x\n" + "namespace A M(x) {\n" + "int i;\n" + "int j;\n" + "}// namespace A M(x)", + fixNamespaceEndComments("#define M(x) x##x\n" + "namespace A M(x) {\n" + "int i;\n" + "int j;\n" + "}")); + EXPECT_EQ("#define M(x) x##x\n" + "namespace A::B M(x) {\n" + "int i;\n" + "int j;\n" + "}// namespace A::B", + fixNamespaceEndComments("#define M(x) x##x\n" + "namespace A::B M(x) {\n" + "int i;\n" + "int j;\n" + "}")); EXPECT_EQ("inline namespace A {\n" "int i;\n" "int j;\n"