Home | History | Annotate | Download | only in Frontend
      1 //===- unittests/Frontend/FrontendActionTest.cpp - FrontendAction 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/Frontend/FrontendAction.h"
     11 #include "clang/AST/ASTConsumer.h"
     12 #include "clang/AST/ASTContext.h"
     13 #include "clang/AST/RecursiveASTVisitor.h"
     14 #include "clang/Frontend/CompilerInstance.h"
     15 #include "clang/Frontend/CompilerInvocation.h"
     16 #include "clang/Lex/Preprocessor.h"
     17 #include "clang/Sema/Sema.h"
     18 #include "llvm/ADT/Triple.h"
     19 #include "llvm/Support/MemoryBuffer.h"
     20 #include "gtest/gtest.h"
     21 
     22 using namespace llvm;
     23 using namespace clang;
     24 
     25 namespace {
     26 
     27 class TestASTFrontendAction : public ASTFrontendAction {
     28 public:
     29   TestASTFrontendAction(bool enableIncrementalProcessing = false,
     30                         bool actOnEndOfTranslationUnit = false)
     31     : EnableIncrementalProcessing(enableIncrementalProcessing),
     32       ActOnEndOfTranslationUnit(actOnEndOfTranslationUnit) { }
     33 
     34   bool EnableIncrementalProcessing;
     35   bool ActOnEndOfTranslationUnit;
     36   std::vector<std::string> decl_names;
     37 
     38   bool BeginSourceFileAction(CompilerInstance &ci,
     39                              StringRef filename) override {
     40     if (EnableIncrementalProcessing)
     41       ci.getPreprocessor().enableIncrementalProcessing();
     42 
     43     return ASTFrontendAction::BeginSourceFileAction(ci, filename);
     44   }
     45 
     46   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
     47                                                  StringRef InFile) override {
     48     return llvm::make_unique<Visitor>(CI, ActOnEndOfTranslationUnit,
     49                                       decl_names);
     50   }
     51 
     52 private:
     53   class Visitor : public ASTConsumer, public RecursiveASTVisitor<Visitor> {
     54   public:
     55     Visitor(CompilerInstance &CI, bool ActOnEndOfTranslationUnit,
     56             std::vector<std::string> &decl_names) :
     57       CI(CI), ActOnEndOfTranslationUnit(ActOnEndOfTranslationUnit),
     58       decl_names_(decl_names) {}
     59 
     60     void HandleTranslationUnit(ASTContext &context) override {
     61       if (ActOnEndOfTranslationUnit) {
     62         CI.getSema().ActOnEndOfTranslationUnit();
     63       }
     64       TraverseDecl(context.getTranslationUnitDecl());
     65     }
     66 
     67     virtual bool VisitNamedDecl(NamedDecl *Decl) {
     68       decl_names_.push_back(Decl->getQualifiedNameAsString());
     69       return true;
     70     }
     71 
     72   private:
     73     CompilerInstance &CI;
     74     bool ActOnEndOfTranslationUnit;
     75     std::vector<std::string> &decl_names_;
     76   };
     77 };
     78 
     79 TEST(ASTFrontendAction, Sanity) {
     80   CompilerInvocation *invocation = new CompilerInvocation;
     81   invocation->getPreprocessorOpts().addRemappedFile(
     82       "test.cc",
     83       MemoryBuffer::getMemBuffer("int main() { float x; }").release());
     84   invocation->getFrontendOpts().Inputs.push_back(FrontendInputFile("test.cc",
     85                                                                    IK_CXX));
     86   invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
     87   invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
     88   CompilerInstance compiler;
     89   compiler.setInvocation(invocation);
     90   compiler.createDiagnostics();
     91 
     92   TestASTFrontendAction test_action;
     93   ASSERT_TRUE(compiler.ExecuteAction(test_action));
     94   ASSERT_EQ(2U, test_action.decl_names.size());
     95   EXPECT_EQ("main", test_action.decl_names[0]);
     96   EXPECT_EQ("x", test_action.decl_names[1]);
     97 }
     98 
     99 TEST(ASTFrontendAction, IncrementalParsing) {
    100   CompilerInvocation *invocation = new CompilerInvocation;
    101   invocation->getPreprocessorOpts().addRemappedFile(
    102       "test.cc",
    103       MemoryBuffer::getMemBuffer("int main() { float x; }").release());
    104   invocation->getFrontendOpts().Inputs.push_back(FrontendInputFile("test.cc",
    105                                                                    IK_CXX));
    106   invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
    107   invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
    108   CompilerInstance compiler;
    109   compiler.setInvocation(invocation);
    110   compiler.createDiagnostics();
    111 
    112   TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true);
    113   ASSERT_TRUE(compiler.ExecuteAction(test_action));
    114   ASSERT_EQ(2U, test_action.decl_names.size());
    115   EXPECT_EQ("main", test_action.decl_names[0]);
    116   EXPECT_EQ("x", test_action.decl_names[1]);
    117 }
    118 
    119 TEST(ASTFrontendAction, LateTemplateIncrementalParsing) {
    120   CompilerInvocation *invocation = new CompilerInvocation;
    121   invocation->getLangOpts()->CPlusPlus = true;
    122   invocation->getLangOpts()->DelayedTemplateParsing = true;
    123   invocation->getPreprocessorOpts().addRemappedFile(
    124     "test.cc", MemoryBuffer::getMemBuffer(
    125       "template<typename T> struct A { A(T); T data; };\n"
    126       "template<typename T> struct B: public A<T> {\n"
    127       "  B();\n"
    128       "  B(B const& b): A<T>(b.data) {}\n"
    129       "};\n"
    130       "B<char> c() { return B<char>(); }\n").release());
    131   invocation->getFrontendOpts().Inputs.push_back(FrontendInputFile("test.cc",
    132                                                                    IK_CXX));
    133   invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
    134   invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
    135   CompilerInstance compiler;
    136   compiler.setInvocation(invocation);
    137   compiler.createDiagnostics();
    138 
    139   TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true,
    140                                     /*actOnEndOfTranslationUnit=*/true);
    141   ASSERT_TRUE(compiler.ExecuteAction(test_action));
    142   ASSERT_EQ(13U, test_action.decl_names.size());
    143   EXPECT_EQ("A", test_action.decl_names[0]);
    144   EXPECT_EQ("c", test_action.decl_names[12]);
    145 }
    146 
    147 struct TestPPCallbacks : public PPCallbacks {
    148   TestPPCallbacks() : SeenEnd(false) {}
    149 
    150   void EndOfMainFile() override { SeenEnd = true; }
    151 
    152   bool SeenEnd;
    153 };
    154 
    155 class TestPPCallbacksFrontendAction : public PreprocessorFrontendAction {
    156   TestPPCallbacks *Callbacks;
    157 
    158 public:
    159   TestPPCallbacksFrontendAction(TestPPCallbacks *C)
    160       : Callbacks(C), SeenEnd(false) {}
    161 
    162   void ExecuteAction() override {
    163     Preprocessor &PP = getCompilerInstance().getPreprocessor();
    164     PP.addPPCallbacks(std::unique_ptr<TestPPCallbacks>(Callbacks));
    165     PP.EnterMainSourceFile();
    166   }
    167   void EndSourceFileAction() override { SeenEnd = Callbacks->SeenEnd; }
    168 
    169   bool SeenEnd;
    170 };
    171 
    172 TEST(PreprocessorFrontendAction, EndSourceFile) {
    173   CompilerInvocation *Invocation = new CompilerInvocation;
    174   Invocation->getPreprocessorOpts().addRemappedFile(
    175       "test.cc",
    176       MemoryBuffer::getMemBuffer("int main() { float x; }").release());
    177   Invocation->getFrontendOpts().Inputs.push_back(
    178       FrontendInputFile("test.cc", IK_CXX));
    179   Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
    180   Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
    181   CompilerInstance Compiler;
    182   Compiler.setInvocation(Invocation);
    183   Compiler.createDiagnostics();
    184 
    185   TestPPCallbacks *Callbacks = new TestPPCallbacks;
    186   TestPPCallbacksFrontendAction TestAction(Callbacks);
    187   ASSERT_FALSE(Callbacks->SeenEnd);
    188   ASSERT_FALSE(TestAction.SeenEnd);
    189   ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
    190   // Check that EndOfMainFile was called before EndSourceFileAction.
    191   ASSERT_TRUE(TestAction.SeenEnd);
    192 }
    193 
    194 } // anonymous namespace
    195