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