xref: /llvm-project/clang/unittests/Analysis/MacroExpansionContextTest.cpp (revision 3116d60494f219bfcb284d05d9ebed5b6c196ca5)
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     PP.LexTokensUntilEOF();
77 
78     // Callbacks have been executed at this point.
79     return Ctx;
80   }
81 
82   /// Returns the expansion location to main file at the given row and column.
at(unsigned row,unsigned col) const83   SourceLocation at(unsigned row, unsigned col) const {
84     SourceLocation Loc =
85         SourceMgr.translateLineCol(SourceMgr.getMainFileID(), row, col);
86     return SourceMgr.getExpansionLoc(Loc);
87   }
88 
dumpExpandedTexts(const MacroExpansionContext & Ctx)89   static std::string dumpExpandedTexts(const MacroExpansionContext &Ctx) {
90     std::string Buf;
91     llvm::raw_string_ostream OS{Buf};
92     Ctx.dumpExpandedTextsToStream(OS);
93     return Buf;
94   }
95 
dumpExpansionRanges(const MacroExpansionContext & Ctx)96   static std::string dumpExpansionRanges(const MacroExpansionContext &Ctx) {
97     std::string Buf;
98     llvm::raw_string_ostream OS{Buf};
99     Ctx.dumpExpansionRangesToStream(OS);
100     return Buf;
101   }
102 };
103 
TEST_F(MacroExpansionContextTest,IgnoresPragmas)104 TEST_F(MacroExpansionContextTest, IgnoresPragmas) {
105   // No-crash during lexing.
106   const auto Ctx = getMacroExpansionContextFor(R"code(
107   _Pragma("pack(push, 1)")
108   _Pragma("pack(pop, 1)")
109       )code");
110   // After preprocessing:
111   // #pragma pack(push, 1)
112   // #pragma pack(pop, 1)
113 
114   EXPECT_EQ("\n=============== ExpandedTokens ===============\n",
115             dumpExpandedTexts(*Ctx));
116   EXPECT_EQ("\n=============== ExpansionRanges ===============\n",
117             dumpExpansionRanges(*Ctx));
118 
119   EXPECT_FALSE(Ctx->getExpandedText(at(2, 1)).has_value());
120   EXPECT_FALSE(Ctx->getOriginalText(at(2, 1)).has_value());
121 
122   EXPECT_FALSE(Ctx->getExpandedText(at(2, 3)).has_value());
123   EXPECT_FALSE(Ctx->getOriginalText(at(2, 3)).has_value());
124 
125   EXPECT_FALSE(Ctx->getExpandedText(at(3, 3)).has_value());
126   EXPECT_FALSE(Ctx->getOriginalText(at(3, 3)).has_value());
127 }
128 
TEST_F(MacroExpansionContextTest,NoneForNonExpansionLocations)129 TEST_F(MacroExpansionContextTest, NoneForNonExpansionLocations) {
130   const auto Ctx = getMacroExpansionContextFor(R"code(
131   #define EMPTY
132   A b cd EMPTY ef EMPTY gh
133 EMPTY zz
134       )code");
135   // After preprocessing:
136   //  A b cd ef gh
137   //      zz
138 
139   // That's the beginning of the definition of EMPTY.
140   EXPECT_FALSE(Ctx->getExpandedText(at(2, 11)).has_value());
141   EXPECT_FALSE(Ctx->getOriginalText(at(2, 11)).has_value());
142 
143   // The space before the first expansion of EMPTY.
144   EXPECT_FALSE(Ctx->getExpandedText(at(3, 9)).has_value());
145   EXPECT_FALSE(Ctx->getOriginalText(at(3, 9)).has_value());
146 
147   // The beginning of the first expansion of EMPTY.
148   EXPECT_TRUE(Ctx->getExpandedText(at(3, 10)).has_value());
149   EXPECT_TRUE(Ctx->getOriginalText(at(3, 10)).has_value());
150 
151   // Pointing inside of the token EMPTY, but not at the beginning.
152   // FIXME: We only deal with begin locations.
153   EXPECT_FALSE(Ctx->getExpandedText(at(3, 11)).has_value());
154   EXPECT_FALSE(Ctx->getOriginalText(at(3, 11)).has_value());
155 
156   // Same here.
157   EXPECT_FALSE(Ctx->getExpandedText(at(3, 12)).has_value());
158   EXPECT_FALSE(Ctx->getOriginalText(at(3, 12)).has_value());
159 
160   // The beginning of the last expansion of EMPTY.
161   EXPECT_TRUE(Ctx->getExpandedText(at(4, 1)).has_value());
162   EXPECT_TRUE(Ctx->getOriginalText(at(4, 1)).has_value());
163 
164   // Same as for the 3:11 case.
165   EXPECT_FALSE(Ctx->getExpandedText(at(4, 2)).has_value());
166   EXPECT_FALSE(Ctx->getOriginalText(at(4, 2)).has_value());
167 }
168 
TEST_F(MacroExpansionContextTest,EmptyExpansions)169 TEST_F(MacroExpansionContextTest, EmptyExpansions) {
170   const auto Ctx = getMacroExpansionContextFor(R"code(
171   #define EMPTY
172   A b cd EMPTY ef EMPTY gh
173 EMPTY zz
174       )code");
175   // After preprocessing:
176   //  A b cd ef gh
177   //      zz
178 
179   EXPECT_EQ("", *Ctx->getExpandedText(at(3, 10)));
180   EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(3, 10)));
181 
182   EXPECT_EQ("", *Ctx->getExpandedText(at(3, 19)));
183   EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(3, 19)));
184 
185   EXPECT_EQ("", *Ctx->getExpandedText(at(4, 1)));
186   EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(4, 1)));
187 }
188 
TEST_F(MacroExpansionContextTest,TransitiveExpansions)189 TEST_F(MacroExpansionContextTest, TransitiveExpansions) {
190   const auto Ctx = getMacroExpansionContextFor(R"code(
191   #define EMPTY
192   #define WOOF EMPTY ) EMPTY   1
193   A b cd WOOF ef EMPTY gh
194       )code");
195   // After preprocessing:
196   //  A b cd ) 1 ef gh
197 
198   EXPECT_EQ("WOOF", *Ctx->getOriginalText(at(4, 10)));
199 
200   EXPECT_EQ("", *Ctx->getExpandedText(at(4, 18)));
201   EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(4, 18)));
202 }
203 
TEST_F(MacroExpansionContextTest,MacroFunctions)204 TEST_F(MacroExpansionContextTest, MacroFunctions) {
205   const auto Ctx = getMacroExpansionContextFor(R"code(
206   #define EMPTY
207   #define WOOF(x) x(EMPTY ) )  ) EMPTY   1
208   A b cd WOOF($$ ef) EMPTY gh
209   WOOF(WOOF)
210   WOOF(WOOF(bar barr))),,),')
211       )code");
212   // After preprocessing:
213   //  A b cd $$ ef( ) ) ) 1 gh
214   //  WOOF( ) ) ) 1
215   //  bar barr( ) ) ) 1( ) ) ) 1),,),')
216 
217   EXPECT_EQ("$$ ef ()))1", *Ctx->getExpandedText(at(4, 10)));
218   EXPECT_EQ("WOOF($$ ef)", *Ctx->getOriginalText(at(4, 10)));
219 
220   EXPECT_EQ("", *Ctx->getExpandedText(at(4, 22)));
221   EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(4, 22)));
222 
223   EXPECT_EQ("WOOF ()))1", *Ctx->getExpandedText(at(5, 3)));
224   EXPECT_EQ("WOOF(WOOF)", *Ctx->getOriginalText(at(5, 3)));
225 
226   EXPECT_EQ("bar barr ()))1()))1", *Ctx->getExpandedText(at(6, 3)));
227   EXPECT_EQ("WOOF(WOOF(bar barr))", *Ctx->getOriginalText(at(6, 3)));
228 }
229 
TEST_F(MacroExpansionContextTest,VariadicMacros)230 TEST_F(MacroExpansionContextTest, VariadicMacros) {
231   // From the GCC website.
232   const auto Ctx = getMacroExpansionContextFor(R"code(
233   #define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)
234   eprintf("success!\n", );
235   eprintf("success!\n");
236 
237   #define eprintf2(format, ...) \
238     fprintf (stderr, format __VA_OPT__(,) __VA_ARGS__)
239   eprintf2("success!\n", );
240   eprintf2("success!\n");
241       )code");
242   // After preprocessing:
243   //  fprintf (stderr, "success!\n", );
244   //  fprintf (stderr, "success!\n", );
245   //  fprintf (stderr, "success!\n" );
246   //  fprintf (stderr, "success!\n" );
247 
248   EXPECT_EQ(R"(fprintf (stderr ,"success!\n",))",
249             *Ctx->getExpandedText(at(3, 3)));
250   EXPECT_EQ(R"(eprintf("success!\n", ))", *Ctx->getOriginalText(at(3, 3)));
251 
252   EXPECT_EQ(R"(fprintf (stderr ,"success!\n",))",
253             *Ctx->getExpandedText(at(4, 3)));
254   EXPECT_EQ(R"(eprintf("success!\n"))", *Ctx->getOriginalText(at(4, 3)));
255 
256   EXPECT_EQ(R"(fprintf (stderr ,"success!\n"))",
257             *Ctx->getExpandedText(at(8, 3)));
258   EXPECT_EQ(R"(eprintf2("success!\n", ))", *Ctx->getOriginalText(at(8, 3)));
259 
260   EXPECT_EQ(R"(fprintf (stderr ,"success!\n"))",
261             *Ctx->getExpandedText(at(9, 3)));
262   EXPECT_EQ(R"(eprintf2("success!\n"))", *Ctx->getOriginalText(at(9, 3)));
263 }
264 
TEST_F(MacroExpansionContextTest,ConcatenationMacros)265 TEST_F(MacroExpansionContextTest, ConcatenationMacros) {
266   // From the GCC website.
267   const auto Ctx = getMacroExpansionContextFor(R"code(
268   #define COMMAND(NAME)  { #NAME, NAME ## _command }
269   struct command commands[] = {
270     COMMAND(quit),
271     COMMAND(help),
272   };)code");
273   // After preprocessing:
274   //  struct command commands[] = {
275   //    { "quit", quit_command },
276   //    { "help", help_command },
277   //  };
278 
279   EXPECT_EQ(R"({"quit",quit_command })", *Ctx->getExpandedText(at(4, 5)));
280   EXPECT_EQ("COMMAND(quit)", *Ctx->getOriginalText(at(4, 5)));
281 
282   EXPECT_EQ(R"({"help",help_command })", *Ctx->getExpandedText(at(5, 5)));
283   EXPECT_EQ("COMMAND(help)", *Ctx->getOriginalText(at(5, 5)));
284 }
285 
TEST_F(MacroExpansionContextTest,StringizingMacros)286 TEST_F(MacroExpansionContextTest, StringizingMacros) {
287   // From the GCC website.
288   const auto Ctx = getMacroExpansionContextFor(R"code(
289   #define WARN_IF(EXP) \
290   do { if (EXP) \
291           fprintf (stderr, "Warning: " #EXP "\n"); } \
292   while (0)
293   WARN_IF (x == 0);
294 
295   #define xstr(s) str(s)
296   #define str(s) #s
297   #define foo 4
298   str (foo)
299   xstr (foo)
300       )code");
301   // After preprocessing:
302   //  do { if (x == 0) fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);
303   //  "foo"
304   //  "4"
305 
306   EXPECT_EQ(
307       R"(do {if (x ==0)fprintf (stderr ,"Warning: ""x == 0""\n");}while (0))",
308       *Ctx->getExpandedText(at(6, 3)));
309   EXPECT_EQ("WARN_IF (x == 0)", *Ctx->getOriginalText(at(6, 3)));
310 
311   EXPECT_EQ(R"("foo")", *Ctx->getExpandedText(at(11, 3)));
312   EXPECT_EQ("str (foo)", *Ctx->getOriginalText(at(11, 3)));
313 
314   EXPECT_EQ(R"("4")", *Ctx->getExpandedText(at(12, 3)));
315   EXPECT_EQ("xstr (foo)", *Ctx->getOriginalText(at(12, 3)));
316 }
317 
TEST_F(MacroExpansionContextTest,StringizingVariadicMacros)318 TEST_F(MacroExpansionContextTest, StringizingVariadicMacros) {
319   const auto Ctx = getMacroExpansionContextFor(R"code(
320   #define xstr(...) str(__VA_ARGS__)
321   #define str(...) #__VA_ARGS__
322   #define RParen2x ) )
323   #define EMPTY
324   #define f(x, ...) __VA_ARGS__ ! x * x
325   #define g(...) zz EMPTY f(__VA_ARGS__ ! x) f() * y
326   #define h(x, G) G(x) G(x ## x RParen2x
327   #define q(G) h(apple, G(apple)) RParen2x
328 
329   q(g)
330   q(xstr)
331   g(RParen2x)
332   f( RParen2x )s
333       )code");
334   // clang-format off
335   // After preprocessing:
336   //  zz ! apple ! x * apple ! x ! * * y(apple) zz ! apple ! x * apple ! x ! * * y(appleapple ) ) ) )
337   //  "apple"(apple) "apple"(appleapple ) ) ) )
338   //  zz ! * ) ! x) ! * * y
339   //  ! ) ) * ) )
340   // clang-format on
341 
342   EXPECT_EQ("zz !apple !x *apple !x !**y (apple )zz !apple !x *apple !x !**y "
343             "(appleapple ))))",
344             *Ctx->getExpandedText(at(11, 3)));
345   EXPECT_EQ("q(g)", *Ctx->getOriginalText(at(11, 3)));
346 
347   EXPECT_EQ(R"res("apple"(apple )"apple"(appleapple )))))res",
348             *Ctx->getExpandedText(at(12, 3)));
349   EXPECT_EQ("q(xstr)", *Ctx->getOriginalText(at(12, 3)));
350 
351   EXPECT_EQ("zz !*)!x )!**y ", *Ctx->getExpandedText(at(13, 3)));
352   EXPECT_EQ("g(RParen2x)", *Ctx->getOriginalText(at(13, 3)));
353 
354   EXPECT_EQ("!))*))", *Ctx->getExpandedText(at(14, 3)));
355   EXPECT_EQ("f( RParen2x )", *Ctx->getOriginalText(at(14, 3)));
356 }
357 
TEST_F(MacroExpansionContextTest,RedefUndef)358 TEST_F(MacroExpansionContextTest, RedefUndef) {
359   const auto Ctx = getMacroExpansionContextFor(R"code(
360   #define Hi(x) Welcome x
361   Hi(Adam)
362   #define Hi Willkommen
363   Hi Hans
364   #undef Hi
365   Hi(Hi)
366       )code");
367   // After preprocessing:
368   //  Welcome Adam
369   //  Willkommen Hans
370   //  Hi(Hi)
371 
372   // FIXME: Extra space follows every identifier.
373   EXPECT_EQ("Welcome Adam ", *Ctx->getExpandedText(at(3, 3)));
374   EXPECT_EQ("Hi(Adam)", *Ctx->getOriginalText(at(3, 3)));
375 
376   EXPECT_EQ("Willkommen ", *Ctx->getExpandedText(at(5, 3)));
377   EXPECT_EQ("Hi", *Ctx->getOriginalText(at(5, 3)));
378 
379   // There was no macro expansion at 7:3, we should expect None.
380   EXPECT_FALSE(Ctx->getExpandedText(at(7, 3)).has_value());
381   EXPECT_FALSE(Ctx->getOriginalText(at(7, 3)).has_value());
382 }
383 
TEST_F(MacroExpansionContextTest,UnbalacedParenthesis)384 TEST_F(MacroExpansionContextTest, UnbalacedParenthesis) {
385   const auto Ctx = getMacroExpansionContextFor(R"code(
386   #define retArg(x) x
387   #define retArgUnclosed retArg(fun()
388   #define BB CC
389   #define applyInt BB(int)
390   #define CC(x) retArgUnclosed
391 
392   applyInt );
393 
394   #define expandArgUnclosedCommaExpr(x) (x, fun(), 1
395   #define f expandArgUnclosedCommaExpr
396 
397   int x =  f(f(1))  ));
398       )code");
399   // After preprocessing:
400   //  fun();
401   //  int x = ((1, fun(), 1, fun(), 1 ));
402 
403   EXPECT_EQ("fun ()", *Ctx->getExpandedText(at(8, 3)));
404   EXPECT_EQ("applyInt )", *Ctx->getOriginalText(at(8, 3)));
405 
406   EXPECT_EQ("((1,fun (),1,fun (),1", *Ctx->getExpandedText(at(13, 12)));
407   EXPECT_EQ("f(f(1))", *Ctx->getOriginalText(at(13, 12)));
408 }
409 
410 } // namespace
411 } // namespace analysis
412 } // namespace clang
413