xref: /llvm-project/clang/unittests/Format/MacroExpanderTest.cpp (revision 01402831aaae76e9c61595f9aa81a506b0d926eb)
1 #include "../../lib/Format/Macros.h"
2 #include "TestLexer.h"
3 #include "clang/Basic/FileManager.h"
4 
5 #include "gtest/gtest.h"
6 
7 namespace clang {
8 namespace format {
9 
10 namespace {
11 
12 class MacroExpanderTest : public ::testing::Test {
13 public:
14   MacroExpanderTest() : Lex(Allocator, Buffers) {}
15   std::unique_ptr<MacroExpander>
16   create(const std::vector<std::string> &MacroDefinitions) {
17     return std::make_unique<MacroExpander>(MacroDefinitions,
18                                            Lex.SourceMgr.get(), Lex.Style,
19                                            Lex.Allocator, Lex.IdentTable);
20   }
21 
22   std::string expand(MacroExpander &Macros, llvm::StringRef Name) {
23     EXPECT_TRUE(Macros.defined(Name))
24         << "Macro not defined: \"" << Name << "\"";
25     return text(Macros.expand(Lex.id(Name), {}));
26   }
27 
28   std::string expand(MacroExpander &Macros, llvm::StringRef Name,
29                      const std::vector<std::string> &Args) {
30     EXPECT_TRUE(Macros.defined(Name))
31         << "Macro not defined: \"" << Name << "\"";
32     return text(Macros.expand(Lex.id(Name), lexArgs(Args)));
33   }
34 
35   llvm::SmallVector<TokenList, 1>
36   lexArgs(const std::vector<std::string> &Args) {
37     llvm::SmallVector<TokenList, 1> Result;
38     for (const auto &Arg : Args)
39       Result.push_back(uneof(Lex.lex(Arg)));
40     return Result;
41   }
42 
43   struct MacroAttributes {
44     clang::tok::TokenKind Kind;
45     MacroRole Role;
46     unsigned Start;
47     unsigned End;
48     llvm::SmallVector<FormatToken *, 1> ExpandedFrom;
49   };
50 
51   void expectAttributes(const TokenList &Tokens,
52                         const std::vector<MacroAttributes> &Attributes,
53                         const std::string &File, unsigned Line) {
54     EXPECT_EQ(Tokens.size(), Attributes.size()) << text(Tokens);
55     for (size_t I = 0, E = Tokens.size(); I != E; ++I) {
56       if (I >= Attributes.size())
57         continue;
58       std::string Context =
59           ("for token " + llvm::Twine(I) + ": " + Tokens[I]->Tok.getName() +
60            " / " + Tokens[I]->TokenText)
61               .str();
62       EXPECT_TRUE(Tokens[I]->is(Attributes[I].Kind))
63           << Context << " in " << text(Tokens) << " at " << File << ":" << Line;
64       EXPECT_EQ(Tokens[I]->MacroCtx->Role, Attributes[I].Role)
65           << Context << " in " << text(Tokens) << " at " << File << ":" << Line;
66       EXPECT_EQ(Tokens[I]->MacroCtx->StartOfExpansion, Attributes[I].Start)
67           << Context << " in " << text(Tokens) << " at " << File << ":" << Line;
68       EXPECT_EQ(Tokens[I]->MacroCtx->EndOfExpansion, Attributes[I].End)
69           << Context << " in " << text(Tokens) << " at " << File << ":" << Line;
70       EXPECT_EQ(Tokens[I]->MacroCtx->ExpandedFrom, Attributes[I].ExpandedFrom)
71           << Context << " in " << text(Tokens) << " at " << File << ":" << Line;
72     }
73   }
74 
75 protected:
76   llvm::SpecificBumpPtrAllocator<FormatToken> Allocator;
77   std::vector<std::unique_ptr<llvm::MemoryBuffer>> Buffers;
78   TestLexer Lex;
79 };
80 
81 #define EXPECT_ATTRIBUTES(Tokens, Attributes)                                  \
82   expectAttributes(Tokens, Attributes, __FILE__, __LINE__)
83 
84 TEST_F(MacroExpanderTest, SkipsDefinitionOnError) {
85   auto Macros =
86       create({"A(", "B(,", "C(a,", "D(a a", "E(a, a", "F(,)", "G(a;"});
87   for (const auto *Name : {"A", "B", "C", "D", "E", "F", "G"})
88     EXPECT_FALSE(Macros->defined(Name)) << "for Name " << Name;
89 }
90 
91 TEST_F(MacroExpanderTest, ExpandsWithoutArguments) {
92   auto Macros = create({
93       "A",
94       "B=b",
95       "C=c + c",
96       "D()",
97   });
98   EXPECT_TRUE(Macros->objectLike("A"));
99   EXPECT_TRUE(Macros->objectLike("B"));
100   EXPECT_TRUE(Macros->objectLike("C"));
101   EXPECT_TRUE(!Macros->objectLike("D"));
102   EXPECT_EQ("", expand(*Macros, "A"));
103   EXPECT_EQ("b", expand(*Macros, "B"));
104   EXPECT_EQ("c+c", expand(*Macros, "C"));
105   EXPECT_EQ("", expand(*Macros, "D", {}));
106 }
107 
108 TEST_F(MacroExpanderTest, ExpandsWithArguments) {
109   auto Macros = create({
110       "A(x)",
111       "B(x, y)=x + y",
112   });
113   EXPECT_EQ("", expand(*Macros, "A", {"a"}));
114   EXPECT_EQ("b1+b2+b3", expand(*Macros, "B", {"b1", "b2 + b3"}));
115 }
116 
117 TEST_F(MacroExpanderTest, AttributizesTokens) {
118   auto Macros = create({
119       "A(x, y)={ x + y; }",
120       "B(x, y)=x + 3 + y",
121   });
122   auto *A = Lex.id("A");
123   auto AArgs = lexArgs({"a1 * a2", "a3 * a4"});
124   auto Result = Macros->expand(A, AArgs);
125   EXPECT_EQ(11U, Result.size()) << text(Result) << " / " << Result;
126   EXPECT_EQ("{a1*a2+a3*a4;}", text(Result));
127   std::vector<MacroAttributes> Attributes = {
128       {tok::l_brace, MR_Hidden, 1, 0, {A}},
129       {tok::identifier, MR_ExpandedArg, 0, 0, {A}},
130       {tok::star, MR_ExpandedArg, 0, 0, {A}},
131       {tok::identifier, MR_ExpandedArg, 0, 0, {A}},
132       {tok::plus, MR_Hidden, 0, 0, {A}},
133       {tok::identifier, MR_ExpandedArg, 0, 0, {A}},
134       {tok::star, MR_ExpandedArg, 0, 0, {A}},
135       {tok::identifier, MR_ExpandedArg, 0, 0, {A}},
136       {tok::semi, MR_Hidden, 0, 0, {A}},
137       {tok::r_brace, MR_Hidden, 0, 1, {A}},
138       {tok::eof, MR_Hidden, 0, 0, {A}},
139   };
140   EXPECT_ATTRIBUTES(Result, Attributes);
141 
142   auto *B = Lex.id("B");
143   auto BArgs = lexArgs({"b1", "b2"});
144   Result = Macros->expand(B, BArgs);
145   EXPECT_EQ(6U, Result.size()) << text(Result) << " / " << Result;
146   EXPECT_EQ("b1+3+b2", text(Result));
147   Attributes = {
148       {tok::identifier, MR_ExpandedArg, 1, 0, {B}},
149       {tok::plus, MR_Hidden, 0, 0, {B}},
150       {tok::numeric_constant, MR_Hidden, 0, 0, {B}},
151       {tok::plus, MR_Hidden, 0, 0, {B}},
152       {tok::identifier, MR_ExpandedArg, 0, 1, {B}},
153       {tok::eof, MR_Hidden, 0, 0, {B}},
154   };
155   EXPECT_ATTRIBUTES(Result, Attributes);
156 }
157 
158 TEST_F(MacroExpanderTest, RecursiveExpansion) {
159   auto Macros = create({
160       "A(x)=x",
161       "B(x)=x",
162       "C(x)=x",
163   });
164 
165   auto *A = Lex.id("A");
166   auto *B = Lex.id("B");
167   auto *C = Lex.id("C");
168 
169   auto Args = lexArgs({"id"});
170   auto CResult = uneof(Macros->expand(C, Args));
171   auto BResult = uneof(Macros->expand(B, CResult));
172   auto AResult = uneof(Macros->expand(A, BResult));
173 
174   std::vector<MacroAttributes> Attributes = {
175       {tok::identifier, MR_ExpandedArg, 3, 3, {C, B, A}},
176   };
177   EXPECT_ATTRIBUTES(AResult, Attributes);
178 }
179 
180 TEST_F(MacroExpanderTest, SingleExpansion) {
181   auto Macros = create({"A(x)=x+x"});
182   auto *A = Lex.id("A");
183   auto Args = lexArgs({"id"});
184   auto Result = uneof(Macros->expand(A, Args));
185   std::vector<MacroAttributes> Attributes = {
186       {tok::identifier, MR_ExpandedArg, 1, 0, {A}},
187       {tok::plus, MR_Hidden, 0, 0, {A}},
188       {tok::identifier, MR_Hidden, 0, 1, {A}},
189   };
190   EXPECT_ATTRIBUTES(Result, Attributes);
191 }
192 
193 TEST_F(MacroExpanderTest, UnderstandsCppTokens) {
194   auto Macros = create({"A(T,name)=T name = 0;"});
195   auto *A = Lex.id("A");
196   auto Args = lexArgs({"const int", "x"});
197   auto Result = uneof(Macros->expand(A, Args));
198   std::vector<MacroAttributes> Attributes = {
199       {tok::kw_const, MR_ExpandedArg, 1, 0, {A}},
200       {tok::kw_int, MR_ExpandedArg, 0, 0, {A}},
201       {tok::identifier, MR_ExpandedArg, 0, 0, {A}},
202       {tok::equal, MR_Hidden, 0, 0, {A}},
203       {tok::numeric_constant, MR_Hidden, 0, 0, {A}},
204       {tok::semi, MR_Hidden, 0, 1, {A}},
205   };
206   EXPECT_ATTRIBUTES(Result, Attributes);
207 }
208 
209 TEST_F(MacroExpanderTest, Overloads) {
210   auto Macros = create({"A=x", "A()=y", "A(a)=a", "A(a, b)=a b"});
211   EXPECT_EQ("x", expand(*Macros, "A"));
212   EXPECT_EQ("y", expand(*Macros, "A", {}));
213   EXPECT_EQ("z", expand(*Macros, "A", {"z"}));
214   EXPECT_EQ("xy", expand(*Macros, "A", {"x", "y"}));
215 }
216 
217 } // namespace
218 } // namespace format
219 } // namespace clang
220