[clang-format] fix namepsace format when the name is macro expansion

Originally filed at crbug.com/1184570.
When the name of a namespace is a macro that takes arguments,
- It fixed the indentation.
- It fixed the namepsace end comments.

Differential Revision: https://reviews.llvm.org/D120931
This commit is contained in:
Zequan Wu 2022-03-03 11:59:57 -08:00
parent 9129743dde
commit be5705767a
4 changed files with 179 additions and 4 deletions

View file

@ -40,12 +40,32 @@ std::string computeName(const FormatToken *NamespaceTok) {
Tok = Tok->getNextNonComment();
}
} else {
// Skip attributes.
if (Tok && Tok->is(tok::l_square)) {
for (int NestLevel = 1; NestLevel > 0;) {
Tok = Tok->getNextNonComment();
if (!Tok)
break;
if (Tok->is(tok::l_square))
++NestLevel;
else if (Tok->is(tok::r_square))
--NestLevel;
}
if (Tok)
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 '{') and then return all tokens from one
// token before that up until the '{'.
// 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;
while (Tok && !Tok->is(tok::l_brace) && !Tok->is(tok::coloncolon)) {
while (Tok && !Tok->isOneOf(tok::l_brace, tok::coloncolon, tok::l_paren)) {
FirstNSName += FirstNSTok->TokenText;
FirstNSTok = Tok;
Tok = Tok->getNextNonComment();
}
@ -57,6 +77,8 @@ std::string computeName(const FormatToken *NamespaceTok) {
name += " ";
Tok = Tok->getNextNonComment();
}
if (name.empty())
name = FirstNSName;
}
return name;
}

View file

@ -2597,10 +2597,12 @@ void UnwrappedLineParser::parseNamespace() {
parseParens();
} else {
while (FormatTok->isOneOf(tok::identifier, tok::coloncolon, tok::kw_inline,
tok::l_square, tok::period) ||
tok::l_square, tok::period, tok::l_paren) ||
(Style.isCSharp() && FormatTok->is(tok::kw_union)))
if (FormatTok->is(tok::l_square))
parseSquare();
else if (FormatTok->is(tok::l_paren))
parseParens();
else
nextToken();
}

View file

@ -3738,6 +3738,36 @@ TEST_F(FormatTest, FormatsNamespaces) {
"void f() { f(); }\n"
"}",
LLVMWithNoNamespaceFix);
verifyFormat("#define M(x) x##x\n"
"namespace M(x) {\n"
"class A {};\n"
"void f() { f(); }\n"
"}",
LLVMWithNoNamespaceFix);
verifyFormat("#define M(x) x##x\n"
"namespace N::inline M(x) {\n"
"class A {};\n"
"void f() { f(); }\n"
"}",
LLVMWithNoNamespaceFix);
verifyFormat("#define M(x) x##x\n"
"namespace M(x)::inline N {\n"
"class A {};\n"
"void f() { f(); }\n"
"}",
LLVMWithNoNamespaceFix);
verifyFormat("#define M(x) x##x\n"
"namespace N::M(x) {\n"
"class A {};\n"
"void f() { f(); }\n"
"}",
LLVMWithNoNamespaceFix);
verifyFormat("#define M(x) x##x\n"
"namespace M::N(x) {\n"
"class A {};\n"
"void f() { f(); }\n"
"}",
LLVMWithNoNamespaceFix);
verifyFormat("namespace N::inline D {\n"
"class A {};\n"
"void f() { f(); }\n"

View file

@ -68,6 +68,127 @@ TEST_F(NamespaceEndCommentsFixerTest, AddsEndComment) {
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("#define M(x) x##x\n"
"namespace M(x) {\n"
"int i;\n"
"int j;\n"
"}// namespace M(x)",
fixNamespaceEndComments("#define M(x) x##x\n"
"namespace M(x) {\n"
"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 M(x)::A {\n"
"int i;\n"
"int j;\n"
"}// namespace M(x)::A",
fixNamespaceEndComments("#define M(x) x##x\n"
"namespace M(x)::A {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("#define M(x) x##x\n"
"namespace A::inline M(x)::B {\n"
"int i;\n"
"int j;\n"
"}// namespace A::inline M(x)::B",
fixNamespaceEndComments("#define M(x) x##x\n"
"namespace A::inline M(x)::B {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("#define M(x) x##x\n"
"namespace [[deprecated(\"foo\")]] A::inline M(x)::A {\n"
"int i;\n"
"int j;\n"
"}// namespace A::inline M(x)::A",
fixNamespaceEndComments(
"#define M(x) x##x\n"
"namespace [[deprecated(\"foo\")]] A::inline M(x)::A {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ(
"namespace /* comment */ [[deprecated(\"foo\")]] /* comment */ A {\n"
"int i;\n"
"int j;\n"
"}// namespace A",
fixNamespaceEndComments(
"namespace /* comment */ [[deprecated(\"foo\")]] /* comment */ A {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("namespace /* comment */ [[deprecated(\"foo\")]] A {\n"
"int i;\n"
"int j;\n"
"}// namespace A",
fixNamespaceEndComments(
"namespace /* comment */ [[deprecated(\"foo\")]] A {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ(
"#define M(x) x##x\n"
"namespace /* comment */ [[deprecated(\"foo\")]] /* comment */ M(x) {\n"
"int i;\n"
"int j;\n"
"}// namespace M(x)",
fixNamespaceEndComments("#define M(x) x##x\n"
"namespace /* comment */ "
"[[deprecated(\"foo\")]] /* comment */ M(x) {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("#define M(x) x##x\n"
"namespace /* comment */ [[deprecated(\"foo\")]] /* comment */ "
"A::M(x) {\n"
"int i;\n"
"int j;\n"
"}// namespace A::M(x)",
fixNamespaceEndComments(
"#define M(x) x##x\n"
"namespace /* comment */ "
"[[deprecated(\"foo\")]] /* comment */ A::M(x) {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("#define M(x) x##x\n"
"namespace /* comment */ [[deprecated(\"foo\")]] /* comment */ "
"M(x) /* comment */ {\n"
"int i;\n"
"int j;\n"
"}// namespace M(x)",
fixNamespaceEndComments(
"#define M(x) x##x\n"
"namespace /* comment */ [[deprecated(\"foo\")]] /* comment "
"*/ M(x) /* comment */ {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("#define M(x) x##x\n"
"namespace /* comment */ [[deprecated(\"foo\")]] /* comment */ "
"A::M(x) /* comment */ {\n"
"int i;\n"
"int j;\n"
"}// namespace A::M(x)",
fixNamespaceEndComments(
"#define M(x) x##x\n"
"namespace /* comment */ [[deprecated(\"foo\")]] /* comment "
"*/ A::M(x) /* comment */ {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("inline namespace A {\n"
"int i;\n"
"int j;\n"