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