xref: /minix3/external/bsd/llvm/dist/clang/unittests/Sema/ExternalSemaSourceTest.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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