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