blob: d2cdd633fa148e2ca5908701c686a811f3561290 [file] [log] [blame]
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +00001//=== unittests/Sema/ExternalSemaSourceTest.cpp - ExternalSemaSource 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/AST/ASTConsumer.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/Frontend/CompilerInstance.h"
13#include "clang/Lex/Preprocessor.h"
14#include "clang/Parse/ParseAST.h"
15#include "clang/Sema/ExternalSemaSource.h"
16#include "clang/Sema/Sema.h"
17#include "clang/Sema/SemaDiagnostic.h"
18#include "clang/Sema/TypoCorrection.h"
19#include "clang/Tooling/Tooling.h"
20#include "gtest/gtest.h"
21
22using namespace clang;
23using namespace clang::tooling;
24
25namespace {
26
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +000027// \brief Counts the number of times MaybeDiagnoseMissingCompleteType
28// is called. Returns the result it was provided on creation.
29class CompleteTypeDiagnoser : public clang::ExternalSemaSource {
30public:
31 CompleteTypeDiagnoser(bool MockResult) : CallCount(0), Result(MockResult) {}
32
Alexander Kornienko34eb2072015-04-11 02:00:23 +000033 bool MaybeDiagnoseMissingCompleteType(SourceLocation L, QualType T) override {
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +000034 ++CallCount;
35 return Result;
36 }
37
38 int CallCount;
39 bool Result;
40};
41
Benjamin Kramerb8727332016-05-19 10:46:10 +000042/// Counts the number of typo-correcting diagnostics correcting from one name to
43/// another while still passing all diagnostics along a chain of consumers.
44class DiagnosticWatcher : public clang::DiagnosticConsumer {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000045 DiagnosticConsumer *Chained;
Benjamin Kramerb8727332016-05-19 10:46:10 +000046 std::string FromName;
47 std::string ToName;
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000048
49public:
Benjamin Kramerb8727332016-05-19 10:46:10 +000050 DiagnosticWatcher(StringRef From, StringRef To)
51 : Chained(nullptr), FromName(From), ToName("'"), SeenCount(0) {
52 ToName.append(To);
53 ToName.append("'");
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000054 }
55
Alexander Kornienko34eb2072015-04-11 02:00:23 +000056 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
57 const Diagnostic &Info) override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000058 if (Chained)
59 Chained->HandleDiagnostic(DiagLevel, Info);
60 if (Info.getID() - 1 == diag::err_using_directive_member_suggest) {
61 const IdentifierInfo *Ident = Info.getArgIdentifier(0);
62 const std::string &CorrectedQuotedStr = Info.getArgStdStr(1);
Benjamin Kramerb8727332016-05-19 10:46:10 +000063 if (Ident->getName() == FromName && CorrectedQuotedStr == ToName)
64 ++SeenCount;
65 } else if (Info.getID() == diag::err_no_member_suggest) {
66 auto Ident = DeclarationName::getFromOpaqueInteger(Info.getRawArg(0));
67 const std::string &CorrectedQuotedStr = Info.getArgStdStr(3);
68 if (Ident.getAsString() == FromName && CorrectedQuotedStr == ToName)
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000069 ++SeenCount;
70 }
71 }
72
Alexander Kornienko34eb2072015-04-11 02:00:23 +000073 void clear() override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000074 DiagnosticConsumer::clear();
75 if (Chained)
76 Chained->clear();
77 }
78
Alexander Kornienko34eb2072015-04-11 02:00:23 +000079 bool IncludeInDiagnosticCounts() const override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000080 if (Chained)
81 return Chained->IncludeInDiagnosticCounts();
82 return false;
83 }
84
Benjamin Kramerb8727332016-05-19 10:46:10 +000085 DiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000086 Chained = ToChain;
87 return this;
88 }
89
90 int SeenCount;
91};
92
93// \brief Always corrects a typo matching CorrectFrom with a new namespace
94// with the name CorrectTo.
95class NamespaceTypoProvider : public clang::ExternalSemaSource {
96 std::string CorrectFrom;
97 std::string CorrectTo;
98 Sema *CurrentSema;
99
100public:
101 NamespaceTypoProvider(StringRef From, StringRef To)
Craig Topper416fa342014-06-08 08:38:12 +0000102 : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000103
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000104 void InitializeSema(Sema &S) override { CurrentSema = &S; }
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000105
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000106 void ForgetSema() override { CurrentSema = nullptr; }
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000107
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000108 TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
109 Scope *S, CXXScopeSpec *SS,
110 CorrectionCandidateCallback &CCC,
111 DeclContext *MemberContext, bool EnteringContext,
112 const ObjCObjectPointerType *OPT) override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000113 ++CallCount;
114 if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
Craig Topper416fa342014-06-08 08:38:12 +0000115 DeclContext *DestContext = nullptr;
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000116 ASTContext &Context = CurrentSema->getASTContext();
Craig Topper416fa342014-06-08 08:38:12 +0000117 if (SS)
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000118 DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
Craig Topper416fa342014-06-08 08:38:12 +0000119 if (!DestContext)
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000120 DestContext = Context.getTranslationUnitDecl();
121 IdentifierInfo *ToIdent =
122 CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
123 NamespaceDecl *NewNamespace =
124 NamespaceDecl::Create(Context, DestContext, false, Typo.getBeginLoc(),
Craig Topper416fa342014-06-08 08:38:12 +0000125 Typo.getLoc(), ToIdent, nullptr);
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000126 DestContext->addDecl(NewNamespace);
127 TypoCorrection Correction(ToIdent);
128 Correction.addCorrectionDecl(NewNamespace);
129 return Correction;
130 }
131 return TypoCorrection();
132 }
133
134 int CallCount;
135};
136
Benjamin Kramerb8727332016-05-19 10:46:10 +0000137class FunctionTypoProvider : public clang::ExternalSemaSource {
138 std::string CorrectFrom;
139 std::string CorrectTo;
140 Sema *CurrentSema;
141
142public:
143 FunctionTypoProvider(StringRef From, StringRef To)
144 : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}
145
146 void InitializeSema(Sema &S) override { CurrentSema = &S; }
147
148 void ForgetSema() override { CurrentSema = nullptr; }
149
150 TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
151 Scope *S, CXXScopeSpec *SS,
152 CorrectionCandidateCallback &CCC,
153 DeclContext *MemberContext, bool EnteringContext,
154 const ObjCObjectPointerType *OPT) override {
155 ++CallCount;
156 if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
157 DeclContext *DestContext = nullptr;
158 ASTContext &Context = CurrentSema->getASTContext();
159 if (SS)
160 DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
161 if (!DestContext)
162 DestContext = Context.getTranslationUnitDecl();
163 IdentifierInfo *ToIdent =
164 CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
165 auto *NewFunction = FunctionDecl::Create(
166 Context, DestContext, SourceLocation(), SourceLocation(), ToIdent,
167 Context.getFunctionType(Context.VoidTy, {}, {}), nullptr, SC_Static);
168 DestContext->addDecl(NewFunction);
169 TypoCorrection Correction(ToIdent);
170 Correction.addCorrectionDecl(NewFunction);
171 return Correction;
172 }
173 return TypoCorrection();
174 }
175
176 int CallCount;
177};
178
179// \brief Chains together a vector of DiagnosticWatchers and
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000180// adds a vector of ExternalSemaSources to the CompilerInstance before
181// performing semantic analysis.
182class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
Benjamin Kramerb8727332016-05-19 10:46:10 +0000183 std::vector<DiagnosticWatcher *> Watchers;
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000184 std::vector<clang::ExternalSemaSource *> Sources;
Ahmed Charlesb8984322014-03-07 20:03:18 +0000185 std::unique_ptr<DiagnosticConsumer> OwnedClient;
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000186
187protected:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000188 std::unique_ptr<clang::ASTConsumer>
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000189 CreateASTConsumer(clang::CompilerInstance &Compiler,
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000190 llvm::StringRef /* dummy */) override {
David Blaikie6beb6aa2014-08-10 19:56:51 +0000191 return llvm::make_unique<clang::ASTConsumer>();
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000192 }
193
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000194 void ExecuteAction() override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000195 CompilerInstance &CI = getCompilerInstance();
196 ASSERT_FALSE(CI.hasSema());
Craig Topper416fa342014-06-08 08:38:12 +0000197 CI.createSema(getTranslationUnitKind(), nullptr);
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000198 ASSERT_TRUE(CI.hasDiagnostics());
199 DiagnosticsEngine &Diagnostics = CI.getDiagnostics();
200 DiagnosticConsumer *Client = Diagnostics.getClient();
201 if (Diagnostics.ownsClient())
Alexander Kornienko41c247a2014-11-17 23:46:02 +0000202 OwnedClient = Diagnostics.takeClient();
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000203 for (size_t I = 0, E = Watchers.size(); I < E; ++I)
204 Client = Watchers[I]->Chain(Client);
205 Diagnostics.setClient(Client, false);
206 for (size_t I = 0, E = Sources.size(); I < E; ++I) {
207 Sources[I]->InitializeSema(CI.getSema());
208 CI.getSema().addExternalSource(Sources[I]);
209 }
210 ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
211 CI.getFrontendOpts().SkipFunctionBodies);
212 }
213
214public:
215 void PushSource(clang::ExternalSemaSource *Source) {
216 Sources.push_back(Source);
217 }
218
Benjamin Kramerb8727332016-05-19 10:46:10 +0000219 void PushWatcher(DiagnosticWatcher *Watcher) { Watchers.push_back(Watcher); }
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000220};
221
Benjamin Kramerb8727332016-05-19 10:46:10 +0000222// Make sure that the DiagnosticWatcher is not miscounting.
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000223TEST(ExternalSemaSource, SanityCheck) {
Roman Lebedev497fd982018-02-27 15:54:55 +0000224 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
225 new ExternalSemaSourceInstaller);
Benjamin Kramerb8727332016-05-19 10:46:10 +0000226 DiagnosticWatcher Watcher("AAB", "BBB");
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000227 Installer->PushWatcher(&Watcher);
228 std::vector<std::string> Args(1, "-std=c++11");
229 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Roman Lebedev497fd982018-02-27 15:54:55 +0000230 Installer.release(), "namespace AAA { } using namespace AAB;", Args));
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000231 ASSERT_EQ(0, Watcher.SeenCount);
232}
233
234// Check that when we add a NamespaceTypeProvider, we use that suggestion
235// instead of the usual suggestion we would use above.
236TEST(ExternalSemaSource, ExternalTypoCorrectionPrioritized) {
Roman Lebedev497fd982018-02-27 15:54:55 +0000237 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
238 new ExternalSemaSourceInstaller);
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000239 NamespaceTypoProvider Provider("AAB", "BBB");
Benjamin Kramerb8727332016-05-19 10:46:10 +0000240 DiagnosticWatcher Watcher("AAB", "BBB");
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000241 Installer->PushSource(&Provider);
242 Installer->PushWatcher(&Watcher);
243 std::vector<std::string> Args(1, "-std=c++11");
244 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Roman Lebedev497fd982018-02-27 15:54:55 +0000245 Installer.release(), "namespace AAA { } using namespace AAB;", Args));
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000246 ASSERT_LE(0, Provider.CallCount);
247 ASSERT_EQ(1, Watcher.SeenCount);
248}
249
250// Check that we use the first successful TypoCorrection returned from an
251// ExternalSemaSource.
252TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) {
Roman Lebedev497fd982018-02-27 15:54:55 +0000253 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
254 new ExternalSemaSourceInstaller);
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000255 NamespaceTypoProvider First("XXX", "BBB");
256 NamespaceTypoProvider Second("AAB", "CCC");
257 NamespaceTypoProvider Third("AAB", "DDD");
Benjamin Kramerb8727332016-05-19 10:46:10 +0000258 DiagnosticWatcher Watcher("AAB", "CCC");
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000259 Installer->PushSource(&First);
260 Installer->PushSource(&Second);
261 Installer->PushSource(&Third);
262 Installer->PushWatcher(&Watcher);
263 std::vector<std::string> Args(1, "-std=c++11");
264 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Roman Lebedev497fd982018-02-27 15:54:55 +0000265 Installer.release(), "namespace AAA { } using namespace AAB;", Args));
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000266 ASSERT_LE(1, First.CallCount);
267 ASSERT_LE(1, Second.CallCount);
268 ASSERT_EQ(0, Third.CallCount);
269 ASSERT_EQ(1, Watcher.SeenCount);
270}
271
Benjamin Kramerb8727332016-05-19 10:46:10 +0000272TEST(ExternalSemaSource, ExternalDelayedTypoCorrection) {
Roman Lebedev497fd982018-02-27 15:54:55 +0000273 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
274 new ExternalSemaSourceInstaller);
Benjamin Kramerb8727332016-05-19 10:46:10 +0000275 FunctionTypoProvider Provider("aaa", "bbb");
276 DiagnosticWatcher Watcher("aaa", "bbb");
277 Installer->PushSource(&Provider);
278 Installer->PushWatcher(&Watcher);
279 std::vector<std::string> Args(1, "-std=c++11");
280 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Roman Lebedev497fd982018-02-27 15:54:55 +0000281 Installer.release(), "namespace AAA { } void foo() { AAA::aaa(); }",
Benjamin Kramerb8727332016-05-19 10:46:10 +0000282 Args));
283 ASSERT_LE(0, Provider.CallCount);
284 ASSERT_EQ(1, Watcher.SeenCount);
285}
286
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000287// We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise
288// solve the problem.
289TEST(ExternalSemaSource, TryOtherTacticsBeforeDiagnosing) {
Roman Lebedev497fd982018-02-27 15:54:55 +0000290 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
291 new ExternalSemaSourceInstaller);
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000292 CompleteTypeDiagnoser Diagnoser(false);
293 Installer->PushSource(&Diagnoser);
294 std::vector<std::string> Args(1, "-std=c++11");
295 // This code hits the class template specialization/class member of a class
296 // template specialization checks in Sema::RequireCompleteTypeImpl.
297 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Roman Lebedev497fd982018-02-27 15:54:55 +0000298 Installer.release(),
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000299 "template <typename T> struct S { class C { }; }; S<char>::C SCInst;",
300 Args));
301 ASSERT_EQ(0, Diagnoser.CallCount);
302}
303
304// The first ExternalSemaSource where MaybeDiagnoseMissingCompleteType returns
305// true should be the last one called.
306TEST(ExternalSemaSource, FirstDiagnoserTaken) {
Roman Lebedev497fd982018-02-27 15:54:55 +0000307 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
308 new ExternalSemaSourceInstaller);
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000309 CompleteTypeDiagnoser First(false);
310 CompleteTypeDiagnoser Second(true);
311 CompleteTypeDiagnoser Third(true);
312 Installer->PushSource(&First);
313 Installer->PushSource(&Second);
314 Installer->PushSource(&Third);
315 std::vector<std::string> Args(1, "-std=c++11");
316 ASSERT_FALSE(clang::tooling::runToolOnCodeWithArgs(
Roman Lebedev497fd982018-02-27 15:54:55 +0000317 Installer.release(), "class Incomplete; Incomplete IncompleteInstance;",
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000318 Args));
319 ASSERT_EQ(1, First.CallCount);
320 ASSERT_EQ(1, Second.CallCount);
321 ASSERT_EQ(0, Third.CallCount);
322}
323
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000324} // anonymous namespace