Home | History | Annotate | Download | only in Lex
      1 //===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks tests ------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===--------------------------------------------------------------===//
      9 
     10 #include "clang/Lex/Preprocessor.h"
     11 #include "clang/AST/ASTConsumer.h"
     12 #include "clang/AST/ASTContext.h"
     13 #include "clang/Basic/Diagnostic.h"
     14 #include "clang/Basic/DiagnosticOptions.h"
     15 #include "clang/Basic/FileManager.h"
     16 #include "clang/Basic/LangOptions.h"
     17 #include "clang/Basic/SourceManager.h"
     18 #include "clang/Basic/TargetInfo.h"
     19 #include "clang/Basic/TargetOptions.h"
     20 #include "clang/Lex/HeaderSearch.h"
     21 #include "clang/Lex/HeaderSearchOptions.h"
     22 #include "clang/Lex/ModuleLoader.h"
     23 #include "clang/Lex/PreprocessorOptions.h"
     24 #include "clang/Parse/Parser.h"
     25 #include "clang/Sema/Sema.h"
     26 #include "llvm/ADT/SmallString.h"
     27 #include "llvm/Support/Path.h"
     28 #include "gtest/gtest.h"
     29 
     30 using namespace llvm;
     31 using namespace llvm::sys;
     32 using namespace clang;
     33 
     34 namespace {
     35 
     36 // Stub out module loading.
     37 class VoidModuleLoader : public ModuleLoader {
     38   ModuleLoadResult loadModule(SourceLocation ImportLoc,
     39                               ModuleIdPath Path,
     40                               Module::NameVisibilityKind Visibility,
     41                               bool IsInclusionDirective) override {
     42     return ModuleLoadResult();
     43   }
     44 
     45   void makeModuleVisible(Module *Mod,
     46                          Module::NameVisibilityKind Visibility,
     47                          SourceLocation ImportLoc,
     48                          bool Complain) override { }
     49 
     50   GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
     51     { return nullptr; }
     52   bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
     53     { return 0; };
     54 };
     55 
     56 // Stub to collect data from InclusionDirective callbacks.
     57 class InclusionDirectiveCallbacks : public PPCallbacks {
     58 public:
     59   void InclusionDirective(SourceLocation HashLoc,
     60     const Token &IncludeTok,
     61     StringRef FileName,
     62     bool IsAngled,
     63     CharSourceRange FilenameRange,
     64     const FileEntry *File,
     65     StringRef SearchPath,
     66     StringRef RelativePath,
     67     const Module *Imported) {
     68       this->HashLoc = HashLoc;
     69       this->IncludeTok = IncludeTok;
     70       this->FileName = FileName.str();
     71       this->IsAngled = IsAngled;
     72       this->FilenameRange = FilenameRange;
     73       this->File = File;
     74       this->SearchPath = SearchPath.str();
     75       this->RelativePath = RelativePath.str();
     76       this->Imported = Imported;
     77   }
     78 
     79   SourceLocation HashLoc;
     80   Token IncludeTok;
     81   SmallString<16> FileName;
     82   bool IsAngled;
     83   CharSourceRange FilenameRange;
     84   const FileEntry* File;
     85   SmallString<16> SearchPath;
     86   SmallString<16> RelativePath;
     87   const Module* Imported;
     88 };
     89 
     90 // Stub to collect data from PragmaOpenCLExtension callbacks.
     91 class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
     92 public:
     93   typedef struct {
     94     SmallString<16> Name;
     95     unsigned State;
     96   } CallbackParameters;
     97 
     98   PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {};
     99 
    100   void PragmaOpenCLExtension(
    101     clang::SourceLocation NameLoc, const clang::IdentifierInfo *Name,
    102     clang::SourceLocation StateLoc, unsigned State) {
    103       this->NameLoc = NameLoc;
    104       this->Name = Name->getName();
    105       this->StateLoc = StateLoc;
    106       this->State = State;
    107   };
    108 
    109   SourceLocation NameLoc;
    110   SmallString<16> Name;
    111   SourceLocation StateLoc;
    112   unsigned State;
    113 };
    114 
    115 // PPCallbacks test fixture.
    116 class PPCallbacksTest : public ::testing::Test {
    117 protected:
    118   PPCallbacksTest()
    119       : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
    120         DiagOpts(new DiagnosticOptions()),
    121         Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()),
    122         SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) {
    123     TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
    124     Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
    125   }
    126 
    127   FileSystemOptions FileMgrOpts;
    128   FileManager FileMgr;
    129   IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
    130   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
    131   DiagnosticsEngine Diags;
    132   SourceManager SourceMgr;
    133   LangOptions LangOpts;
    134   std::shared_ptr<TargetOptions> TargetOpts;
    135   IntrusiveRefCntPtr<TargetInfo> Target;
    136 
    137   // Register a header path as a known file and add its location
    138   // to search path.
    139   void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath,
    140     bool IsSystemHeader) {
    141       // Tell FileMgr about header.
    142       FileMgr.getVirtualFile(HeaderPath, 0, 0);
    143 
    144       // Add header's parent path to search path.
    145       StringRef SearchPath = path::parent_path(HeaderPath);
    146       const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath);
    147       DirectoryLookup DL(DE, SrcMgr::C_User, false);
    148       HeaderInfo.AddSearchPath(DL, IsSystemHeader);
    149   }
    150 
    151   // Get the raw source string of the range.
    152   StringRef GetSourceString(CharSourceRange Range) {
    153     const char* B = SourceMgr.getCharacterData(Range.getBegin());
    154     const char* E = SourceMgr.getCharacterData(Range.getEnd());
    155 
    156     return StringRef(B, E - B);
    157   }
    158 
    159   // Run lexer over SourceText and collect FilenameRange from
    160   // the InclusionDirective callback.
    161   CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText,
    162       const char* HeaderPath, bool SystemHeader) {
    163     MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(SourceText);
    164     SourceMgr.setMainFileID(SourceMgr.createFileID(Buf));
    165 
    166     VoidModuleLoader ModLoader;
    167 
    168     IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts = new HeaderSearchOptions();
    169     HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts,
    170                             Target.get());
    171     AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
    172 
    173     IntrusiveRefCntPtr<PreprocessorOptions> PPOpts = new PreprocessorOptions();
    174     Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader,
    175                     /*IILookup =*/nullptr,
    176                     /*OwnsHeaderSearch =*/false);
    177     PP.Initialize(*Target);
    178     InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
    179     PP.addPPCallbacks(Callbacks); // Takes ownership.
    180 
    181     // Lex source text.
    182     PP.EnterMainSourceFile();
    183 
    184     while (true) {
    185       Token Tok;
    186       PP.Lex(Tok);
    187       if (Tok.is(tok::eof))
    188         break;
    189     }
    190 
    191     // Callbacks have been executed at this point -- return filename range.
    192     return Callbacks->FilenameRange;
    193   }
    194 
    195   PragmaOpenCLExtensionCallbacks::CallbackParameters
    196   PragmaOpenCLExtensionCall(const char* SourceText) {
    197     LangOptions OpenCLLangOpts;
    198     OpenCLLangOpts.OpenCL = 1;
    199 
    200     MemoryBuffer* sourceBuf = MemoryBuffer::getMemBuffer(SourceText, "test.cl");
    201     SourceMgr.setMainFileID(SourceMgr.createFileID(sourceBuf));
    202 
    203     VoidModuleLoader ModLoader;
    204     HeaderSearch HeaderInfo(new HeaderSearchOptions, SourceMgr, Diags,
    205                             OpenCLLangOpts, Target.get());
    206 
    207     Preprocessor PP(new PreprocessorOptions(), Diags, OpenCLLangOpts, SourceMgr,
    208                     HeaderInfo, ModLoader, /*IILookup =*/nullptr,
    209                     /*OwnsHeaderSearch =*/false);
    210     PP.Initialize(*Target);
    211 
    212     // parser actually sets correct pragma handlers for preprocessor
    213     // according to LangOptions, so we init Parser to register opencl
    214     // pragma handlers
    215     ASTContext Context(OpenCLLangOpts, SourceMgr,
    216                        PP.getIdentifierTable(), PP.getSelectorTable(),
    217                        PP.getBuiltinInfo());
    218     Context.InitBuiltinTypes(*Target);
    219 
    220     ASTConsumer Consumer;
    221     Sema S(PP, Context, Consumer);
    222     Parser P(PP, S, false);
    223     PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks;
    224     PP.addPPCallbacks(Callbacks); // Takes ownership.
    225 
    226     // Lex source text.
    227     PP.EnterMainSourceFile();
    228     while (true) {
    229       Token Tok;
    230       PP.Lex(Tok);
    231       if (Tok.is(tok::eof))
    232         break;
    233     }
    234 
    235     PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = {
    236       Callbacks->Name,
    237       Callbacks->State
    238     };
    239     return RetVal;
    240   }
    241 };
    242 
    243 TEST_F(PPCallbacksTest, QuotedFilename) {
    244   const char* Source =
    245     "#include \"quoted.h\"\n";
    246 
    247   CharSourceRange Range =
    248     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
    249 
    250   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
    251 }
    252 
    253 TEST_F(PPCallbacksTest, AngledFilename) {
    254   const char* Source =
    255     "#include <angled.h>\n";
    256 
    257   CharSourceRange Range =
    258     InclusionDirectiveFilenameRange(Source, "/angled.h", true);
    259 
    260   ASSERT_EQ("<angled.h>", GetSourceString(Range));
    261 }
    262 
    263 TEST_F(PPCallbacksTest, QuotedInMacro) {
    264   const char* Source =
    265     "#define MACRO_QUOTED \"quoted.h\"\n"
    266     "#include MACRO_QUOTED\n";
    267 
    268   CharSourceRange Range =
    269     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
    270 
    271   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
    272 }
    273 
    274 TEST_F(PPCallbacksTest, AngledInMacro) {
    275   const char* Source =
    276     "#define MACRO_ANGLED <angled.h>\n"
    277     "#include MACRO_ANGLED\n";
    278 
    279   CharSourceRange Range =
    280     InclusionDirectiveFilenameRange(Source, "/angled.h", true);
    281 
    282   ASSERT_EQ("<angled.h>", GetSourceString(Range));
    283 }
    284 
    285 TEST_F(PPCallbacksTest, StringizedMacroArgument) {
    286   const char* Source =
    287     "#define MACRO_STRINGIZED(x) #x\n"
    288     "#include MACRO_STRINGIZED(quoted.h)\n";
    289 
    290   CharSourceRange Range =
    291     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
    292 
    293   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
    294 }
    295 
    296 TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) {
    297   const char* Source =
    298     "#define MACRO_ANGLED <angled.h>\n"
    299     "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
    300     "#include MACRO_CONCAT(MACRO, ANGLED)\n";
    301 
    302   CharSourceRange Range =
    303     InclusionDirectiveFilenameRange(Source, "/angled.h", false);
    304 
    305   ASSERT_EQ("<angled.h>", GetSourceString(Range));
    306 }
    307 
    308 TEST_F(PPCallbacksTest, TrigraphFilename) {
    309   const char* Source =
    310     "#include \"tri\?\?-graph.h\"\n";
    311 
    312   CharSourceRange Range =
    313     InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
    314 
    315   ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
    316 }
    317 
    318 TEST_F(PPCallbacksTest, TrigraphInMacro) {
    319   const char* Source =
    320     "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
    321     "#include MACRO_TRIGRAPH\n";
    322 
    323   CharSourceRange Range =
    324     InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
    325 
    326   ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
    327 }
    328 
    329 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) {
    330   const char* Source =
    331     "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
    332 
    333   PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
    334     PragmaOpenCLExtensionCall(Source);
    335 
    336   ASSERT_EQ("cl_khr_fp64", Parameters.Name);
    337   unsigned ExpectedState = 1;
    338   ASSERT_EQ(ExpectedState, Parameters.State);
    339 }
    340 
    341 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) {
    342   const char* Source =
    343     "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
    344 
    345   PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
    346     PragmaOpenCLExtensionCall(Source);
    347 
    348   ASSERT_EQ("cl_khr_fp16", Parameters.Name);
    349   unsigned ExpectedState = 0;
    350   ASSERT_EQ(ExpectedState, Parameters.State);
    351 }
    352 
    353 } // anonoymous namespace
    354