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:
TestASTFrontendAction(bool enableIncrementalProcessing=false,bool actOnEndOfTranslationUnit=false)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
BeginSourceFileAction(CompilerInstance & ci,StringRef filename)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
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)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:
Visitor(CompilerInstance & CI,bool ActOnEndOfTranslationUnit,std::vector<std::string> & decl_names)55 Visitor(CompilerInstance &CI, bool ActOnEndOfTranslationUnit,
56 std::vector<std::string> &decl_names) :
57 CI(CI), ActOnEndOfTranslationUnit(ActOnEndOfTranslationUnit),
58 decl_names_(decl_names) {}
59
HandleTranslationUnit(ASTContext & context)60 void HandleTranslationUnit(ASTContext &context) override {
61 if (ActOnEndOfTranslationUnit) {
62 CI.getSema().ActOnEndOfTranslationUnit();
63 }
64 TraverseDecl(context.getTranslationUnitDecl());
65 }
66
VisitNamedDecl(NamedDecl * Decl)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
TEST(ASTFrontendAction,Sanity)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
TEST(ASTFrontendAction,IncrementalParsing)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
TEST(ASTFrontendAction,LateTemplateIncrementalParsing)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 {
TestPPCallbacks__anon5af6fb650111::TestPPCallbacks148 TestPPCallbacks() : SeenEnd(false) {}
149
EndOfMainFile__anon5af6fb650111::TestPPCallbacks150 void EndOfMainFile() override { SeenEnd = true; }
151
152 bool SeenEnd;
153 };
154
155 class TestPPCallbacksFrontendAction : public PreprocessorFrontendAction {
156 TestPPCallbacks *Callbacks;
157
158 public:
TestPPCallbacksFrontendAction(TestPPCallbacks * C)159 TestPPCallbacksFrontendAction(TestPPCallbacks *C)
160 : Callbacks(C), SeenEnd(false) {}
161
ExecuteAction()162 void ExecuteAction() override {
163 Preprocessor &PP = getCompilerInstance().getPreprocessor();
164 PP.addPPCallbacks(std::unique_ptr<TestPPCallbacks>(Callbacks));
165 PP.EnterMainSourceFile();
166 }
EndSourceFileAction()167 void EndSourceFileAction() override { SeenEnd = Callbacks->SeenEnd; }
168
169 bool SeenEnd;
170 };
171
TEST(PreprocessorFrontendAction,EndSourceFile)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