brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.3 KiB · 25a76ed Raw
411 lines · cpp
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-exception6//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(37            llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>()),38        FileMgr(FileSystemOptions(), InMemoryFileSystem),39        Diags(DiagnosticIDs::create(), DiagOpts, 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  DiagnosticOptions DiagOpts;49  DiagnosticsEngine Diags;50  SourceManager SourceMgr;51  LangOptions LangOpts;52  std::shared_ptr<TargetOptions> TargetOpts;53  IntrusiveRefCntPtr<TargetInfo> Target;54 55  std::unique_ptr<MacroExpansionContext>56  getMacroExpansionContextFor(StringRef SourceText) {57    std::unique_ptr<llvm::MemoryBuffer> Buf =58        llvm::MemoryBuffer::getMemBuffer(SourceText);59    SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));60    HeaderSearchOptions HSOpts;61    TrivialModuleLoader ModLoader;62    PreprocessorOptions PPOpts;63    HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get());64    Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader,65                    /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false);66 67    PP.Initialize(*Target);68    auto Ctx = std::make_unique<MacroExpansionContext>(LangOpts);69    Ctx->registerForPreprocessor(PP);70 71    // Lex source text.72    PP.EnterMainSourceFile();73 74    PP.LexTokensUntilEOF();75 76    // Callbacks have been executed at this point.77    return Ctx;78  }79 80  /// Returns the expansion location to main file at the given row and column.81  SourceLocation at(unsigned row, unsigned col) const {82    SourceLocation Loc =83        SourceMgr.translateLineCol(SourceMgr.getMainFileID(), row, col);84    return SourceMgr.getExpansionLoc(Loc);85  }86 87  static std::string dumpExpandedTexts(const MacroExpansionContext &Ctx) {88    std::string Buf;89    llvm::raw_string_ostream OS{Buf};90    Ctx.dumpExpandedTextsToStream(OS);91    return Buf;92  }93 94  static std::string dumpExpansionRanges(const MacroExpansionContext &Ctx) {95    std::string Buf;96    llvm::raw_string_ostream OS{Buf};97    Ctx.dumpExpansionRangesToStream(OS);98    return Buf;99  }100};101 102TEST_F(MacroExpansionContextTest, IgnoresPragmas) {103  // No-crash during lexing.104  const auto Ctx = getMacroExpansionContextFor(R"code(105  _Pragma("pack(push, 1)")106  _Pragma("pack(pop, 1)")107      )code");108  // After preprocessing:109  // #pragma pack(push, 1)110  // #pragma pack(pop, 1)111 112  EXPECT_EQ("\n=============== ExpandedTokens ===============\n",113            dumpExpandedTexts(*Ctx));114  EXPECT_EQ("\n=============== ExpansionRanges ===============\n",115            dumpExpansionRanges(*Ctx));116 117  EXPECT_FALSE(Ctx->getExpandedText(at(2, 1)).has_value());118  EXPECT_FALSE(Ctx->getOriginalText(at(2, 1)).has_value());119 120  EXPECT_FALSE(Ctx->getExpandedText(at(2, 3)).has_value());121  EXPECT_FALSE(Ctx->getOriginalText(at(2, 3)).has_value());122 123  EXPECT_FALSE(Ctx->getExpandedText(at(3, 3)).has_value());124  EXPECT_FALSE(Ctx->getOriginalText(at(3, 3)).has_value());125}126 127TEST_F(MacroExpansionContextTest, NoneForNonExpansionLocations) {128  const auto Ctx = getMacroExpansionContextFor(R"code(129  #define EMPTY130  A b cd EMPTY ef EMPTY gh131EMPTY zz132      )code");133  // After preprocessing:134  //  A b cd ef gh135  //      zz136 137  // That's the beginning of the definition of EMPTY.138  EXPECT_FALSE(Ctx->getExpandedText(at(2, 11)).has_value());139  EXPECT_FALSE(Ctx->getOriginalText(at(2, 11)).has_value());140 141  // The space before the first expansion of EMPTY.142  EXPECT_FALSE(Ctx->getExpandedText(at(3, 9)).has_value());143  EXPECT_FALSE(Ctx->getOriginalText(at(3, 9)).has_value());144 145  // The beginning of the first expansion of EMPTY.146  EXPECT_TRUE(Ctx->getExpandedText(at(3, 10)).has_value());147  EXPECT_TRUE(Ctx->getOriginalText(at(3, 10)).has_value());148 149  // Pointing inside of the token EMPTY, but not at the beginning.150  // FIXME: We only deal with begin locations.151  EXPECT_FALSE(Ctx->getExpandedText(at(3, 11)).has_value());152  EXPECT_FALSE(Ctx->getOriginalText(at(3, 11)).has_value());153 154  // Same here.155  EXPECT_FALSE(Ctx->getExpandedText(at(3, 12)).has_value());156  EXPECT_FALSE(Ctx->getOriginalText(at(3, 12)).has_value());157 158  // The beginning of the last expansion of EMPTY.159  EXPECT_TRUE(Ctx->getExpandedText(at(4, 1)).has_value());160  EXPECT_TRUE(Ctx->getOriginalText(at(4, 1)).has_value());161 162  // Same as for the 3:11 case.163  EXPECT_FALSE(Ctx->getExpandedText(at(4, 2)).has_value());164  EXPECT_FALSE(Ctx->getOriginalText(at(4, 2)).has_value());165}166 167TEST_F(MacroExpansionContextTest, EmptyExpansions) {168  const auto Ctx = getMacroExpansionContextFor(R"code(169  #define EMPTY170  A b cd EMPTY ef EMPTY gh171EMPTY zz172      )code");173  // After preprocessing:174  //  A b cd ef gh175  //      zz176 177  EXPECT_EQ("", *Ctx->getExpandedText(at(3, 10)));178  EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(3, 10)));179 180  EXPECT_EQ("", *Ctx->getExpandedText(at(3, 19)));181  EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(3, 19)));182 183  EXPECT_EQ("", *Ctx->getExpandedText(at(4, 1)));184  EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(4, 1)));185}186 187TEST_F(MacroExpansionContextTest, TransitiveExpansions) {188  const auto Ctx = getMacroExpansionContextFor(R"code(189  #define EMPTY190  #define WOOF EMPTY ) EMPTY   1191  A b cd WOOF ef EMPTY gh192      )code");193  // After preprocessing:194  //  A b cd ) 1 ef gh195 196  EXPECT_EQ("WOOF", *Ctx->getOriginalText(at(4, 10)));197 198  EXPECT_EQ("", *Ctx->getExpandedText(at(4, 18)));199  EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(4, 18)));200}201 202TEST_F(MacroExpansionContextTest, MacroFunctions) {203  const auto Ctx = getMacroExpansionContextFor(R"code(204  #define EMPTY205  #define WOOF(x) x(EMPTY ) )  ) EMPTY   1206  A b cd WOOF($$ ef) EMPTY gh207  WOOF(WOOF)208  WOOF(WOOF(bar barr))),,),')209      )code");210  // After preprocessing:211  //  A b cd $$ ef( ) ) ) 1 gh212  //  WOOF( ) ) ) 1213  //  bar barr( ) ) ) 1( ) ) ) 1),,),')214 215  EXPECT_EQ("$$ ef ()))1", *Ctx->getExpandedText(at(4, 10)));216  EXPECT_EQ("WOOF($$ ef)", *Ctx->getOriginalText(at(4, 10)));217 218  EXPECT_EQ("", *Ctx->getExpandedText(at(4, 22)));219  EXPECT_EQ("EMPTY", *Ctx->getOriginalText(at(4, 22)));220 221  EXPECT_EQ("WOOF ()))1", *Ctx->getExpandedText(at(5, 3)));222  EXPECT_EQ("WOOF(WOOF)", *Ctx->getOriginalText(at(5, 3)));223 224  EXPECT_EQ("bar barr ()))1()))1", *Ctx->getExpandedText(at(6, 3)));225  EXPECT_EQ("WOOF(WOOF(bar barr))", *Ctx->getOriginalText(at(6, 3)));226}227 228TEST_F(MacroExpansionContextTest, VariadicMacros) {229  // From the GCC website.230  const auto Ctx = getMacroExpansionContextFor(R"code(231  #define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)232  eprintf("success!\n", );233  eprintf("success!\n");234 235  #define eprintf2(format, ...) \236    fprintf (stderr, format __VA_OPT__(,) __VA_ARGS__)237  eprintf2("success!\n", );238  eprintf2("success!\n");239      )code");240  // After preprocessing:241  //  fprintf (stderr, "success!\n", );242  //  fprintf (stderr, "success!\n", );243  //  fprintf (stderr, "success!\n" );244  //  fprintf (stderr, "success!\n" );245 246  EXPECT_EQ(R"(fprintf (stderr ,"success!\n",))",247            *Ctx->getExpandedText(at(3, 3)));248  EXPECT_EQ(R"(eprintf("success!\n", ))", *Ctx->getOriginalText(at(3, 3)));249 250  EXPECT_EQ(R"(fprintf (stderr ,"success!\n",))",251            *Ctx->getExpandedText(at(4, 3)));252  EXPECT_EQ(R"(eprintf("success!\n"))", *Ctx->getOriginalText(at(4, 3)));253 254  EXPECT_EQ(R"(fprintf (stderr ,"success!\n"))",255            *Ctx->getExpandedText(at(8, 3)));256  EXPECT_EQ(R"(eprintf2("success!\n", ))", *Ctx->getOriginalText(at(8, 3)));257 258  EXPECT_EQ(R"(fprintf (stderr ,"success!\n"))",259            *Ctx->getExpandedText(at(9, 3)));260  EXPECT_EQ(R"(eprintf2("success!\n"))", *Ctx->getOriginalText(at(9, 3)));261}262 263TEST_F(MacroExpansionContextTest, ConcatenationMacros) {264  // From the GCC website.265  const auto Ctx = getMacroExpansionContextFor(R"code(266  #define COMMAND(NAME)  { #NAME, NAME ## _command }267  struct command commands[] = {268    COMMAND(quit),269    COMMAND(help),270  };)code");271  // After preprocessing:272  //  struct command commands[] = {273  //    { "quit", quit_command },274  //    { "help", help_command },275  //  };276 277  EXPECT_EQ(R"({"quit",quit_command })", *Ctx->getExpandedText(at(4, 5)));278  EXPECT_EQ("COMMAND(quit)", *Ctx->getOriginalText(at(4, 5)));279 280  EXPECT_EQ(R"({"help",help_command })", *Ctx->getExpandedText(at(5, 5)));281  EXPECT_EQ("COMMAND(help)", *Ctx->getOriginalText(at(5, 5)));282}283 284TEST_F(MacroExpansionContextTest, StringizingMacros) {285  // From the GCC website.286  const auto Ctx = getMacroExpansionContextFor(R"code(287  #define WARN_IF(EXP) \288  do { if (EXP) \289          fprintf (stderr, "Warning: " #EXP "\n"); } \290  while (0)291  WARN_IF (x == 0);292 293  #define xstr(s) str(s)294  #define str(s) #s295  #define foo 4296  str (foo)297  xstr (foo)298      )code");299  // After preprocessing:300  //  do { if (x == 0) fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);301  //  "foo"302  //  "4"303 304  EXPECT_EQ(305      R"(do {if (x ==0)fprintf (stderr ,"Warning: ""x == 0""\n");}while (0))",306      *Ctx->getExpandedText(at(6, 3)));307  EXPECT_EQ("WARN_IF (x == 0)", *Ctx->getOriginalText(at(6, 3)));308 309  EXPECT_EQ(R"("foo")", *Ctx->getExpandedText(at(11, 3)));310  EXPECT_EQ("str (foo)", *Ctx->getOriginalText(at(11, 3)));311 312  EXPECT_EQ(R"("4")", *Ctx->getExpandedText(at(12, 3)));313  EXPECT_EQ("xstr (foo)", *Ctx->getOriginalText(at(12, 3)));314}315 316TEST_F(MacroExpansionContextTest, StringizingVariadicMacros) {317  const auto Ctx = getMacroExpansionContextFor(R"code(318  #define xstr(...) str(__VA_ARGS__)319  #define str(...) #__VA_ARGS__320  #define RParen2x ) )321  #define EMPTY322  #define f(x, ...) __VA_ARGS__ ! x * x323  #define g(...) zz EMPTY f(__VA_ARGS__ ! x) f() * y324  #define h(x, G) G(x) G(x ## x RParen2x325  #define q(G) h(apple, G(apple)) RParen2x326 327  q(g)328  q(xstr)329  g(RParen2x)330  f( RParen2x )s331      )code");332  // clang-format off333  // After preprocessing:334  //  zz ! apple ! x * apple ! x ! * * y(apple) zz ! apple ! x * apple ! x ! * * y(appleapple ) ) ) )335  //  "apple"(apple) "apple"(appleapple ) ) ) )336  //  zz ! * ) ! x) ! * * y337  //  ! ) ) * ) )338  // clang-format on339 340  EXPECT_EQ("zz !apple !x *apple !x !**y (apple )zz !apple !x *apple !x !**y "341            "(appleapple ))))",342            *Ctx->getExpandedText(at(11, 3)));343  EXPECT_EQ("q(g)", *Ctx->getOriginalText(at(11, 3)));344 345  EXPECT_EQ(R"res("apple"(apple )"apple"(appleapple )))))res",346            *Ctx->getExpandedText(at(12, 3)));347  EXPECT_EQ("q(xstr)", *Ctx->getOriginalText(at(12, 3)));348 349  EXPECT_EQ("zz !*)!x )!**y ", *Ctx->getExpandedText(at(13, 3)));350  EXPECT_EQ("g(RParen2x)", *Ctx->getOriginalText(at(13, 3)));351 352  EXPECT_EQ("!))*))", *Ctx->getExpandedText(at(14, 3)));353  EXPECT_EQ("f( RParen2x )", *Ctx->getOriginalText(at(14, 3)));354}355 356TEST_F(MacroExpansionContextTest, RedefUndef) {357  const auto Ctx = getMacroExpansionContextFor(R"code(358  #define Hi(x) Welcome x359  Hi(Adam)360  #define Hi Willkommen361  Hi Hans362  #undef Hi363  Hi(Hi)364      )code");365  // After preprocessing:366  //  Welcome Adam367  //  Willkommen Hans368  //  Hi(Hi)369 370  // FIXME: Extra space follows every identifier.371  EXPECT_EQ("Welcome Adam ", *Ctx->getExpandedText(at(3, 3)));372  EXPECT_EQ("Hi(Adam)", *Ctx->getOriginalText(at(3, 3)));373 374  EXPECT_EQ("Willkommen ", *Ctx->getExpandedText(at(5, 3)));375  EXPECT_EQ("Hi", *Ctx->getOriginalText(at(5, 3)));376 377  // There was no macro expansion at 7:3, we should expect None.378  EXPECT_FALSE(Ctx->getExpandedText(at(7, 3)).has_value());379  EXPECT_FALSE(Ctx->getOriginalText(at(7, 3)).has_value());380}381 382TEST_F(MacroExpansionContextTest, UnbalacedParenthesis) {383  const auto Ctx = getMacroExpansionContextFor(R"code(384  #define retArg(x) x385  #define retArgUnclosed retArg(fun()386  #define BB CC387  #define applyInt BB(int)388  #define CC(x) retArgUnclosed389 390  applyInt );391 392  #define expandArgUnclosedCommaExpr(x) (x, fun(), 1393  #define f expandArgUnclosedCommaExpr394 395  int x =  f(f(1))  ));396      )code");397  // After preprocessing:398  //  fun();399  //  int x = ((1, fun(), 1, fun(), 1 ));400 401  EXPECT_EQ("fun ()", *Ctx->getExpandedText(at(8, 3)));402  EXPECT_EQ("applyInt )", *Ctx->getOriginalText(at(8, 3)));403 404  EXPECT_EQ("((1,fun (),1,fun (),1", *Ctx->getExpandedText(at(13, 12)));405  EXPECT_EQ("f(f(1))", *Ctx->getOriginalText(at(13, 12)));406}407 408} // namespace409} // namespace analysis410} // namespace clang411