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
29namespace clang {
30namespace analysis {
31namespace {
32
33class MacroExpansionContextTest : public ::testing::Test {
34protected:
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, Opts: 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>
57 getMacroExpansionContextFor(StringRef SourceText) {
58 std::unique_ptr<llvm::MemoryBuffer> Buf =
59 llvm::MemoryBuffer::getMemBuffer(InputData: SourceText);
60 SourceMgr.setMainFileID(SourceMgr.createFileID(Buffer: 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: *Target);
70 auto Ctx = std::make_unique<MacroExpansionContext>(args&: 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.
83 SourceLocation at(unsigned row, unsigned col) const {
84 SourceLocation Loc =
85 SourceMgr.translateLineCol(FID: SourceMgr.getMainFileID(), Line: row, Col: col);
86 return SourceMgr.getExpansionLoc(Loc);
87 }
88
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
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
104TEST_F(MacroExpansionContextTest, IgnoresPragmas) {
105 // No-crash during lexing.
106 const auto Ctx = getMacroExpansionContextFor(SourceText: 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
129TEST_F(MacroExpansionContextTest, NoneForNonExpansionLocations) {
130 const auto Ctx = getMacroExpansionContextFor(SourceText: R"code(
131 #define EMPTY
132 A b cd EMPTY ef EMPTY gh
133EMPTY 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
169TEST_F(MacroExpansionContextTest, EmptyExpansions) {
170 const auto Ctx = getMacroExpansionContextFor(SourceText: R"code(
171 #define EMPTY
172 A b cd EMPTY ef EMPTY gh
173EMPTY 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
189TEST_F(MacroExpansionContextTest, TransitiveExpansions) {
190 const auto Ctx = getMacroExpansionContextFor(SourceText: 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
204TEST_F(MacroExpansionContextTest, MacroFunctions) {
205 const auto Ctx = getMacroExpansionContextFor(SourceText: 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
230TEST_F(MacroExpansionContextTest, VariadicMacros) {
231 // From the GCC website.
232 const auto Ctx = getMacroExpansionContextFor(SourceText: 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
265TEST_F(MacroExpansionContextTest, ConcatenationMacros) {
266 // From the GCC website.
267 const auto Ctx = getMacroExpansionContextFor(SourceText: 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
286TEST_F(MacroExpansionContextTest, StringizingMacros) {
287 // From the GCC website.
288 const auto Ctx = getMacroExpansionContextFor(SourceText: 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
318TEST_F(MacroExpansionContextTest, StringizingVariadicMacros) {
319 const auto Ctx = getMacroExpansionContextFor(SourceText: 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
358TEST_F(MacroExpansionContextTest, RedefUndef) {
359 const auto Ctx = getMacroExpansionContextFor(SourceText: 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
384TEST_F(MacroExpansionContextTest, UnbalacedParenthesis) {
385 const auto Ctx = getMacroExpansionContextFor(SourceText: 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

source code of clang/unittests/Analysis/MacroExpansionContextTest.cpp