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 | |