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/Basic/Diagnostic.h"
     12 #include "clang/Basic/FileManager.h"
     13 #include "clang/Basic/LangOptions.h"
     14 #include "clang/Basic/SourceManager.h"
     15 #include "clang/Basic/TargetInfo.h"
     16 #include "clang/Basic/TargetOptions.h"
     17 #include "clang/Lex/HeaderSearch.h"
     18 #include "clang/Lex/HeaderSearchOptions.h"
     19 #include "clang/Lex/ModuleLoader.h"
     20 #include "clang/Lex/PreprocessorOptions.h"
     21 #include "llvm/ADT/SmallString.h"
     22 #include "llvm/Support/PathV2.h"
     23 #include "gtest/gtest.h"
     24 
     25 using namespace llvm;
     26 using namespace llvm::sys;
     27 using namespace clang;
     28 
     29 namespace {
     30 
     31 // Stub out module loading.
     32 class VoidModuleLoader : public ModuleLoader {
     33   virtual ModuleLoadResult loadModule(SourceLocation ImportLoc,
     34                                       ModuleIdPath Path,
     35                                       Module::NameVisibilityKind Visibility,
     36                                       bool IsInclusionDirective) {
     37     return ModuleLoadResult();
     38   }
     39 
     40   virtual void makeModuleVisible(Module *Mod,
     41                                  Module::NameVisibilityKind Visibility,
     42                                  SourceLocation ImportLoc) { }
     43 };
     44 
     45 // Stub to collect data from InclusionDirective callbacks.
     46 class InclusionDirectiveCallbacks : public PPCallbacks {
     47 public:
     48   void InclusionDirective(SourceLocation HashLoc,
     49     const Token &IncludeTok,
     50     StringRef FileName,
     51     bool IsAngled,
     52     CharSourceRange FilenameRange,
     53     const FileEntry *File,
     54     StringRef SearchPath,
     55     StringRef RelativePath,
     56     const Module *Imported) {
     57       this->HashLoc = HashLoc;
     58       this->IncludeTok = IncludeTok;
     59       this->FileName = FileName.str();
     60       this->IsAngled = IsAngled;
     61       this->FilenameRange = FilenameRange;
     62       this->File = File;
     63       this->SearchPath = SearchPath.str();
     64       this->RelativePath = RelativePath.str();
     65       this->Imported = Imported;
     66   }
     67 
     68   SourceLocation HashLoc;
     69   Token IncludeTok;
     70   SmallString<16> FileName;
     71   bool IsAngled;
     72   CharSourceRange FilenameRange;
     73   const FileEntry* File;
     74   SmallString<16> SearchPath;
     75   SmallString<16> RelativePath;
     76   const Module* Imported;
     77 };
     78 
     79 // PPCallbacks test fixture.
     80 class PPCallbacksTest : public ::testing::Test {
     81 protected:
     82   PPCallbacksTest()
     83     : FileMgr(FileMgrOpts),
     84       DiagID(new DiagnosticIDs()),
     85       DiagOpts(new DiagnosticOptions()),
     86       Diags(DiagID, DiagOpts.getPtr(), new IgnoringDiagConsumer()),
     87       SourceMgr(Diags, FileMgr) {
     88     TargetOpts = new TargetOptions();
     89     TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
     90     Target = TargetInfo::CreateTargetInfo(Diags, &*TargetOpts);
     91   }
     92 
     93   FileSystemOptions FileMgrOpts;
     94   FileManager FileMgr;
     95   IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
     96   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
     97   DiagnosticsEngine Diags;
     98   SourceManager SourceMgr;
     99   LangOptions LangOpts;
    100   IntrusiveRefCntPtr<TargetOptions> TargetOpts;
    101   IntrusiveRefCntPtr<TargetInfo> Target;
    102 
    103   // Register a header path as a known file and add its location
    104   // to search path.
    105   void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath,
    106     bool IsSystemHeader) {
    107       // Tell FileMgr about header.
    108       FileMgr.getVirtualFile(HeaderPath, 0, 0);
    109 
    110       // Add header's parent path to search path.
    111       StringRef SearchPath = path::parent_path(HeaderPath);
    112       const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath);
    113       DirectoryLookup DL(DE, SrcMgr::C_User, false);
    114       HeaderInfo.AddSearchPath(DL, IsSystemHeader);
    115   }
    116 
    117   // Get the raw source string of the range.
    118   StringRef GetSourceString(CharSourceRange Range) {
    119     const char* B = SourceMgr.getCharacterData(Range.getBegin());
    120     const char* E = SourceMgr.getCharacterData(Range.getEnd());
    121 
    122     return StringRef(B, E - B);
    123   }
    124 
    125   // Run lexer over SourceText and collect FilenameRange from
    126   // the InclusionDirective callback.
    127   CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText,
    128       const char* HeaderPath, bool SystemHeader) {
    129     MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(SourceText);
    130     (void)SourceMgr.createMainFileIDForMemBuffer(Buf);
    131 
    132     VoidModuleLoader ModLoader;
    133 
    134     IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts = new HeaderSearchOptions();
    135     HeaderSearch HeaderInfo(HSOpts, FileMgr, Diags, LangOpts, Target.getPtr());
    136     AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
    137 
    138     IntrusiveRefCntPtr<PreprocessorOptions> PPOpts = new PreprocessorOptions();
    139     Preprocessor PP(PPOpts, Diags, LangOpts,
    140       Target.getPtr(),
    141       SourceMgr, HeaderInfo, ModLoader,
    142       /*IILookup =*/ 0,
    143       /*OwnsHeaderSearch =*/false,
    144       /*DelayInitialization =*/ false);
    145     InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
    146     PP.addPPCallbacks(Callbacks); // Takes ownership.
    147 
    148     // Lex source text.
    149     PP.EnterMainSourceFile();
    150 
    151     while (true) {
    152       Token Tok;
    153       PP.Lex(Tok);
    154       if (Tok.is(tok::eof))
    155         break;
    156     }
    157 
    158     // Callbacks have been executed at this point -- return filename range.
    159     return Callbacks->FilenameRange;
    160   }
    161 };
    162 
    163 TEST_F(PPCallbacksTest, QuotedFilename) {
    164   const char* Source =
    165     "#include \"quoted.h\"\n";
    166 
    167   CharSourceRange Range =
    168     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
    169 
    170   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
    171 }
    172 
    173 TEST_F(PPCallbacksTest, AngledFilename) {
    174   const char* Source =
    175     "#include <angled.h>\n";
    176 
    177   CharSourceRange Range =
    178     InclusionDirectiveFilenameRange(Source, "/angled.h", true);
    179 
    180   ASSERT_EQ("<angled.h>", GetSourceString(Range));
    181 }
    182 
    183 TEST_F(PPCallbacksTest, QuotedInMacro) {
    184   const char* Source =
    185     "#define MACRO_QUOTED \"quoted.h\"\n"
    186     "#include MACRO_QUOTED\n";
    187 
    188   CharSourceRange Range =
    189     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
    190 
    191   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
    192 }
    193 
    194 TEST_F(PPCallbacksTest, AngledInMacro) {
    195   const char* Source =
    196     "#define MACRO_ANGLED <angled.h>\n"
    197     "#include MACRO_ANGLED\n";
    198 
    199   CharSourceRange Range =
    200     InclusionDirectiveFilenameRange(Source, "/angled.h", true);
    201 
    202   ASSERT_EQ("<angled.h>", GetSourceString(Range));
    203 }
    204 
    205 TEST_F(PPCallbacksTest, StringizedMacroArgument) {
    206   const char* Source =
    207     "#define MACRO_STRINGIZED(x) #x\n"
    208     "#include MACRO_STRINGIZED(quoted.h)\n";
    209 
    210   CharSourceRange Range =
    211     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
    212 
    213   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
    214 }
    215 
    216 TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) {
    217   const char* Source =
    218     "#define MACRO_ANGLED <angled.h>\n"
    219     "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
    220     "#include MACRO_CONCAT(MACRO, ANGLED)\n";
    221 
    222   CharSourceRange Range =
    223     InclusionDirectiveFilenameRange(Source, "/angled.h", false);
    224 
    225   ASSERT_EQ("<angled.h>", GetSourceString(Range));
    226 }
    227 
    228 TEST_F(PPCallbacksTest, TrigraphFilename) {
    229   const char* Source =
    230     "#include \"tri\?\?-graph.h\"\n";
    231 
    232   CharSourceRange Range =
    233     InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
    234 
    235   ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
    236 }
    237 
    238 TEST_F(PPCallbacksTest, TrigraphInMacro) {
    239   const char* Source =
    240     "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
    241     "#include MACRO_TRIGRAPH\n";
    242 
    243   CharSourceRange Range =
    244     InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
    245 
    246   ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
    247 }
    248 
    249 } // anonoymous namespace
    250