blob: e27d0cd1e397bf1a1bae5c1d82ca9d13920ec157 [file] [log] [blame]
Kaelyn Uhrain4432bf02013-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 Uhrain5d937b32013-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
33 virtual bool MaybeDiagnoseMissingCompleteType(SourceLocation L, QualType T) {
34 ++CallCount;
35 return Result;
36 }
37
38 int CallCount;
39 bool Result;
40};
41
Kaelyn Uhrain4432bf02013-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)
52 : Chained(NULL), FromNS(From), ToNS("'"), SeenCount(0) {
53 ToNS.append(To);
54 ToNS.append("'");
55 }
56
57 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
58 const Diagnostic &Info) {
59 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
69 virtual void clear() {
70 DiagnosticConsumer::clear();
71 if (Chained)
72 Chained->clear();
73 }
74
75 virtual bool IncludeInDiagnosticCounts() const {
76 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)
98 : CorrectFrom(From), CorrectTo(To), CurrentSema(NULL), CallCount(0) {}
99
100 virtual void InitializeSema(Sema &S) { CurrentSema = &S; }
101
102 virtual void ForgetSema() { CurrentSema = NULL; }
103
104 virtual TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
105 int LookupKind, Scope *S, CXXScopeSpec *SS,
106 CorrectionCandidateCallback &CCC,
107 DeclContext *MemberContext,
108 bool EnteringContext,
109 const ObjCObjectPointerType *OPT) {
110 ++CallCount;
111 if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
112 DeclContext *DestContext = NULL;
113 ASTContext &Context = CurrentSema->getASTContext();
114 if (SS != NULL)
115 DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
116 if (DestContext == NULL)
117 DestContext = Context.getTranslationUnitDecl();
118 IdentifierInfo *ToIdent =
119 CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
120 NamespaceDecl *NewNamespace =
121 NamespaceDecl::Create(Context, DestContext, false, Typo.getBeginLoc(),
122 Typo.getLoc(), ToIdent, NULL);
123 DestContext->addDecl(NewNamespace);
124 TypoCorrection Correction(ToIdent);
125 Correction.addCorrectionDecl(NewNamespace);
126 return Correction;
127 }
128 return TypoCorrection();
129 }
130
131 int CallCount;
132};
133
134// \brief Chains together a vector of NamespaceDiagnosticWatchers and
135// adds a vector of ExternalSemaSources to the CompilerInstance before
136// performing semantic analysis.
137class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
138 std::vector<NamespaceDiagnosticWatcher *> Watchers;
139 std::vector<clang::ExternalSemaSource *> Sources;
140 llvm::OwningPtr<DiagnosticConsumer> OwnedClient;
141
142protected:
143 virtual clang::ASTConsumer *
144 CreateASTConsumer(clang::CompilerInstance &Compiler,
145 llvm::StringRef /* dummy */) {
146 return new clang::ASTConsumer();
147 }
148
149 virtual void ExecuteAction() {
150 CompilerInstance &CI = getCompilerInstance();
151 ASSERT_FALSE(CI.hasSema());
152 CI.createSema(getTranslationUnitKind(), NULL);
153 ASSERT_TRUE(CI.hasDiagnostics());
154 DiagnosticsEngine &Diagnostics = CI.getDiagnostics();
155 DiagnosticConsumer *Client = Diagnostics.getClient();
156 if (Diagnostics.ownsClient())
157 OwnedClient.reset(Diagnostics.takeClient());
158 for (size_t I = 0, E = Watchers.size(); I < E; ++I)
159 Client = Watchers[I]->Chain(Client);
160 Diagnostics.setClient(Client, false);
161 for (size_t I = 0, E = Sources.size(); I < E; ++I) {
162 Sources[I]->InitializeSema(CI.getSema());
163 CI.getSema().addExternalSource(Sources[I]);
164 }
165 ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
166 CI.getFrontendOpts().SkipFunctionBodies);
167 }
168
169public:
170 void PushSource(clang::ExternalSemaSource *Source) {
171 Sources.push_back(Source);
172 }
173
174 void PushWatcher(NamespaceDiagnosticWatcher *Watcher) {
175 Watchers.push_back(Watcher);
176 }
177};
178
179// Make sure that the NamespaceDiagnosticWatcher is not miscounting.
180TEST(ExternalSemaSource, SanityCheck) {
181 llvm::OwningPtr<ExternalSemaSourceInstaller> Installer(
182 new ExternalSemaSourceInstaller);
183 NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
184 Installer->PushWatcher(&Watcher);
185 std::vector<std::string> Args(1, "-std=c++11");
186 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
187 Installer.take(), "namespace AAA { } using namespace AAB;", Args));
188 ASSERT_EQ(0, Watcher.SeenCount);
189}
190
191// Check that when we add a NamespaceTypeProvider, we use that suggestion
192// instead of the usual suggestion we would use above.
193TEST(ExternalSemaSource, ExternalTypoCorrectionPrioritized) {
194 llvm::OwningPtr<ExternalSemaSourceInstaller> Installer(
195 new ExternalSemaSourceInstaller);
196 NamespaceTypoProvider Provider("AAB", "BBB");
197 NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
198 Installer->PushSource(&Provider);
199 Installer->PushWatcher(&Watcher);
200 std::vector<std::string> Args(1, "-std=c++11");
201 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
202 Installer.take(), "namespace AAA { } using namespace AAB;", Args));
203 ASSERT_LE(0, Provider.CallCount);
204 ASSERT_EQ(1, Watcher.SeenCount);
205}
206
207// Check that we use the first successful TypoCorrection returned from an
208// ExternalSemaSource.
209TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) {
210 llvm::OwningPtr<ExternalSemaSourceInstaller> Installer(
211 new ExternalSemaSourceInstaller);
212 NamespaceTypoProvider First("XXX", "BBB");
213 NamespaceTypoProvider Second("AAB", "CCC");
214 NamespaceTypoProvider Third("AAB", "DDD");
215 NamespaceDiagnosticWatcher Watcher("AAB", "CCC");
216 Installer->PushSource(&First);
217 Installer->PushSource(&Second);
218 Installer->PushSource(&Third);
219 Installer->PushWatcher(&Watcher);
220 std::vector<std::string> Args(1, "-std=c++11");
221 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
222 Installer.take(), "namespace AAA { } using namespace AAB;", Args));
223 ASSERT_LE(1, First.CallCount);
224 ASSERT_LE(1, Second.CallCount);
225 ASSERT_EQ(0, Third.CallCount);
226 ASSERT_EQ(1, Watcher.SeenCount);
227}
228
Kaelyn Uhrain5d937b32013-08-12 22:11:14 +0000229// We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise
230// solve the problem.
231TEST(ExternalSemaSource, TryOtherTacticsBeforeDiagnosing) {
232 llvm::OwningPtr<ExternalSemaSourceInstaller> Installer(
233 new ExternalSemaSourceInstaller);
234 CompleteTypeDiagnoser Diagnoser(false);
235 Installer->PushSource(&Diagnoser);
236 std::vector<std::string> Args(1, "-std=c++11");
237 // This code hits the class template specialization/class member of a class
238 // template specialization checks in Sema::RequireCompleteTypeImpl.
239 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
240 Installer.take(),
241 "template <typename T> struct S { class C { }; }; S<char>::C SCInst;",
242 Args));
243 ASSERT_EQ(0, Diagnoser.CallCount);
244}
245
246// The first ExternalSemaSource where MaybeDiagnoseMissingCompleteType returns
247// true should be the last one called.
248TEST(ExternalSemaSource, FirstDiagnoserTaken) {
249 llvm::OwningPtr<ExternalSemaSourceInstaller> Installer(
250 new ExternalSemaSourceInstaller);
251 CompleteTypeDiagnoser First(false);
252 CompleteTypeDiagnoser Second(true);
253 CompleteTypeDiagnoser Third(true);
254 Installer->PushSource(&First);
255 Installer->PushSource(&Second);
256 Installer->PushSource(&Third);
257 std::vector<std::string> Args(1, "-std=c++11");
258 ASSERT_FALSE(clang::tooling::runToolOnCodeWithArgs(
259 Installer.take(), "class Incomplete; Incomplete IncompleteInstance;",
260 Args));
261 ASSERT_EQ(1, First.CallCount);
262 ASSERT_EQ(1, Second.CallCount);
263 ASSERT_EQ(0, Third.CallCount);
264}
265
Kaelyn Uhrain4432bf02013-08-12 19:57:06 +0000266} // anonymous namespace