]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - unittests/Sema/ExternalSemaSourceTest.cpp
Vendor import of clang release_34 branch r197841 (effectively, 3.4 RC3):
[FreeBSD/FreeBSD.git] / unittests / Sema / ExternalSemaSourceTest.cpp
1 //=== 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
22 using namespace clang;
23 using namespace clang::tooling;
24
25 namespace {
26
27 // \brief Counts the number of times MaybeDiagnoseMissingCompleteType
28 // is called. Returns the result it was provided on creation.
29 class CompleteTypeDiagnoser : public clang::ExternalSemaSource {
30 public:
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
42 // \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.
45 class NamespaceDiagnosticWatcher : public clang::DiagnosticConsumer {
46   DiagnosticConsumer *Chained;
47   std::string FromNS;
48   std::string ToNS;
49
50 public:
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.
91 class NamespaceTypoProvider : public clang::ExternalSemaSource {
92   std::string CorrectFrom;
93   std::string CorrectTo;
94   Sema *CurrentSema;
95
96 public:
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.
137 class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
138   std::vector<NamespaceDiagnosticWatcher *> Watchers;
139   std::vector<clang::ExternalSemaSource *> Sources;
140   llvm::OwningPtr<DiagnosticConsumer> OwnedClient;
141
142 protected:
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
169 public:
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.
180 TEST(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.
193 TEST(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.
209 TEST(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
229 // We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise
230 // solve the problem.
231 TEST(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.
248 TEST(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
266 } // anonymous namespace