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