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