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