blob: 68181c705ce8c66974f84c53a291cdb70f6b38f4 [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
Alexander Kornienko34eb2072015-04-11 02:00:23 +000040 bool BeginSourceFileAction(CompilerInstance &ci,
41 StringRef filename) override {
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +000042 if (EnableIncrementalProcessing)
43 ci.getPreprocessor().enableIncrementalProcessing();
44
45 return ASTFrontendAction::BeginSourceFileAction(ci, filename);
46 }
47
Alexander Kornienko34eb2072015-04-11 02:00:23 +000048 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
49 StringRef InFile) override {
Reid Kleckner89bd8d62014-10-22 17:50:19 +000050 return llvm::make_unique<Visitor>(CI, ActOnEndOfTranslationUnit,
51 decl_names);
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000052 }
53
54private:
55 class Visitor : public ASTConsumer, public RecursiveASTVisitor<Visitor> {
56 public:
Reid Kleckner89bd8d62014-10-22 17:50:19 +000057 Visitor(CompilerInstance &CI, bool ActOnEndOfTranslationUnit,
58 std::vector<std::string> &decl_names) :
59 CI(CI), ActOnEndOfTranslationUnit(ActOnEndOfTranslationUnit),
60 decl_names_(decl_names) {}
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000061
Alexander Kornienko34eb2072015-04-11 02:00:23 +000062 void HandleTranslationUnit(ASTContext &context) override {
Reid Kleckner89bd8d62014-10-22 17:50:19 +000063 if (ActOnEndOfTranslationUnit) {
64 CI.getSema().ActOnEndOfTranslationUnit();
65 }
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000066 TraverseDecl(context.getTranslationUnitDecl());
67 }
68
69 virtual bool VisitNamedDecl(NamedDecl *Decl) {
70 decl_names_.push_back(Decl->getQualifiedNameAsString());
71 return true;
72 }
73
74 private:
Reid Kleckner89bd8d62014-10-22 17:50:19 +000075 CompilerInstance &CI;
76 bool ActOnEndOfTranslationUnit;
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000077 std::vector<std::string> &decl_names_;
78 };
79};
80
81TEST(ASTFrontendAction, Sanity) {
David Blaikieea4395e2017-01-06 19:49:01 +000082 auto invocation = std::make_shared<CompilerInvocation>();
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000083 invocation->getPreprocessorOpts().addRemappedFile(
Rafael Espindolad87f8d72014-08-27 20:03:29 +000084 "test.cc",
85 MemoryBuffer::getMemBuffer("int main() { float x; }").release());
Richard Smith40c0efa2017-04-26 18:57:40 +000086 invocation->getFrontendOpts().Inputs.push_back(
87 FrontendInputFile("test.cc", InputKind::CXX));
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000088 invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
89 invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
90 CompilerInstance compiler;
David Blaikieea4395e2017-01-06 19:49:01 +000091 compiler.setInvocation(std::move(invocation));
Sean Silvaf1b49e22013-01-20 01:58:28 +000092 compiler.createDiagnostics();
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000093
94 TestASTFrontendAction test_action;
95 ASSERT_TRUE(compiler.ExecuteAction(test_action));
Alp Toker56b5cc92013-12-15 10:36:26 +000096 ASSERT_EQ(2U, test_action.decl_names.size());
97 EXPECT_EQ("main", test_action.decl_names[0]);
98 EXPECT_EQ("x", test_action.decl_names[1]);
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +000099}
100
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000101TEST(ASTFrontendAction, IncrementalParsing) {
David Blaikieea4395e2017-01-06 19:49:01 +0000102 auto invocation = std::make_shared<CompilerInvocation>();
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000103 invocation->getPreprocessorOpts().addRemappedFile(
Rafael Espindolad87f8d72014-08-27 20:03:29 +0000104 "test.cc",
105 MemoryBuffer::getMemBuffer("int main() { float x; }").release());
Richard Smith40c0efa2017-04-26 18:57:40 +0000106 invocation->getFrontendOpts().Inputs.push_back(
107 FrontendInputFile("test.cc", InputKind::CXX));
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000108 invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
109 invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
110 CompilerInstance compiler;
David Blaikieea4395e2017-01-06 19:49:01 +0000111 compiler.setInvocation(std::move(invocation));
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000112 compiler.createDiagnostics();
113
114 TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true);
115 ASSERT_TRUE(compiler.ExecuteAction(test_action));
Alp Toker56b5cc92013-12-15 10:36:26 +0000116 ASSERT_EQ(2U, test_action.decl_names.size());
117 EXPECT_EQ("main", test_action.decl_names[0]);
118 EXPECT_EQ("x", test_action.decl_names[1]);
Argyrios Kyrtzidis336fcd92013-11-24 02:12:18 +0000119}
120
Reid Kleckner89bd8d62014-10-22 17:50:19 +0000121TEST(ASTFrontendAction, LateTemplateIncrementalParsing) {
David Blaikieea4395e2017-01-06 19:49:01 +0000122 auto invocation = std::make_shared<CompilerInvocation>();
Reid Kleckner89bd8d62014-10-22 17:50:19 +0000123 invocation->getLangOpts()->CPlusPlus = true;
124 invocation->getLangOpts()->DelayedTemplateParsing = true;
125 invocation->getPreprocessorOpts().addRemappedFile(
126 "test.cc", MemoryBuffer::getMemBuffer(
127 "template<typename T> struct A { A(T); T data; };\n"
128 "template<typename T> struct B: public A<T> {\n"
129 " B();\n"
130 " B(B const& b): A<T>(b.data) {}\n"
131 "};\n"
132 "B<char> c() { return B<char>(); }\n").release());
Richard Smith40c0efa2017-04-26 18:57:40 +0000133 invocation->getFrontendOpts().Inputs.push_back(
134 FrontendInputFile("test.cc", InputKind::CXX));
Reid Kleckner89bd8d62014-10-22 17:50:19 +0000135 invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
136 invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
137 CompilerInstance compiler;
David Blaikieea4395e2017-01-06 19:49:01 +0000138 compiler.setInvocation(std::move(invocation));
Reid Kleckner89bd8d62014-10-22 17:50:19 +0000139 compiler.createDiagnostics();
140
141 TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true,
142 /*actOnEndOfTranslationUnit=*/true);
143 ASSERT_TRUE(compiler.ExecuteAction(test_action));
144 ASSERT_EQ(13U, test_action.decl_names.size());
145 EXPECT_EQ("A", test_action.decl_names[0]);
146 EXPECT_EQ("c", test_action.decl_names[12]);
147}
148
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000149struct TestPPCallbacks : public PPCallbacks {
150 TestPPCallbacks() : SeenEnd(false) {}
151
152 void EndOfMainFile() override { SeenEnd = true; }
153
154 bool SeenEnd;
155};
156
157class TestPPCallbacksFrontendAction : public PreprocessorFrontendAction {
Benjamin Kramera2406fa2014-09-10 09:35:49 +0000158 TestPPCallbacks *Callbacks;
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000159
160public:
Benjamin Kramera2406fa2014-09-10 09:35:49 +0000161 TestPPCallbacksFrontendAction(TestPPCallbacks *C)
162 : Callbacks(C), SeenEnd(false) {}
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000163
164 void ExecuteAction() override {
165 Preprocessor &PP = getCompilerInstance().getPreprocessor();
Benjamin Kramera2406fa2014-09-10 09:35:49 +0000166 PP.addPPCallbacks(std::unique_ptr<TestPPCallbacks>(Callbacks));
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000167 PP.EnterMainSourceFile();
168 }
169 void EndSourceFileAction() override { SeenEnd = Callbacks->SeenEnd; }
170
171 bool SeenEnd;
172};
173
174TEST(PreprocessorFrontendAction, EndSourceFile) {
David Blaikieea4395e2017-01-06 19:49:01 +0000175 auto Invocation = std::make_shared<CompilerInvocation>();
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000176 Invocation->getPreprocessorOpts().addRemappedFile(
Rafael Espindolad87f8d72014-08-27 20:03:29 +0000177 "test.cc",
178 MemoryBuffer::getMemBuffer("int main() { float x; }").release());
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000179 Invocation->getFrontendOpts().Inputs.push_back(
Richard Smith40c0efa2017-04-26 18:57:40 +0000180 FrontendInputFile("test.cc", InputKind::CXX));
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000181 Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
182 Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
183 CompilerInstance Compiler;
David Blaikieea4395e2017-01-06 19:49:01 +0000184 Compiler.setInvocation(std::move(Invocation));
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000185 Compiler.createDiagnostics();
186
Benjamin Kramera2406fa2014-09-10 09:35:49 +0000187 TestPPCallbacks *Callbacks = new TestPPCallbacks;
188 TestPPCallbacksFrontendAction TestAction(Callbacks);
Benjamin Kramer88d99e42014-08-07 20:51:16 +0000189 ASSERT_FALSE(Callbacks->SeenEnd);
190 ASSERT_FALSE(TestAction.SeenEnd);
191 ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
192 // Check that EndOfMainFile was called before EndSourceFileAction.
193 ASSERT_TRUE(TestAction.SeenEnd);
194}
195
Benjamin Kramer7de99692016-11-16 18:15:26 +0000196class TypoExternalSemaSource : public ExternalSemaSource {
197 CompilerInstance &CI;
198
199public:
200 TypoExternalSemaSource(CompilerInstance &CI) : CI(CI) {}
201
202 TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
203 Scope *S, CXXScopeSpec *SS,
204 CorrectionCandidateCallback &CCC,
205 DeclContext *MemberContext, bool EnteringContext,
206 const ObjCObjectPointerType *OPT) override {
207 // Generate a fake typo correction with one attached note.
208 ASTContext &Ctx = CI.getASTContext();
209 TypoCorrection TC(DeclarationName(&Ctx.Idents.get("moo")));
210 unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(
211 DiagnosticsEngine::Note, "This is a note");
212 TC.addExtraDiagnostic(PartialDiagnostic(DiagID, Ctx.getDiagAllocator()));
213 return TC;
214 }
215};
216
217struct TypoDiagnosticConsumer : public DiagnosticConsumer {
218 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
219 const Diagnostic &Info) override {
220 // Capture errors and notes. There should be one of each.
221 if (DiagLevel == DiagnosticsEngine::Error) {
222 assert(Error.empty());
223 Info.FormatDiagnostic(Error);
224 } else {
225 assert(Note.empty());
226 Info.FormatDiagnostic(Note);
227 }
228 }
229 SmallString<32> Error;
230 SmallString<32> Note;
231};
232
233TEST(ASTFrontendAction, ExternalSemaSource) {
David Blaikieea4395e2017-01-06 19:49:01 +0000234 auto Invocation = std::make_shared<CompilerInvocation>();
Benjamin Kramer7de99692016-11-16 18:15:26 +0000235 Invocation->getLangOpts()->CPlusPlus = true;
236 Invocation->getPreprocessorOpts().addRemappedFile(
237 "test.cc", MemoryBuffer::getMemBuffer("void fooo();\n"
238 "int main() { foo(); }")
239 .release());
240 Invocation->getFrontendOpts().Inputs.push_back(
Richard Smith40c0efa2017-04-26 18:57:40 +0000241 FrontendInputFile("test.cc", InputKind::CXX));
Benjamin Kramer7de99692016-11-16 18:15:26 +0000242 Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
243 Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
244 CompilerInstance Compiler;
David Blaikieea4395e2017-01-06 19:49:01 +0000245 Compiler.setInvocation(std::move(Invocation));
Benjamin Kramer7de99692016-11-16 18:15:26 +0000246 auto *TDC = new TypoDiagnosticConsumer;
247 Compiler.createDiagnostics(TDC, /*ShouldOwnClient=*/true);
248 Compiler.setExternalSemaSource(new TypoExternalSemaSource(Compiler));
249
250 SyntaxOnlyAction TestAction;
251 ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
252 // There should be one error correcting to 'moo' and a note attached to it.
253 EXPECT_EQ("use of undeclared identifier 'foo'; did you mean 'moo'?",
254 TDC->Error.str().str());
255 EXPECT_EQ("This is a note", TDC->Note.str().str());
256}
257
Jeffrey Yasskinc7da9932011-02-03 04:51:52 +0000258} // anonymous namespace