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