1*67e74705SXin Li //=== unittests/Sema/ExternalSemaSourceTest.cpp - ExternalSemaSource tests ===//
2*67e74705SXin Li //
3*67e74705SXin Li // The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li
10*67e74705SXin Li #include "clang/AST/ASTConsumer.h"
11*67e74705SXin Li #include "clang/AST/ASTContext.h"
12*67e74705SXin Li #include "clang/Frontend/CompilerInstance.h"
13*67e74705SXin Li #include "clang/Lex/Preprocessor.h"
14*67e74705SXin Li #include "clang/Parse/ParseAST.h"
15*67e74705SXin Li #include "clang/Sema/ExternalSemaSource.h"
16*67e74705SXin Li #include "clang/Sema/Sema.h"
17*67e74705SXin Li #include "clang/Sema/SemaDiagnostic.h"
18*67e74705SXin Li #include "clang/Sema/TypoCorrection.h"
19*67e74705SXin Li #include "clang/Tooling/Tooling.h"
20*67e74705SXin Li #include "gtest/gtest.h"
21*67e74705SXin Li
22*67e74705SXin Li using namespace clang;
23*67e74705SXin Li using namespace clang::tooling;
24*67e74705SXin Li
25*67e74705SXin Li namespace {
26*67e74705SXin Li
27*67e74705SXin Li // \brief Counts the number of times MaybeDiagnoseMissingCompleteType
28*67e74705SXin Li // is called. Returns the result it was provided on creation.
29*67e74705SXin Li class CompleteTypeDiagnoser : public clang::ExternalSemaSource {
30*67e74705SXin Li public:
CompleteTypeDiagnoser(bool MockResult)31*67e74705SXin Li CompleteTypeDiagnoser(bool MockResult) : CallCount(0), Result(MockResult) {}
32*67e74705SXin Li
MaybeDiagnoseMissingCompleteType(SourceLocation L,QualType T)33*67e74705SXin Li bool MaybeDiagnoseMissingCompleteType(SourceLocation L, QualType T) override {
34*67e74705SXin Li ++CallCount;
35*67e74705SXin Li return Result;
36*67e74705SXin Li }
37*67e74705SXin Li
38*67e74705SXin Li int CallCount;
39*67e74705SXin Li bool Result;
40*67e74705SXin Li };
41*67e74705SXin Li
42*67e74705SXin Li /// Counts the number of typo-correcting diagnostics correcting from one name to
43*67e74705SXin Li /// another while still passing all diagnostics along a chain of consumers.
44*67e74705SXin Li class DiagnosticWatcher : public clang::DiagnosticConsumer {
45*67e74705SXin Li DiagnosticConsumer *Chained;
46*67e74705SXin Li std::string FromName;
47*67e74705SXin Li std::string ToName;
48*67e74705SXin Li
49*67e74705SXin Li public:
DiagnosticWatcher(StringRef From,StringRef To)50*67e74705SXin Li DiagnosticWatcher(StringRef From, StringRef To)
51*67e74705SXin Li : Chained(nullptr), FromName(From), ToName("'"), SeenCount(0) {
52*67e74705SXin Li ToName.append(To);
53*67e74705SXin Li ToName.append("'");
54*67e74705SXin Li }
55*67e74705SXin Li
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)56*67e74705SXin Li void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
57*67e74705SXin Li const Diagnostic &Info) override {
58*67e74705SXin Li if (Chained)
59*67e74705SXin Li Chained->HandleDiagnostic(DiagLevel, Info);
60*67e74705SXin Li if (Info.getID() - 1 == diag::err_using_directive_member_suggest) {
61*67e74705SXin Li const IdentifierInfo *Ident = Info.getArgIdentifier(0);
62*67e74705SXin Li const std::string &CorrectedQuotedStr = Info.getArgStdStr(1);
63*67e74705SXin Li if (Ident->getName() == FromName && CorrectedQuotedStr == ToName)
64*67e74705SXin Li ++SeenCount;
65*67e74705SXin Li } else if (Info.getID() == diag::err_no_member_suggest) {
66*67e74705SXin Li auto Ident = DeclarationName::getFromOpaqueInteger(Info.getRawArg(0));
67*67e74705SXin Li const std::string &CorrectedQuotedStr = Info.getArgStdStr(3);
68*67e74705SXin Li if (Ident.getAsString() == FromName && CorrectedQuotedStr == ToName)
69*67e74705SXin Li ++SeenCount;
70*67e74705SXin Li }
71*67e74705SXin Li }
72*67e74705SXin Li
clear()73*67e74705SXin Li void clear() override {
74*67e74705SXin Li DiagnosticConsumer::clear();
75*67e74705SXin Li if (Chained)
76*67e74705SXin Li Chained->clear();
77*67e74705SXin Li }
78*67e74705SXin Li
IncludeInDiagnosticCounts() const79*67e74705SXin Li bool IncludeInDiagnosticCounts() const override {
80*67e74705SXin Li if (Chained)
81*67e74705SXin Li return Chained->IncludeInDiagnosticCounts();
82*67e74705SXin Li return false;
83*67e74705SXin Li }
84*67e74705SXin Li
Chain(DiagnosticConsumer * ToChain)85*67e74705SXin Li DiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) {
86*67e74705SXin Li Chained = ToChain;
87*67e74705SXin Li return this;
88*67e74705SXin Li }
89*67e74705SXin Li
90*67e74705SXin Li int SeenCount;
91*67e74705SXin Li };
92*67e74705SXin Li
93*67e74705SXin Li // \brief Always corrects a typo matching CorrectFrom with a new namespace
94*67e74705SXin Li // with the name CorrectTo.
95*67e74705SXin Li class NamespaceTypoProvider : public clang::ExternalSemaSource {
96*67e74705SXin Li std::string CorrectFrom;
97*67e74705SXin Li std::string CorrectTo;
98*67e74705SXin Li Sema *CurrentSema;
99*67e74705SXin Li
100*67e74705SXin Li public:
NamespaceTypoProvider(StringRef From,StringRef To)101*67e74705SXin Li NamespaceTypoProvider(StringRef From, StringRef To)
102*67e74705SXin Li : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}
103*67e74705SXin Li
InitializeSema(Sema & S)104*67e74705SXin Li void InitializeSema(Sema &S) override { CurrentSema = &S; }
105*67e74705SXin Li
ForgetSema()106*67e74705SXin Li void ForgetSema() override { CurrentSema = nullptr; }
107*67e74705SXin Li
CorrectTypo(const DeclarationNameInfo & Typo,int LookupKind,Scope * S,CXXScopeSpec * SS,CorrectionCandidateCallback & CCC,DeclContext * MemberContext,bool EnteringContext,const ObjCObjectPointerType * OPT)108*67e74705SXin Li TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
109*67e74705SXin Li Scope *S, CXXScopeSpec *SS,
110*67e74705SXin Li CorrectionCandidateCallback &CCC,
111*67e74705SXin Li DeclContext *MemberContext, bool EnteringContext,
112*67e74705SXin Li const ObjCObjectPointerType *OPT) override {
113*67e74705SXin Li ++CallCount;
114*67e74705SXin Li if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
115*67e74705SXin Li DeclContext *DestContext = nullptr;
116*67e74705SXin Li ASTContext &Context = CurrentSema->getASTContext();
117*67e74705SXin Li if (SS)
118*67e74705SXin Li DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
119*67e74705SXin Li if (!DestContext)
120*67e74705SXin Li DestContext = Context.getTranslationUnitDecl();
121*67e74705SXin Li IdentifierInfo *ToIdent =
122*67e74705SXin Li CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
123*67e74705SXin Li NamespaceDecl *NewNamespace =
124*67e74705SXin Li NamespaceDecl::Create(Context, DestContext, false, Typo.getBeginLoc(),
125*67e74705SXin Li Typo.getLoc(), ToIdent, nullptr);
126*67e74705SXin Li DestContext->addDecl(NewNamespace);
127*67e74705SXin Li TypoCorrection Correction(ToIdent);
128*67e74705SXin Li Correction.addCorrectionDecl(NewNamespace);
129*67e74705SXin Li return Correction;
130*67e74705SXin Li }
131*67e74705SXin Li return TypoCorrection();
132*67e74705SXin Li }
133*67e74705SXin Li
134*67e74705SXin Li int CallCount;
135*67e74705SXin Li };
136*67e74705SXin Li
137*67e74705SXin Li class FunctionTypoProvider : public clang::ExternalSemaSource {
138*67e74705SXin Li std::string CorrectFrom;
139*67e74705SXin Li std::string CorrectTo;
140*67e74705SXin Li Sema *CurrentSema;
141*67e74705SXin Li
142*67e74705SXin Li public:
FunctionTypoProvider(StringRef From,StringRef To)143*67e74705SXin Li FunctionTypoProvider(StringRef From, StringRef To)
144*67e74705SXin Li : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}
145*67e74705SXin Li
InitializeSema(Sema & S)146*67e74705SXin Li void InitializeSema(Sema &S) override { CurrentSema = &S; }
147*67e74705SXin Li
ForgetSema()148*67e74705SXin Li void ForgetSema() override { CurrentSema = nullptr; }
149*67e74705SXin Li
CorrectTypo(const DeclarationNameInfo & Typo,int LookupKind,Scope * S,CXXScopeSpec * SS,CorrectionCandidateCallback & CCC,DeclContext * MemberContext,bool EnteringContext,const ObjCObjectPointerType * OPT)150*67e74705SXin Li TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
151*67e74705SXin Li Scope *S, CXXScopeSpec *SS,
152*67e74705SXin Li CorrectionCandidateCallback &CCC,
153*67e74705SXin Li DeclContext *MemberContext, bool EnteringContext,
154*67e74705SXin Li const ObjCObjectPointerType *OPT) override {
155*67e74705SXin Li ++CallCount;
156*67e74705SXin Li if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
157*67e74705SXin Li DeclContext *DestContext = nullptr;
158*67e74705SXin Li ASTContext &Context = CurrentSema->getASTContext();
159*67e74705SXin Li if (SS)
160*67e74705SXin Li DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
161*67e74705SXin Li if (!DestContext)
162*67e74705SXin Li DestContext = Context.getTranslationUnitDecl();
163*67e74705SXin Li IdentifierInfo *ToIdent =
164*67e74705SXin Li CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
165*67e74705SXin Li auto *NewFunction = FunctionDecl::Create(
166*67e74705SXin Li Context, DestContext, SourceLocation(), SourceLocation(), ToIdent,
167*67e74705SXin Li Context.getFunctionType(Context.VoidTy, {}, {}), nullptr, SC_Static);
168*67e74705SXin Li DestContext->addDecl(NewFunction);
169*67e74705SXin Li TypoCorrection Correction(ToIdent);
170*67e74705SXin Li Correction.addCorrectionDecl(NewFunction);
171*67e74705SXin Li return Correction;
172*67e74705SXin Li }
173*67e74705SXin Li return TypoCorrection();
174*67e74705SXin Li }
175*67e74705SXin Li
176*67e74705SXin Li int CallCount;
177*67e74705SXin Li };
178*67e74705SXin Li
179*67e74705SXin Li // \brief Chains together a vector of DiagnosticWatchers and
180*67e74705SXin Li // adds a vector of ExternalSemaSources to the CompilerInstance before
181*67e74705SXin Li // performing semantic analysis.
182*67e74705SXin Li class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
183*67e74705SXin Li std::vector<DiagnosticWatcher *> Watchers;
184*67e74705SXin Li std::vector<clang::ExternalSemaSource *> Sources;
185*67e74705SXin Li std::unique_ptr<DiagnosticConsumer> OwnedClient;
186*67e74705SXin Li
187*67e74705SXin Li protected:
188*67e74705SXin Li std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance & Compiler,llvm::StringRef)189*67e74705SXin Li CreateASTConsumer(clang::CompilerInstance &Compiler,
190*67e74705SXin Li llvm::StringRef /* dummy */) override {
191*67e74705SXin Li return llvm::make_unique<clang::ASTConsumer>();
192*67e74705SXin Li }
193*67e74705SXin Li
ExecuteAction()194*67e74705SXin Li void ExecuteAction() override {
195*67e74705SXin Li CompilerInstance &CI = getCompilerInstance();
196*67e74705SXin Li ASSERT_FALSE(CI.hasSema());
197*67e74705SXin Li CI.createSema(getTranslationUnitKind(), nullptr);
198*67e74705SXin Li ASSERT_TRUE(CI.hasDiagnostics());
199*67e74705SXin Li DiagnosticsEngine &Diagnostics = CI.getDiagnostics();
200*67e74705SXin Li DiagnosticConsumer *Client = Diagnostics.getClient();
201*67e74705SXin Li if (Diagnostics.ownsClient())
202*67e74705SXin Li OwnedClient = Diagnostics.takeClient();
203*67e74705SXin Li for (size_t I = 0, E = Watchers.size(); I < E; ++I)
204*67e74705SXin Li Client = Watchers[I]->Chain(Client);
205*67e74705SXin Li Diagnostics.setClient(Client, false);
206*67e74705SXin Li for (size_t I = 0, E = Sources.size(); I < E; ++I) {
207*67e74705SXin Li Sources[I]->InitializeSema(CI.getSema());
208*67e74705SXin Li CI.getSema().addExternalSource(Sources[I]);
209*67e74705SXin Li }
210*67e74705SXin Li ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
211*67e74705SXin Li CI.getFrontendOpts().SkipFunctionBodies);
212*67e74705SXin Li }
213*67e74705SXin Li
214*67e74705SXin Li public:
PushSource(clang::ExternalSemaSource * Source)215*67e74705SXin Li void PushSource(clang::ExternalSemaSource *Source) {
216*67e74705SXin Li Sources.push_back(Source);
217*67e74705SXin Li }
218*67e74705SXin Li
PushWatcher(DiagnosticWatcher * Watcher)219*67e74705SXin Li void PushWatcher(DiagnosticWatcher *Watcher) { Watchers.push_back(Watcher); }
220*67e74705SXin Li };
221*67e74705SXin Li
222*67e74705SXin Li // Make sure that the DiagnosticWatcher is not miscounting.
TEST(ExternalSemaSource,SanityCheck)223*67e74705SXin Li TEST(ExternalSemaSource, SanityCheck) {
224*67e74705SXin Li std::unique_ptr<ExternalSemaSourceInstaller> Installer(
225*67e74705SXin Li new ExternalSemaSourceInstaller);
226*67e74705SXin Li DiagnosticWatcher Watcher("AAB", "BBB");
227*67e74705SXin Li Installer->PushWatcher(&Watcher);
228*67e74705SXin Li std::vector<std::string> Args(1, "-std=c++11");
229*67e74705SXin Li ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
230*67e74705SXin Li Installer.release(), "namespace AAA { } using namespace AAB;", Args));
231*67e74705SXin Li ASSERT_EQ(0, Watcher.SeenCount);
232*67e74705SXin Li }
233*67e74705SXin Li
234*67e74705SXin Li // Check that when we add a NamespaceTypeProvider, we use that suggestion
235*67e74705SXin Li // instead of the usual suggestion we would use above.
TEST(ExternalSemaSource,ExternalTypoCorrectionPrioritized)236*67e74705SXin Li TEST(ExternalSemaSource, ExternalTypoCorrectionPrioritized) {
237*67e74705SXin Li std::unique_ptr<ExternalSemaSourceInstaller> Installer(
238*67e74705SXin Li new ExternalSemaSourceInstaller);
239*67e74705SXin Li NamespaceTypoProvider Provider("AAB", "BBB");
240*67e74705SXin Li DiagnosticWatcher Watcher("AAB", "BBB");
241*67e74705SXin Li Installer->PushSource(&Provider);
242*67e74705SXin Li Installer->PushWatcher(&Watcher);
243*67e74705SXin Li std::vector<std::string> Args(1, "-std=c++11");
244*67e74705SXin Li ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
245*67e74705SXin Li Installer.release(), "namespace AAA { } using namespace AAB;", Args));
246*67e74705SXin Li ASSERT_LE(0, Provider.CallCount);
247*67e74705SXin Li ASSERT_EQ(1, Watcher.SeenCount);
248*67e74705SXin Li }
249*67e74705SXin Li
250*67e74705SXin Li // Check that we use the first successful TypoCorrection returned from an
251*67e74705SXin Li // ExternalSemaSource.
TEST(ExternalSemaSource,ExternalTypoCorrectionOrdering)252*67e74705SXin Li TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) {
253*67e74705SXin Li std::unique_ptr<ExternalSemaSourceInstaller> Installer(
254*67e74705SXin Li new ExternalSemaSourceInstaller);
255*67e74705SXin Li NamespaceTypoProvider First("XXX", "BBB");
256*67e74705SXin Li NamespaceTypoProvider Second("AAB", "CCC");
257*67e74705SXin Li NamespaceTypoProvider Third("AAB", "DDD");
258*67e74705SXin Li DiagnosticWatcher Watcher("AAB", "CCC");
259*67e74705SXin Li Installer->PushSource(&First);
260*67e74705SXin Li Installer->PushSource(&Second);
261*67e74705SXin Li Installer->PushSource(&Third);
262*67e74705SXin Li Installer->PushWatcher(&Watcher);
263*67e74705SXin Li std::vector<std::string> Args(1, "-std=c++11");
264*67e74705SXin Li ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
265*67e74705SXin Li Installer.release(), "namespace AAA { } using namespace AAB;", Args));
266*67e74705SXin Li ASSERT_LE(1, First.CallCount);
267*67e74705SXin Li ASSERT_LE(1, Second.CallCount);
268*67e74705SXin Li ASSERT_EQ(0, Third.CallCount);
269*67e74705SXin Li ASSERT_EQ(1, Watcher.SeenCount);
270*67e74705SXin Li }
271*67e74705SXin Li
TEST(ExternalSemaSource,ExternalDelayedTypoCorrection)272*67e74705SXin Li TEST(ExternalSemaSource, ExternalDelayedTypoCorrection) {
273*67e74705SXin Li std::unique_ptr<ExternalSemaSourceInstaller> Installer(
274*67e74705SXin Li new ExternalSemaSourceInstaller);
275*67e74705SXin Li FunctionTypoProvider Provider("aaa", "bbb");
276*67e74705SXin Li DiagnosticWatcher Watcher("aaa", "bbb");
277*67e74705SXin Li Installer->PushSource(&Provider);
278*67e74705SXin Li Installer->PushWatcher(&Watcher);
279*67e74705SXin Li std::vector<std::string> Args(1, "-std=c++11");
280*67e74705SXin Li ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
281*67e74705SXin Li Installer.release(), "namespace AAA { } void foo() { AAA::aaa(); }",
282*67e74705SXin Li Args));
283*67e74705SXin Li ASSERT_LE(0, Provider.CallCount);
284*67e74705SXin Li ASSERT_EQ(1, Watcher.SeenCount);
285*67e74705SXin Li }
286*67e74705SXin Li
287*67e74705SXin Li // We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise
288*67e74705SXin Li // solve the problem.
TEST(ExternalSemaSource,TryOtherTacticsBeforeDiagnosing)289*67e74705SXin Li TEST(ExternalSemaSource, TryOtherTacticsBeforeDiagnosing) {
290*67e74705SXin Li std::unique_ptr<ExternalSemaSourceInstaller> Installer(
291*67e74705SXin Li new ExternalSemaSourceInstaller);
292*67e74705SXin Li CompleteTypeDiagnoser Diagnoser(false);
293*67e74705SXin Li Installer->PushSource(&Diagnoser);
294*67e74705SXin Li std::vector<std::string> Args(1, "-std=c++11");
295*67e74705SXin Li // This code hits the class template specialization/class member of a class
296*67e74705SXin Li // template specialization checks in Sema::RequireCompleteTypeImpl.
297*67e74705SXin Li ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
298*67e74705SXin Li Installer.release(),
299*67e74705SXin Li "template <typename T> struct S { class C { }; }; S<char>::C SCInst;",
300*67e74705SXin Li Args));
301*67e74705SXin Li ASSERT_EQ(0, Diagnoser.CallCount);
302*67e74705SXin Li }
303*67e74705SXin Li
304*67e74705SXin Li // The first ExternalSemaSource where MaybeDiagnoseMissingCompleteType returns
305*67e74705SXin Li // true should be the last one called.
TEST(ExternalSemaSource,FirstDiagnoserTaken)306*67e74705SXin Li TEST(ExternalSemaSource, FirstDiagnoserTaken) {
307*67e74705SXin Li std::unique_ptr<ExternalSemaSourceInstaller> Installer(
308*67e74705SXin Li new ExternalSemaSourceInstaller);
309*67e74705SXin Li CompleteTypeDiagnoser First(false);
310*67e74705SXin Li CompleteTypeDiagnoser Second(true);
311*67e74705SXin Li CompleteTypeDiagnoser Third(true);
312*67e74705SXin Li Installer->PushSource(&First);
313*67e74705SXin Li Installer->PushSource(&Second);
314*67e74705SXin Li Installer->PushSource(&Third);
315*67e74705SXin Li std::vector<std::string> Args(1, "-std=c++11");
316*67e74705SXin Li ASSERT_FALSE(clang::tooling::runToolOnCodeWithArgs(
317*67e74705SXin Li Installer.release(), "class Incomplete; Incomplete IncompleteInstance;",
318*67e74705SXin Li Args));
319*67e74705SXin Li ASSERT_EQ(1, First.CallCount);
320*67e74705SXin Li ASSERT_EQ(1, Second.CallCount);
321*67e74705SXin Li ASSERT_EQ(0, Third.CallCount);
322*67e74705SXin Li }
323*67e74705SXin Li
324*67e74705SXin Li } // anonymous namespace
325