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