1 //===- unittests/Analysis/MacroExpansionContextTest.cpp - -----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "clang/Analysis/MacroExpansionContext.h"
10 #include "clang/AST/ASTConsumer.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/Basic/Diagnostic.h"
13 #include "clang/Basic/DiagnosticOptions.h"
14 #include "clang/Basic/FileManager.h"
15 #include "clang/Basic/LangOptions.h"
16 #include "clang/Basic/SourceManager.h"
17 #include "clang/Basic/TargetInfo.h"
18 #include "clang/Basic/TargetOptions.h"
19 #include "clang/Lex/HeaderSearch.h"
20 #include "clang/Lex/HeaderSearchOptions.h"
21 #include "clang/Lex/Preprocessor.h"
22 #include "clang/Lex/PreprocessorOptions.h"
23 #include "clang/Parse/Parser.h"
24 #include "llvm/ADT/SmallString.h"
25 #include "gtest/gtest.h"
26
27 // static bool HACK_EnableDebugInUnitTest = (::llvm::DebugFlag = true);
28
29 namespace clang {
30 namespace analysis {
31 namespace {
32
33 class MacroExpansionContextTest : public ::testing::Test {
34 protected:
MacroExpansionContextTest()35 MacroExpansionContextTest()
36 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
37 FileMgr(FileSystemOptions(), InMemoryFileSystem),
38 DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()),
39 Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()),
40 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) {
41 TargetOpts->Triple = "x86_64-pc-linux-unknown";
42 Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
43 LangOpts.CPlusPlus20 = 1; // For __VA_OPT__
44 }
45
46 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
47 FileManager FileMgr;
48 IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
49 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
50 DiagnosticsEngine Diags;
51 SourceManager SourceMgr;
52 LangOptions LangOpts;
53 std::shared_ptr<TargetOptions> TargetOpts;
54 IntrusiveRefCntPtr<TargetInfo> Target;
55
56 std::unique_ptr<MacroExpansionContext>
getMacroExpansionContextFor(StringRef SourceText)57 getMacroExpansionContextFor(StringRef SourceText) {
58 std::unique_ptr<llvm::MemoryBuffer> Buf =
59 llvm::MemoryBuffer::getMemBuffer(SourceText);
60 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
61 TrivialModuleLoader ModLoader;
62 HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
63 Diags, LangOpts, Target.get());
64 Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
65 SourceMgr, HeaderInfo, ModLoader,
66 /*IILookup =*/nullptr,
67 /*OwnsHeaderSearch =*/false);
68
69 PP.Initialize(*Target);
70 auto Ctx = std::make_unique<MacroExpansionContext>(LangOpts);
71 Ctx->registerForPreprocessor(PP);
72
73 // Lex source text.
74 PP.EnterMainSourceFile();
75
76 while (true) {
77 Token Tok;
78 PP.Lex(Tok);
79 if (Tok.is(tok::eof))
80 break;
81 }
82
83 // Callbacks have been executed at this point.
84 return Ctx;
85 }
86
87 /// Returns the expansion location to main file at the given row and column.
at(unsigned row,unsigned col) const88 SourceLocation at(unsigned row, unsigned col) const {
89 SourceLocation Loc =
90 SourceMgr.translateLineCol(SourceMgr.getMainFileID(), row, col);
91 return SourceMgr.getExpansionLoc(Loc);
92 }
93
dumpExpandedTexts(const MacroExpansionContext & Ctx)94 static std::string dumpExpandedTexts(const MacroExpansionContext &Ctx) {
95 std::string Buf;
96 llvm::raw_string_ostream OS{Buf};
97 Ctx.dumpExpandedTextsToStream(OS);
98 return Buf;
99 }
100
dumpExpansionRanges(const MacroExpansionContext & Ctx)101 static std::string dumpExpansionRanges(const MacroExpansionContext &Ctx) {
102 std::string Buf;
103 llvm::raw_string_ostream OS{Buf};
104 Ctx.dumpExpansionRangesToStream(OS);
105 return Buf;
106 }
107 };
108
TEST_F(MacroExpansionContextTest,IgnoresPragmas)109 TEST_F(MacroExpansionContextTest, IgnoresPragmas) {
110 // No-crash during lexing.
111 const auto Ctx = getMacroExpansionContextFor(R"code(
112 _Pragma("pack(push, 1)")
113 _Pragma("pack(pop, 1)")
114 )code");
115 // After preprocessing:
116 // #pragma pack(push, 1)
117 // #pragma pack(pop, 1)
118
119 EXPECT_EQ("\n=============== ExpandedTokens ===============\n",
120 dumpExpandedTexts(*Ctx));
121 EXPECT_EQ("\n=============== ExpansionRanges ===============\n",
122 dumpExpansionRanges(*Ctx));
123
124 EXPECT_FALSE(Ctx->getExpandedText(at(2, 1)).has_value());
125 EXPECT_FALSE(Ctx->getOriginalText(at(2, 1)).has_value());
126
127 EXPECT_FALSE(Ctx->getExpandedText(at(2, 3)).has_value());
128 EXPECT_FALSE(Ctx->getOriginalText(at(2, 3)).has_value());
129
130 EXPECT_FALSE(Ctx->getExpandedText(at(3, 3)).has_value());
131 EXPECT_FALSE(Ctx->getOriginalText(at(3, 3)).has_value());
132 }
133
TEST_F(MacroExpansionContextTest,NoneForNonExpansionLocations)134 TEST_F(MacroExpansionContextTest, NoneForNonExpansionLocations) {
135 const auto Ctx = getMacroExpansionContextFor(R"code(
136 #define EMPTY
137 A b cd EMPTY ef EMPTY gh
138 EMPTY zz
139 )code");
140 // After preprocessing:
141 // A b cd ef gh
142 // zz
143
144 // That's the beginning of the definition of EMPTY.
145 EXPECT_FALSE(Ctx->getExpandedText(at(2, 11)).has_value());
146 EXPECT_FALSE(Ctx->getOriginalText(at(2, 11)).has_value());
147
148 // The space before the first expansion of EMPTY.
149 EXPECT_FALSE(Ctx->getExpandedText(at(3, 9)).has_value());
150 EXPECT_FALSE(Ctx->getOriginalText(at(3, 9)).has_value());
151
152 // The beginning of the first expansion of EMPTY.
153 EXPECT_TRUE(Ctx->getExpandedText(at(3, 10)).has_value());
154 EXPECT_TRUE(Ctx->getOriginalText(at(3, 10)).has_value());
155
156 // Pointing inside of the token EMPTY, but not at the beginning.
157 // FIXME: We only deal with begin locations.
158 EXPECT_FALSE(Ctx->getExpandedText(at(3, 11)).has_value());
159 EXPECT_FALSE(Ctx->getOriginalText(at(3, 11)).has_value());
160
161 // Same here.
162 EXPECT_FALSE(Ctx->getExpandedText(at(3, 12)).has_value());
163 EXPECT_FALSE(Ctx->getOriginalText(at(3, 12)).has_value());
164
165 // The beginning of the last expansion of EMPTY.
166 EXPECT_TRUE(Ctx->getExpandedText(at(4, 1)).has_value());
167 EXPECT_TRUE(Ctx->getOriginalText(at(4, 1)).has_value());
168
169 // Same as for the 3:11 case.
170 EXPECT_FALSE(Ctx->getExpandedText(at(4, 2)).has_value());
171 EXPECT_FALSE(Ctx->getOriginalText(at(4, 2)).has_value());
172 }
173
TEST_F(MacroExpansionContextTest,EmptyExpansions)174 TEST_F(MacroExpansionContextTest, EmptyExpansions) {
175 const auto Ctx = getMacroExpansionContextFor(R"code(
176 #define EMPTY
177 A b cd EMPTY ef EMPTY gh
178 EMPTY zz
179 )code");
180 // After preprocessing:
181 // A b cd ef gh
182 // zz
183
184 EXPECT_EQ("", Ctx->getExpandedText(at(3, 10)).value());
185 EXPECT_EQ("EMPTY", Ctx->getOriginalText(at(3, 10)).value());
186
187 EXPECT_EQ("", Ctx->getExpandedText(at(3, 19)).value());
188 EXPECT_EQ("EMPTY", Ctx->getOriginalText(at(3, 19)).value());
189
190 EXPECT_EQ("", Ctx->getExpandedText(at(4, 1)).value());
191 EXPECT_EQ("EMPTY", Ctx->getOriginalText(at(4, 1)).value());
192 }
193
TEST_F(MacroExpansionContextTest,TransitiveExpansions)194 TEST_F(MacroExpansionContextTest, TransitiveExpansions) {
195 const auto Ctx = getMacroExpansionContextFor(R"code(
196 #define EMPTY
197 #define WOOF EMPTY ) EMPTY 1
198 A b cd WOOF ef EMPTY gh
199 )code");
200 // After preprocessing:
201 // A b cd ) 1 ef gh
202
203 EXPECT_EQ("WOOF", Ctx->getOriginalText(at(4, 10)).value());
204
205 EXPECT_EQ("", Ctx->getExpandedText(at(4, 18)).value());
206 EXPECT_EQ("EMPTY", Ctx->getOriginalText(at(4, 18)).value());
207 }
208
TEST_F(MacroExpansionContextTest,MacroFunctions)209 TEST_F(MacroExpansionContextTest, MacroFunctions) {
210 const auto Ctx = getMacroExpansionContextFor(R"code(
211 #define EMPTY
212 #define WOOF(x) x(EMPTY ) ) ) EMPTY 1
213 A b cd WOOF($$ ef) EMPTY gh
214 WOOF(WOOF)
215 WOOF(WOOF(bar barr))),,),')
216 )code");
217 // After preprocessing:
218 // A b cd $$ ef( ) ) ) 1 gh
219 // WOOF( ) ) ) 1
220 // bar barr( ) ) ) 1( ) ) ) 1),,),')
221
222 EXPECT_EQ("$$ ef ()))1", Ctx->getExpandedText(at(4, 10)).value());
223 EXPECT_EQ("WOOF($$ ef)", Ctx->getOriginalText(at(4, 10)).value());
224
225 EXPECT_EQ("", Ctx->getExpandedText(at(4, 22)).value());
226 EXPECT_EQ("EMPTY", Ctx->getOriginalText(at(4, 22)).value());
227
228 EXPECT_EQ("WOOF ()))1", Ctx->getExpandedText(at(5, 3)).value());
229 EXPECT_EQ("WOOF(WOOF)", Ctx->getOriginalText(at(5, 3)).value());
230
231 EXPECT_EQ("bar barr ()))1()))1", Ctx->getExpandedText(at(6, 3)).value());
232 EXPECT_EQ("WOOF(WOOF(bar barr))", Ctx->getOriginalText(at(6, 3)).value());
233 }
234
TEST_F(MacroExpansionContextTest,VariadicMacros)235 TEST_F(MacroExpansionContextTest, VariadicMacros) {
236 // From the GCC website.
237 const auto Ctx = getMacroExpansionContextFor(R"code(
238 #define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)
239 eprintf("success!\n", );
240 eprintf("success!\n");
241
242 #define eprintf2(format, ...) \
243 fprintf (stderr, format __VA_OPT__(,) __VA_ARGS__)
244 eprintf2("success!\n", );
245 eprintf2("success!\n");
246 )code");
247 // After preprocessing:
248 // fprintf (stderr, "success!\n", );
249 // fprintf (stderr, "success!\n", );
250 // fprintf (stderr, "success!\n" );
251 // fprintf (stderr, "success!\n" );
252
253 EXPECT_EQ(R"(fprintf (stderr ,"success!\n",))",
254 Ctx->getExpandedText(at(3, 3)).value());
255 EXPECT_EQ(R"(eprintf("success!\n", ))",
256 Ctx->getOriginalText(at(3, 3)).value());
257
258 EXPECT_EQ(R"(fprintf (stderr ,"success!\n",))",
259 Ctx->getExpandedText(at(4, 3)).value());
260 EXPECT_EQ(R"(eprintf("success!\n"))", Ctx->getOriginalText(at(4, 3)).value());
261
262 EXPECT_EQ(R"(fprintf (stderr ,"success!\n"))",
263 Ctx->getExpandedText(at(8, 3)).value());
264 EXPECT_EQ(R"(eprintf2("success!\n", ))",
265 Ctx->getOriginalText(at(8, 3)).value());
266
267 EXPECT_EQ(R"(fprintf (stderr ,"success!\n"))",
268 Ctx->getExpandedText(at(9, 3)).value());
269 EXPECT_EQ(R"(eprintf2("success!\n"))",
270 Ctx->getOriginalText(at(9, 3)).value());
271 }
272
TEST_F(MacroExpansionContextTest,ConcatenationMacros)273 TEST_F(MacroExpansionContextTest, ConcatenationMacros) {
274 // From the GCC website.
275 const auto Ctx = getMacroExpansionContextFor(R"code(
276 #define COMMAND(NAME) { #NAME, NAME ## _command }
277 struct command commands[] = {
278 COMMAND(quit),
279 COMMAND(help),
280 };)code");
281 // After preprocessing:
282 // struct command commands[] = {
283 // { "quit", quit_command },
284 // { "help", help_command },
285 // };
286
287 EXPECT_EQ(R"({"quit",quit_command })",
288 Ctx->getExpandedText(at(4, 5)).value());
289 EXPECT_EQ("COMMAND(quit)", Ctx->getOriginalText(at(4, 5)).value());
290
291 EXPECT_EQ(R"({"help",help_command })",
292 Ctx->getExpandedText(at(5, 5)).value());
293 EXPECT_EQ("COMMAND(help)", Ctx->getOriginalText(at(5, 5)).value());
294 }
295
TEST_F(MacroExpansionContextTest,StringizingMacros)296 TEST_F(MacroExpansionContextTest, StringizingMacros) {
297 // From the GCC website.
298 const auto Ctx = getMacroExpansionContextFor(R"code(
299 #define WARN_IF(EXP) \
300 do { if (EXP) \
301 fprintf (stderr, "Warning: " #EXP "\n"); } \
302 while (0)
303 WARN_IF (x == 0);
304
305 #define xstr(s) str(s)
306 #define str(s) #s
307 #define foo 4
308 str (foo)
309 xstr (foo)
310 )code");
311 // After preprocessing:
312 // do { if (x == 0) fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);
313 // "foo"
314 // "4"
315
316 EXPECT_EQ(
317 R"(do {if (x ==0)fprintf (stderr ,"Warning: ""x == 0""\n");}while (0))",
318 Ctx->getExpandedText(at(6, 3)).value());
319 EXPECT_EQ("WARN_IF (x == 0)", Ctx->getOriginalText(at(6, 3)).value());
320
321 EXPECT_EQ(R"("foo")", Ctx->getExpandedText(at(11, 3)).value());
322 EXPECT_EQ("str (foo)", Ctx->getOriginalText(at(11, 3)).value());
323
324 EXPECT_EQ(R"("4")", Ctx->getExpandedText(at(12, 3)).value());
325 EXPECT_EQ("xstr (foo)", Ctx->getOriginalText(at(12, 3)).value());
326 }
327
TEST_F(MacroExpansionContextTest,StringizingVariadicMacros)328 TEST_F(MacroExpansionContextTest, StringizingVariadicMacros) {
329 const auto Ctx = getMacroExpansionContextFor(R"code(
330 #define xstr(...) str(__VA_ARGS__)
331 #define str(...) #__VA_ARGS__
332 #define RParen2x ) )
333 #define EMPTY
334 #define f(x, ...) __VA_ARGS__ ! x * x
335 #define g(...) zz EMPTY f(__VA_ARGS__ ! x) f() * y
336 #define h(x, G) G(x) G(x ## x RParen2x
337 #define q(G) h(apple, G(apple)) RParen2x
338
339 q(g)
340 q(xstr)
341 g(RParen2x)
342 f( RParen2x )s
343 )code");
344 // clang-format off
345 // After preprocessing:
346 // zz ! apple ! x * apple ! x ! * * y(apple) zz ! apple ! x * apple ! x ! * * y(appleapple ) ) ) )
347 // "apple"(apple) "apple"(appleapple ) ) ) )
348 // zz ! * ) ! x) ! * * y
349 // ! ) ) * ) )
350 // clang-format on
351
352 EXPECT_EQ("zz !apple !x *apple !x !**y (apple )zz !apple !x *apple !x !**y "
353 "(appleapple ))))",
354 Ctx->getExpandedText(at(11, 3)).value());
355 EXPECT_EQ("q(g)", Ctx->getOriginalText(at(11, 3)).value());
356
357 EXPECT_EQ(R"res("apple"(apple )"apple"(appleapple )))))res",
358 Ctx->getExpandedText(at(12, 3)).value());
359 EXPECT_EQ("q(xstr)", Ctx->getOriginalText(at(12, 3)).value());
360
361 EXPECT_EQ("zz !*)!x )!**y ", Ctx->getExpandedText(at(13, 3)).value());
362 EXPECT_EQ("g(RParen2x)", Ctx->getOriginalText(at(13, 3)).value());
363
364 EXPECT_EQ("!))*))", Ctx->getExpandedText(at(14, 3)).value());
365 EXPECT_EQ("f( RParen2x )", Ctx->getOriginalText(at(14, 3)).value());
366 }
367
TEST_F(MacroExpansionContextTest,RedefUndef)368 TEST_F(MacroExpansionContextTest, RedefUndef) {
369 const auto Ctx = getMacroExpansionContextFor(R"code(
370 #define Hi(x) Welcome x
371 Hi(Adam)
372 #define Hi Willkommen
373 Hi Hans
374 #undef Hi
375 Hi(Hi)
376 )code");
377 // After preprocessing:
378 // Welcome Adam
379 // Willkommen Hans
380 // Hi(Hi)
381
382 // FIXME: Extra space follows every identifier.
383 EXPECT_EQ("Welcome Adam ", Ctx->getExpandedText(at(3, 3)).value());
384 EXPECT_EQ("Hi(Adam)", Ctx->getOriginalText(at(3, 3)).value());
385
386 EXPECT_EQ("Willkommen ", Ctx->getExpandedText(at(5, 3)).value());
387 EXPECT_EQ("Hi", Ctx->getOriginalText(at(5, 3)).value());
388
389 // There was no macro expansion at 7:3, we should expect None.
390 EXPECT_FALSE(Ctx->getExpandedText(at(7, 3)).has_value());
391 EXPECT_FALSE(Ctx->getOriginalText(at(7, 3)).has_value());
392 }
393
TEST_F(MacroExpansionContextTest,UnbalacedParenthesis)394 TEST_F(MacroExpansionContextTest, UnbalacedParenthesis) {
395 const auto Ctx = getMacroExpansionContextFor(R"code(
396 #define retArg(x) x
397 #define retArgUnclosed retArg(fun()
398 #define BB CC
399 #define applyInt BB(int)
400 #define CC(x) retArgUnclosed
401
402 applyInt );
403
404 #define expandArgUnclosedCommaExpr(x) (x, fun(), 1
405 #define f expandArgUnclosedCommaExpr
406
407 int x = f(f(1)) ));
408 )code");
409 // After preprocessing:
410 // fun();
411 // int x = ((1, fun(), 1, fun(), 1 ));
412
413 EXPECT_EQ("fun ()", Ctx->getExpandedText(at(8, 3)).value());
414 EXPECT_EQ("applyInt )", Ctx->getOriginalText(at(8, 3)).value());
415
416 EXPECT_EQ("((1,fun (),1,fun (),1", Ctx->getExpandedText(at(13, 12)).value());
417 EXPECT_EQ("f(f(1))", Ctx->getOriginalText(at(13, 12)).value());
418 }
419
420 } // namespace
421 } // namespace analysis
422 } // namespace clang
423