| //===- unittest/Format/FormatMacroExpansion.cpp - Formatting unit tests ---===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "FormatTestBase.h" |
| |
| #define DEBUG_TYPE "format-test-macro-expansion" |
| |
| namespace clang { |
| namespace format { |
| namespace test { |
| namespace { |
| |
| class FormatTestMacroExpansion : public FormatTestBase {}; |
| |
| TEST_F(FormatTestMacroExpansion, UnexpandConfiguredMacros) { |
| FormatStyle Style = getLLVMStyle(); |
| Style.Macros.push_back("CLASS=class C {"); |
| Style.Macros.push_back("SEMI=;"); |
| Style.Macros.push_back("STMT=f();"); |
| Style.Macros.push_back("ID(x)=x"); |
| Style.Macros.push_back("ID3(x, y, z)=x y z"); |
| Style.Macros.push_back("CALL(x)=f([] { x })"); |
| Style.Macros.push_back("ASSIGN_OR_RETURN(a, b)=a = (b)"); |
| Style.Macros.push_back("ASSIGN_OR_RETURN(a, b, c)=a = (b); if (x) return c"); |
| Style.Macros.push_back("MOCK_METHOD(r, n, a, s)=r n a s"); |
| |
| verifyFormat("ID(nested(a(b, c), d))", Style); |
| verifyFormat("CLASS\n" |
| " a *b;\n" |
| "};", |
| Style); |
| verifyFormat("SEMI\n" |
| "SEMI\n" |
| "SEMI", |
| Style); |
| verifyFormat("STMT\n" |
| "STMT\n" |
| "STMT", |
| Style); |
| verifyFormat("void f() { ID(a *b); }", Style); |
| verifyFormat("ID(\n" |
| " {\n" |
| " ID(a *b);\n" |
| " });", |
| Style); |
| verifyIncompleteFormat("ID3({, ID(a *b),\n" |
| " ;\n" |
| " });", |
| Style); |
| |
| verifyFormat("ID(CALL(CALL(return a * b;)));", Style); |
| |
| verifyFormat("ASSIGN_OR_RETURN(MySomewhatLongType *variable,\n" |
| " MySomewhatLongFunction(SomethingElse()));", |
| Style); |
| verifyFormat("ASSIGN_OR_RETURN(MySomewhatLongType *variable,\n" |
| " MySomewhatLongFunction(SomethingElse()), " |
| "ReturnMe());", |
| Style); |
| |
| verifyFormat(R"( |
| #define MACRO(a, b) ID(a + b) |
| )", |
| Style); |
| EXPECT_EQ(R"( |
| int a; |
| int b; |
| int c; |
| int d; |
| int e; |
| int f; |
| ID( |
| namespace foo { |
| int a; |
| } |
| ) // namespace k |
| )", |
| format(R"( |
| int a; |
| int b; |
| int c; |
| int d; |
| int e; |
| int f; |
| ID(namespace foo { int a; }) // namespace k |
| )", |
| Style)); |
| verifyFormat(R"(ID( |
| // |
| ({ ; })) |
| )", |
| Style); |
| |
| Style.ColumnLimit = 35; |
| // FIXME: Arbitrary formatting of macros where the end of the logical |
| // line is in the middle of a macro call are not working yet. |
| verifyFormat(R"(ID( |
| void f(); |
| void) |
| ID(g) ID(()) ID( |
| ; |
| void g();) |
| )", |
| Style); |
| |
| Style.ColumnLimit = 10; |
| verifyFormat("STMT\n" |
| "STMT\n" |
| "STMT", |
| Style); |
| |
| EXPECT_EQ(R"( |
| ID(CALL(CALL( |
| a *b))); |
| )", |
| format(R"( |
| ID(CALL(CALL(a * b))); |
| )", |
| Style)); |
| |
| // FIXME: If we want to support unbalanced braces or parens from macro |
| // expansions we need to re-think how we propagate errors in |
| // TokenAnnotator::parseLine; for investigation, switching the inner loop of |
| // TokenAnnotator::parseLine to return LT_Other instead of LT_Invalid in case |
| // of !consumeToken() changes the formatting of the test below and makes it |
| // believe it has a fully correct formatting. |
| EXPECT_EQ(R"( |
| ID3( |
| { |
| CLASS |
| a *b; |
| }; |
| }, |
| ID(x *y); |
| , |
| STMT |
| STMT |
| STMT) |
| void f(); |
| )", |
| format(R"( |
| ID3({CLASS a*b; };}, ID(x*y);, STMT STMT STMT) |
| void f(); |
| )", |
| Style)); |
| |
| verifyFormat("ID(a(\n" |
| "#ifdef A\n" |
| " b, c\n" |
| "#else\n" |
| " d(e)\n" |
| "#endif\n" |
| " ))", |
| Style); |
| Style.ColumnLimit = 80; |
| verifyFormat(R"(ASSIGN_OR_RETURN( |
| // Comment |
| a b, c); |
| )", |
| Style); |
| Style.ColumnLimit = 30; |
| verifyFormat(R"(ASSIGN_OR_RETURN( |
| // Comment |
| // |
| a b, |
| xxxxxxxxxxxx( |
| yyyyyyyyyyyyyyyyy, |
| zzzzzzzzzzzzzzzzzz), |
| f([]() { |
| a(); |
| b(); |
| })); |
| )", |
| Style); |
| verifyFormat(R"(int a = []() { |
| ID( |
| x; |
| y; |
| z;) |
| ; |
| }(); |
| )", |
| Style); |
| EXPECT_EQ( |
| R"(ASSIGN_OR_RETURN(( |
| ==== |
| #)) |
| })", |
| format(R"(ASSIGN_OR_RETURN(( |
| ==== |
| #)) |
| })", |
| Style, SC_ExpectIncomplete)); |
| EXPECT_EQ(R"(ASSIGN_OR_RETURN( |
| } |
| ( |
| ==== |
| #), |
| a))", |
| format(R"(ASSIGN_OR_RETURN( |
| } |
| ( |
| ==== |
| #), |
| a))", |
| Style, SC_ExpectIncomplete)); |
| EXPECT_EQ(R"(ASSIGN_OR_RETURN(a |
| // |
| ==== |
| # |
| <))", |
| format(R"(ASSIGN_OR_RETURN(a |
| // |
| ==== |
| # |
| <))", |
| Style)); |
| verifyFormat("class C {\n" |
| " MOCK_METHOD(R, f,\n" |
| " (a *b, c *d),\n" |
| " (override));\n" |
| "};", |
| Style); |
| } |
| |
| TEST_F(FormatTestMacroExpansion, KeepParensWhenExpandingObjectLikeMacros) { |
| FormatStyle Style = getLLVMStyle(); |
| Style.Macros.push_back("FN=class C { int f"); |
| verifyFormat("void f() {\n" |
| " FN(a *b);\n" |
| " };\n" |
| "}", |
| Style); |
| } |
| |
| TEST_F(FormatTestMacroExpansion, DoesNotExpandFunctionLikeMacrosWithoutParens) { |
| FormatStyle Style = getLLVMStyle(); |
| Style.Macros.push_back("CLASS()=class C {"); |
| verifyFormat("CLASS void f();\n" |
| "}\n" |
| ";", |
| Style); |
| } |
| |
| TEST_F(FormatTestMacroExpansion, |
| ContinueFormattingAfterUnclosedParensAfterObjectLikeMacro) { |
| FormatStyle Style = getLLVMStyle(); |
| Style.Macros.push_back("O=class {"); |
| verifyIncompleteFormat("O(auto x = [](){\n" |
| " f();}", |
| Style); |
| } |
| |
| TEST_F(FormatTestMacroExpansion, CommaAsOperator) { |
| FormatStyle Style = getGoogleStyleWithColumns(42); |
| Style.Macros.push_back("MACRO(a, b, c)=a=(b); if(x) c"); |
| verifyFormat("MACRO(auto a,\n" |
| " looooongfunction(first, second,\n" |
| " third),\n" |
| " fourth);", |
| Style); |
| } |
| |
| TEST_F(FormatTestMacroExpansion, ForcedBreakDiffers) { |
| FormatStyle Style = getGoogleStyleWithColumns(40); |
| Style.Macros.push_back("MACRO(a, b)=a=(b)"); |
| verifyFormat("//\n" |
| "MACRO(const type variable,\n" |
| " functtioncall(\n" |
| " first, longsecondarg, third));", |
| Style); |
| } |
| |
| TEST_F(FormatTestMacroExpansion, |
| PreferNotBreakingBetweenReturnTypeAndFunction) { |
| FormatStyle Style = getGoogleStyleWithColumns(22); |
| Style.Macros.push_back("MOCK_METHOD(r, n, a)=r n a"); |
| // In the expanded code, we parse a full function signature, and afterwards |
| // know that we prefer not to break before the function name. |
| verifyFormat("MOCK_METHOD(\n" |
| " type, variable,\n" |
| " (type));", |
| Style); |
| } |
| |
| TEST_F(FormatTestMacroExpansion, IndentChildrenWithinMacroCall) { |
| FormatStyle Style = getGoogleStyleWithColumns(22); |
| Style.Macros.push_back("MACRO(a, b)=a=(b)"); |
| verifyFormat("void f() {\n" |
| " MACRO(a b, call([] {\n" |
| " if (expr) {\n" |
| " indent();\n" |
| " }\n" |
| " }));\n" |
| "}", |
| Style); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace format |
| } // namespace clang |