blob: 703e97b4ac957c7f85cdcd1087f5809924dd6983 [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
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000042// \brief Counts the number of err_using_directive_member_suggest diagnostics
43// correcting from one namespace to another while still passing all diagnostics
44// along a chain of consumers.
45class NamespaceDiagnosticWatcher : public clang::DiagnosticConsumer {
46 DiagnosticConsumer *Chained;
47 std::string FromNS;
48 std::string ToNS;
49
50public:
51 NamespaceDiagnosticWatcher(StringRef From, StringRef To)
Craig Topper416fa342014-06-08 08:38:12 +000052 : Chained(nullptr), FromNS(From), ToNS("'"), SeenCount(0) {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000053 ToNS.append(To);
54 ToNS.append("'");
55 }
56
Alexander Kornienko34eb2072015-04-11 02:00:23 +000057 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
58 const Diagnostic &Info) override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000059 if (Chained)
60 Chained->HandleDiagnostic(DiagLevel, Info);
61 if (Info.getID() - 1 == diag::err_using_directive_member_suggest) {
62 const IdentifierInfo *Ident = Info.getArgIdentifier(0);
63 const std::string &CorrectedQuotedStr = Info.getArgStdStr(1);
64 if (Ident->getName() == FromNS && CorrectedQuotedStr == ToNS)
65 ++SeenCount;
66 }
67 }
68
Alexander Kornienko34eb2072015-04-11 02:00:23 +000069 void clear() override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000070 DiagnosticConsumer::clear();
71 if (Chained)
72 Chained->clear();
73 }
74
Alexander Kornienko34eb2072015-04-11 02:00:23 +000075 bool IncludeInDiagnosticCounts() const override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000076 if (Chained)
77 return Chained->IncludeInDiagnosticCounts();
78 return false;
79 }
80
81 NamespaceDiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) {
82 Chained = ToChain;
83 return this;
84 }
85
86 int SeenCount;
87};
88
89// \brief Always corrects a typo matching CorrectFrom with a new namespace
90// with the name CorrectTo.
91class NamespaceTypoProvider : public clang::ExternalSemaSource {
92 std::string CorrectFrom;
93 std::string CorrectTo;
94 Sema *CurrentSema;
95
96public:
97 NamespaceTypoProvider(StringRef From, StringRef To)
Craig Topper416fa342014-06-08 08:38:12 +000098 : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +000099
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000100 void InitializeSema(Sema &S) override { CurrentSema = &S; }
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000101
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000102 void ForgetSema() override { CurrentSema = nullptr; }
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000103
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000104 TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
105 Scope *S, CXXScopeSpec *SS,
106 CorrectionCandidateCallback &CCC,
107 DeclContext *MemberContext, bool EnteringContext,
108 const ObjCObjectPointerType *OPT) override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000109 ++CallCount;
110 if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
Craig Topper416fa342014-06-08 08:38:12 +0000111 DeclContext *DestContext = nullptr;
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000112 ASTContext &Context = CurrentSema->getASTContext();
Craig Topper416fa342014-06-08 08:38:12 +0000113 if (SS)
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000114 DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
Craig Topper416fa342014-06-08 08:38:12 +0000115 if (!DestContext)
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000116 DestContext = Context.getTranslationUnitDecl();
117 IdentifierInfo *ToIdent =
118 CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
119 NamespaceDecl *NewNamespace =
120 NamespaceDecl::Create(Context, DestContext, false, Typo.getBeginLoc(),
Craig Topper416fa342014-06-08 08:38:12 +0000121 Typo.getLoc(), ToIdent, nullptr);
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000122 DestContext->addDecl(NewNamespace);
123 TypoCorrection Correction(ToIdent);
124 Correction.addCorrectionDecl(NewNamespace);
125 return Correction;
126 }
127 return TypoCorrection();
128 }
129
130 int CallCount;
131};
132
133// \brief Chains together a vector of NamespaceDiagnosticWatchers and
134// adds a vector of ExternalSemaSources to the CompilerInstance before
135// performing semantic analysis.
136class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
137 std::vector<NamespaceDiagnosticWatcher *> Watchers;
138 std::vector<clang::ExternalSemaSource *> Sources;
Ahmed Charlesb8984322014-03-07 20:03:18 +0000139 std::unique_ptr<DiagnosticConsumer> OwnedClient;
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000140
141protected:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000142 std::unique_ptr<clang::ASTConsumer>
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000143 CreateASTConsumer(clang::CompilerInstance &Compiler,
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000144 llvm::StringRef /* dummy */) override {
David Blaikie6beb6aa2014-08-10 19:56:51 +0000145 return llvm::make_unique<clang::ASTConsumer>();
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000146 }
147
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000148 void ExecuteAction() override {
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000149 CompilerInstance &CI = getCompilerInstance();
150 ASSERT_FALSE(CI.hasSema());
Craig Topper416fa342014-06-08 08:38:12 +0000151 CI.createSema(getTranslationUnitKind(), nullptr);
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000152 ASSERT_TRUE(CI.hasDiagnostics());
153 DiagnosticsEngine &Diagnostics = CI.getDiagnostics();
154 DiagnosticConsumer *Client = Diagnostics.getClient();
155 if (Diagnostics.ownsClient())
Alexander Kornienko41c247a2014-11-17 23:46:02 +0000156 OwnedClient = Diagnostics.takeClient();
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000157 for (size_t I = 0, E = Watchers.size(); I < E; ++I)
158 Client = Watchers[I]->Chain(Client);
159 Diagnostics.setClient(Client, false);
160 for (size_t I = 0, E = Sources.size(); I < E; ++I) {
161 Sources[I]->InitializeSema(CI.getSema());
162 CI.getSema().addExternalSource(Sources[I]);
163 }
164 ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
165 CI.getFrontendOpts().SkipFunctionBodies);
166 }
167
168public:
169 void PushSource(clang::ExternalSemaSource *Source) {
170 Sources.push_back(Source);
171 }
172
173 void PushWatcher(NamespaceDiagnosticWatcher *Watcher) {
174 Watchers.push_back(Watcher);
175 }
176};
177
178// Make sure that the NamespaceDiagnosticWatcher is not miscounting.
179TEST(ExternalSemaSource, SanityCheck) {
Ahmed Charlesb8984322014-03-07 20:03:18 +0000180 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000181 new ExternalSemaSourceInstaller);
182 NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
183 Installer->PushWatcher(&Watcher);
184 std::vector<std::string> Args(1, "-std=c++11");
185 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Ahmed Charles9a16beb2014-03-07 19:33:25 +0000186 Installer.release(), "namespace AAA { } using namespace AAB;", Args));
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000187 ASSERT_EQ(0, Watcher.SeenCount);
188}
189
190// Check that when we add a NamespaceTypeProvider, we use that suggestion
191// instead of the usual suggestion we would use above.
192TEST(ExternalSemaSource, ExternalTypoCorrectionPrioritized) {
Ahmed Charlesb8984322014-03-07 20:03:18 +0000193 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000194 new ExternalSemaSourceInstaller);
195 NamespaceTypoProvider Provider("AAB", "BBB");
196 NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
197 Installer->PushSource(&Provider);
198 Installer->PushWatcher(&Watcher);
199 std::vector<std::string> Args(1, "-std=c++11");
200 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Ahmed Charles9a16beb2014-03-07 19:33:25 +0000201 Installer.release(), "namespace AAA { } using namespace AAB;", Args));
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000202 ASSERT_LE(0, Provider.CallCount);
203 ASSERT_EQ(1, Watcher.SeenCount);
204}
205
206// Check that we use the first successful TypoCorrection returned from an
207// ExternalSemaSource.
208TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) {
Ahmed Charlesb8984322014-03-07 20:03:18 +0000209 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000210 new ExternalSemaSourceInstaller);
211 NamespaceTypoProvider First("XXX", "BBB");
212 NamespaceTypoProvider Second("AAB", "CCC");
213 NamespaceTypoProvider Third("AAB", "DDD");
214 NamespaceDiagnosticWatcher Watcher("AAB", "CCC");
215 Installer->PushSource(&First);
216 Installer->PushSource(&Second);
217 Installer->PushSource(&Third);
218 Installer->PushWatcher(&Watcher);
219 std::vector<std::string> Args(1, "-std=c++11");
220 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Ahmed Charles9a16beb2014-03-07 19:33:25 +0000221 Installer.release(), "namespace AAA { } using namespace AAB;", Args));
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000222 ASSERT_LE(1, First.CallCount);
223 ASSERT_LE(1, Second.CallCount);
224 ASSERT_EQ(0, Third.CallCount);
225 ASSERT_EQ(1, Watcher.SeenCount);
226}
227
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000228// We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise
229// solve the problem.
230TEST(ExternalSemaSource, TryOtherTacticsBeforeDiagnosing) {
Ahmed Charlesb8984322014-03-07 20:03:18 +0000231 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000232 new ExternalSemaSourceInstaller);
233 CompleteTypeDiagnoser Diagnoser(false);
234 Installer->PushSource(&Diagnoser);
235 std::vector<std::string> Args(1, "-std=c++11");
236 // This code hits the class template specialization/class member of a class
237 // template specialization checks in Sema::RequireCompleteTypeImpl.
238 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Ahmed Charles9a16beb2014-03-07 19:33:25 +0000239 Installer.release(),
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000240 "template <typename T> struct S { class C { }; }; S<char>::C SCInst;",
241 Args));
242 ASSERT_EQ(0, Diagnoser.CallCount);
243}
244
245// The first ExternalSemaSource where MaybeDiagnoseMissingCompleteType returns
246// true should be the last one called.
247TEST(ExternalSemaSource, FirstDiagnoserTaken) {
Ahmed Charlesb8984322014-03-07 20:03:18 +0000248 std::unique_ptr<ExternalSemaSourceInstaller> Installer(
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000249 new ExternalSemaSourceInstaller);
250 CompleteTypeDiagnoser First(false);
251 CompleteTypeDiagnoser Second(true);
252 CompleteTypeDiagnoser Third(true);
253 Installer->PushSource(&First);
254 Installer->PushSource(&Second);
255 Installer->PushSource(&Third);
256 std::vector<std::string> Args(1, "-std=c++11");
257 ASSERT_FALSE(clang::tooling::runToolOnCodeWithArgs(
Ahmed Charles9a16beb2014-03-07 19:33:25 +0000258 Installer.release(), "class Incomplete; Incomplete IncompleteInstance;",
Kaelyn Uhrain2c351bb2013-08-12 22:11:14 +0000259 Args));
260 ASSERT_EQ(1, First.CallCount);
261 ASSERT_EQ(1, Second.CallCount);
262 ASSERT_EQ(0, Third.CallCount);
263}
264
Kaelyn Uhrain4ebf5762013-08-12 19:57:06 +0000265} // anonymous namespace