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: 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> 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. 88 SourceLocation at(unsigned row, unsigned col) const { 89 SourceLocation Loc = 90 SourceMgr.translateLineCol(SourceMgr.getMainFileID(), row, col); 91 return SourceMgr.getExpansionLoc(Loc); 92 } 93 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 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 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 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 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 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 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 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 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 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 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 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 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