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