blob: ce0144538db7f4eda96867c593531ae80022b036 [file] [log] [blame]
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +00001//===- 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
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000010#include "clang/AST/ASTConsumer.h"
Chandler Carruth320d9662012-12-04 09:45:34 +000011#include "clang/AST/ASTContext.h"
Chandler Carruthfa0b3bb2012-12-04 09:53:37 +000012#include "clang/AST/RecursiveASTVisitor.h"
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000013#include "clang/Frontend/CompilerInstance.h"
14#include "clang/Frontend/CompilerInvocation.h"
Mehdi Amini9670f842016-07-18 19:02:11 +000015#include "clang/Frontend/FrontendAction.h"
Benjamin Kramer7de99692016-11-16 18:15:26 +000016#include "clang/Frontend/FrontendActions.h"
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +000017#include "clang/Lex/Preprocessor.h"
Mehdi Amini9670f842016-07-18 19:02:11 +000018#include "clang/Lex/PreprocessorOptions.h"
Reid Kleckner89bd8d62014-10-22 17:50:19 +000019#include "clang/Sema/Sema.h"
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000020#include "llvm/ADT/Triple.h"
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000021#include "llvm/Support/MemoryBuffer.h"
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000022#include "gtest/gtest.h"
23
24using namespace llvm;
25using namespace clang;
26
27namespace {
28
29class TestASTFrontendAction : public ASTFrontendAction {
30public:
Reid Kleckner89bd8d62014-10-22 17:50:19 +000031 TestASTFrontendAction(bool enableIncrementalProcessing = false,
32 bool actOnEndOfTranslationUnit = false)
33 : EnableIncrementalProcessing(enableIncrementalProcessing),
34 ActOnEndOfTranslationUnit(actOnEndOfTranslationUnit) { }
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +000035
36 bool EnableIncrementalProcessing;
Reid Kleckner89bd8d62014-10-22 17:50:19 +000037 bool ActOnEndOfTranslationUnit;
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000038 std::vector<std::string> decl_names;
39
Richard Smithd9259c22017-06-09 01:36:10 +000040 bool BeginSourceFileAction(CompilerInstance &ci) override {
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +000041 if (EnableIncrementalProcessing)
42 ci.getPreprocessor().enableIncrementalProcessing();
43
Richard Smithd9259c22017-06-09 01:36:10 +000044 return ASTFrontendAction::BeginSourceFileAction(ci);
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +000045 }
46
Alexander Kornienko34eb2072015-04-11 02:00:23 +000047 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
48 StringRef InFile) override {
Reid Kleckner89bd8d62014-10-22 17:50:19 +000049 return llvm::make_unique<Visitor>(CI, ActOnEndOfTranslationUnit,
50 decl_names);
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000051 }
52
53private:
54 class Visitor : public ASTConsumer, public RecursiveASTVisitor<Visitor> {
55 public:
Reid Kleckner89bd8d62014-10-22 17:50:19 +000056 Visitor(CompilerInstance &CI, bool ActOnEndOfTranslationUnit,
57 std::vector<std::string> &decl_names) :
58 CI(CI), ActOnEndOfTranslationUnit(ActOnEndOfTranslationUnit),
59 decl_names_(decl_names) {}
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000060
Alexander Kornienko34eb2072015-04-11 02:00:23 +000061 void HandleTranslationUnit(ASTContext &context) override {
Reid Kleckner89bd8d62014-10-22 17:50:19 +000062 if (ActOnEndOfTranslationUnit) {
63 CI.getSema().ActOnEndOfTranslationUnit();
64 }
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000065 TraverseDecl(context.getTranslationUnitDecl());
66 }
67
68 virtual bool VisitNamedDecl(NamedDecl *Decl) {
69 decl_names_.push_back(Decl->getQualifiedNameAsString());
70 return true;
71 }
72
73 private:
Reid Kleckner89bd8d62014-10-22 17:50:19 +000074 CompilerInstance &CI;
75 bool ActOnEndOfTranslationUnit;
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000076 std::vector<std::string> &decl_names_;
77 };
78};
79
80TEST(ASTFrontendAction, Sanity) {
David Blaikieea4395e2017-01-06 19:49:01 +000081 auto invocation = std::make_shared<CompilerInvocation>();
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000082 invocation->getPreprocessorOpts().addRemappedFile(
Rafael Espindolad87f8d72014-08-27 20:03:29 +000083 "test.cc",
84 MemoryBuffer::getMemBuffer("int main() { float x; }").release());
Richard Smith40c0efa2017-04-26 18:57:40 +000085 invocation->getFrontendOpts().Inputs.push_back(
86 FrontendInputFile("test.cc", InputKind::CXX));
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000087 invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
88 invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
89 CompilerInstance compiler;
David Blaikieea4395e2017-01-06 19:49:01 +000090 compiler.setInvocation(std::move(invocation));
Sean Silvaf1b49e22013-01-20 01:58:28 +000091 compiler.createDiagnostics();
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000092
93 TestASTFrontendAction test_action;
94 ASSERT_TRUE(compiler.ExecuteAction(test_action));
Alp Toker56b5cc92013-12-15 10:36:26 +000095 ASSERT_EQ(2U, test_action.decl_names.size());
96 EXPECT_EQ("main", test_action.decl_names[0]);
97 EXPECT_EQ("x", test_action.decl_names[1]);
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000098}
99
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000100TEST(ASTFrontendAction, IncrementalParsing) {
David Blaikieea4395e2017-01-06 19:49:01 +0000101 auto invocation = std::make_shared<CompilerInvocation>();
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000102 invocation->getPreprocessorOpts().addRemappedFile(
Rafael Espindolad87f8d72014-08-27 20:03:29 +0000103 "test.cc",
104 MemoryBuffer::getMemBuffer("int main() { float x; }").release());
Richard Smith40c0efa2017-04-26 18:57:40 +0000105 invocation->getFrontendOpts().Inputs.push_back(
106 FrontendInputFile("test.cc", InputKind::CXX));
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000107 invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
108 invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
109 CompilerInstance compiler;
David Blaikieea4395e2017-01-06 19:49:01 +0000110 compiler.setInvocation(std::move(invocation));
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000111 compiler.createDiagnostics();
112
113 TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true);
114 ASSERT_TRUE(compiler.ExecuteAction(test_action));
Alp Toker56b5cc92013-12-15 10:36:26 +0000115 ASSERT_EQ(2U, test_action.decl_names.size());
116 EXPECT_EQ("main", test_action.decl_names[0]);
117 EXPECT_EQ("x", test_action.decl_names[1]);
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000118}
119
Reid Kleckner89bd8d62014-10-22 17:50:19 +0000120TEST(ASTFrontendAction, LateTemplateIncrementalParsing) {
David Blaikieea4395e2017-01-06 19:49:01 +0000121 auto invocation = std::make_shared<CompilerInvocation>();
Reid Kleckner89bd8d62014-10-22 17:50:19 +0000122 invocation->getLangOpts()->CPlusPlus = true;
123 invocation->getLangOpts()->DelayedTemplateParsing = true;
124 invocation->getPreprocessorOpts().addRemappedFile(
125 "test.cc", MemoryBuffer::getMemBuffer(
126 "template<typename T> struct A { A(T); T data; };\n"
127 "template<typename T> struct B: public A<T> {\n"
128 " B();\n"
129 " B(B const& b): A<T>(b.data) {}\n"
130 "};\n"
131 "B<char> c() { return B<char>(); }\n").release());
Richard Smith40c0efa2017-04-26 18:57:40 +0000132 invocation->getFrontendOpts().Inputs.push_back(
133 FrontendInputFile("test.cc", InputKind::CXX));
Reid Kleckner89bd8d62014-10-22 17:50:19 +0000134 invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
135 invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
136 CompilerInstance compiler;
David Blaikieea4395e2017-01-06 19:49:01 +0000137 compiler.setInvocation(std::move(invocation));
Reid Kleckner89bd8d62014-10-22 17:50:19 +0000138 compiler.createDiagnostics();
139
140 TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true,
141 /*actOnEndOfTranslationUnit=*/true);
142 ASSERT_TRUE(compiler.ExecuteAction(test_action));
143 ASSERT_EQ(13U, test_action.decl_names.size());
144 EXPECT_EQ("A", test_action.decl_names[0]);
145 EXPECT_EQ("c", test_action.decl_names[12]);
146}
147
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000148struct TestPPCallbacks : public PPCallbacks {
149 TestPPCallbacks() : SeenEnd(false) {}
150
151 void EndOfMainFile() override { SeenEnd = true; }
152
153 bool SeenEnd;
154};
155
156class TestPPCallbacksFrontendAction : public PreprocessorFrontendAction {
Benjamin Kramera2406fa2014-09-10 09:35:49 +0000157 TestPPCallbacks *Callbacks;
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000158
159public:
Benjamin Kramera2406fa2014-09-10 09:35:49 +0000160 TestPPCallbacksFrontendAction(TestPPCallbacks *C)
161 : Callbacks(C), SeenEnd(false) {}
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000162
163 void ExecuteAction() override {
164 Preprocessor &PP = getCompilerInstance().getPreprocessor();
Benjamin Kramera2406fa2014-09-10 09:35:49 +0000165 PP.addPPCallbacks(std::unique_ptr<TestPPCallbacks>(Callbacks));
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000166 PP.EnterMainSourceFile();
167 }
168 void EndSourceFileAction() override { SeenEnd = Callbacks->SeenEnd; }
169
170 bool SeenEnd;
171};
172
173TEST(PreprocessorFrontendAction, EndSourceFile) {
David Blaikieea4395e2017-01-06 19:49:01 +0000174 auto Invocation = std::make_shared<CompilerInvocation>();
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000175 Invocation->getPreprocessorOpts().addRemappedFile(
Rafael Espindolad87f8d72014-08-27 20:03:29 +0000176 "test.cc",
177 MemoryBuffer::getMemBuffer("int main() { float x; }").release());
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000178 Invocation->getFrontendOpts().Inputs.push_back(
Richard Smith40c0efa2017-04-26 18:57:40 +0000179 FrontendInputFile("test.cc", InputKind::CXX));
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000180 Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
181 Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
182 CompilerInstance Compiler;
David Blaikieea4395e2017-01-06 19:49:01 +0000183 Compiler.setInvocation(std::move(Invocation));
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000184 Compiler.createDiagnostics();
185
Benjamin Kramera2406fa2014-09-10 09:35:49 +0000186 TestPPCallbacks *Callbacks = new TestPPCallbacks;
187 TestPPCallbacksFrontendAction TestAction(Callbacks);
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000188 ASSERT_FALSE(Callbacks->SeenEnd);
189 ASSERT_FALSE(TestAction.SeenEnd);
190 ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
191 // Check that EndOfMainFile was called before EndSourceFileAction.
192 ASSERT_TRUE(TestAction.SeenEnd);
193}
194
Benjamin Kramer7de99692016-11-16 18:15:26 +0000195class TypoExternalSemaSource : public ExternalSemaSource {
196 CompilerInstance &CI;
197
198public:
199 TypoExternalSemaSource(CompilerInstance &CI) : CI(CI) {}
200
201 TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
202 Scope *S, CXXScopeSpec *SS,
203 CorrectionCandidateCallback &CCC,
204 DeclContext *MemberContext, bool EnteringContext,
205 const ObjCObjectPointerType *OPT) override {
206 // Generate a fake typo correction with one attached note.
207 ASTContext &Ctx = CI.getASTContext();
208 TypoCorrection TC(DeclarationName(&Ctx.Idents.get("moo")));
209 unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(
210 DiagnosticsEngine::Note, "This is a note");
211 TC.addExtraDiagnostic(PartialDiagnostic(DiagID, Ctx.getDiagAllocator()));
212 return TC;
213 }
214};
215
216struct TypoDiagnosticConsumer : public DiagnosticConsumer {
217 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
218 const Diagnostic &Info) override {
219 // Capture errors and notes. There should be one of each.
220 if (DiagLevel == DiagnosticsEngine::Error) {
221 assert(Error.empty());
222 Info.FormatDiagnostic(Error);
223 } else {
224 assert(Note.empty());
225 Info.FormatDiagnostic(Note);
226 }
227 }
228 SmallString<32> Error;
229 SmallString<32> Note;
230};
231
232TEST(ASTFrontendAction, ExternalSemaSource) {
David Blaikieea4395e2017-01-06 19:49:01 +0000233 auto Invocation = std::make_shared<CompilerInvocation>();
Benjamin Kramer7de99692016-11-16 18:15:26 +0000234 Invocation->getLangOpts()->CPlusPlus = true;
235 Invocation->getPreprocessorOpts().addRemappedFile(
236 "test.cc", MemoryBuffer::getMemBuffer("void fooo();\n"
237 "int main() { foo(); }")
238 .release());
239 Invocation->getFrontendOpts().Inputs.push_back(
Richard Smith40c0efa2017-04-26 18:57:40 +0000240 FrontendInputFile("test.cc", InputKind::CXX));
Benjamin Kramer7de99692016-11-16 18:15:26 +0000241 Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
242 Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
243 CompilerInstance Compiler;
David Blaikieea4395e2017-01-06 19:49:01 +0000244 Compiler.setInvocation(std::move(Invocation));
Benjamin Kramer7de99692016-11-16 18:15:26 +0000245 auto *TDC = new TypoDiagnosticConsumer;
246 Compiler.createDiagnostics(TDC, /*ShouldOwnClient=*/true);
247 Compiler.setExternalSemaSource(new TypoExternalSemaSource(Compiler));
248
249 SyntaxOnlyAction TestAction;
250 ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
251 // There should be one error correcting to 'moo' and a note attached to it.
252 EXPECT_EQ("use of undeclared identifier 'foo'; did you mean 'moo'?",
253 TDC->Error.str().str());
254 EXPECT_EQ("This is a note", TDC->Note.str().str());
255}
256
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +0000257} // anonymous namespace