blob: 8aa626c2df12f24fae1249dac5bf4784c7c28c0b [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
27// \brief Counts the number of err_using_directive_member_suggest diagnostics
28// correcting from one namespace to another while still passing all diagnostics
29// along a chain of consumers.
30class NamespaceDiagnosticWatcher : public clang::DiagnosticConsumer {
31 DiagnosticConsumer *Chained;
32 std::string FromNS;
33 std::string ToNS;
34
35public:
36 NamespaceDiagnosticWatcher(StringRef From, StringRef To)
37 : Chained(NULL), FromNS(From), ToNS("'"), SeenCount(0) {
38 ToNS.append(To);
39 ToNS.append("'");
40 }
41
42 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
43 const Diagnostic &Info) {
44 if (Chained)
45 Chained->HandleDiagnostic(DiagLevel, Info);
46 if (Info.getID() - 1 == diag::err_using_directive_member_suggest) {
47 const IdentifierInfo *Ident = Info.getArgIdentifier(0);
48 const std::string &CorrectedQuotedStr = Info.getArgStdStr(1);
49 if (Ident->getName() == FromNS && CorrectedQuotedStr == ToNS)
50 ++SeenCount;
51 }
52 }
53
54 virtual void clear() {
55 DiagnosticConsumer::clear();
56 if (Chained)
57 Chained->clear();
58 }
59
60 virtual bool IncludeInDiagnosticCounts() const {
61 if (Chained)
62 return Chained->IncludeInDiagnosticCounts();
63 return false;
64 }
65
66 NamespaceDiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) {
67 Chained = ToChain;
68 return this;
69 }
70
71 int SeenCount;
72};
73
74// \brief Always corrects a typo matching CorrectFrom with a new namespace
75// with the name CorrectTo.
76class NamespaceTypoProvider : public clang::ExternalSemaSource {
77 std::string CorrectFrom;
78 std::string CorrectTo;
79 Sema *CurrentSema;
80
81public:
82 NamespaceTypoProvider(StringRef From, StringRef To)
83 : CorrectFrom(From), CorrectTo(To), CurrentSema(NULL), CallCount(0) {}
84
85 virtual void InitializeSema(Sema &S) { CurrentSema = &S; }
86
87 virtual void ForgetSema() { CurrentSema = NULL; }
88
89 virtual TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
90 int LookupKind, Scope *S, CXXScopeSpec *SS,
91 CorrectionCandidateCallback &CCC,
92 DeclContext *MemberContext,
93 bool EnteringContext,
94 const ObjCObjectPointerType *OPT) {
95 ++CallCount;
96 if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
97 DeclContext *DestContext = NULL;
98 ASTContext &Context = CurrentSema->getASTContext();
99 if (SS != NULL)
100 DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
101 if (DestContext == NULL)
102 DestContext = Context.getTranslationUnitDecl();
103 IdentifierInfo *ToIdent =
104 CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
105 NamespaceDecl *NewNamespace =
106 NamespaceDecl::Create(Context, DestContext, false, Typo.getBeginLoc(),
107 Typo.getLoc(), ToIdent, NULL);
108 DestContext->addDecl(NewNamespace);
109 TypoCorrection Correction(ToIdent);
110 Correction.addCorrectionDecl(NewNamespace);
111 return Correction;
112 }
113 return TypoCorrection();
114 }
115
116 int CallCount;
117};
118
119// \brief Chains together a vector of NamespaceDiagnosticWatchers and
120// adds a vector of ExternalSemaSources to the CompilerInstance before
121// performing semantic analysis.
122class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
123 std::vector<NamespaceDiagnosticWatcher *> Watchers;
124 std::vector<clang::ExternalSemaSource *> Sources;
125 llvm::OwningPtr<DiagnosticConsumer> OwnedClient;
126
127protected:
128 virtual clang::ASTConsumer *
129 CreateASTConsumer(clang::CompilerInstance &Compiler,
130 llvm::StringRef /* dummy */) {
131 return new clang::ASTConsumer();
132 }
133
134 virtual void ExecuteAction() {
135 CompilerInstance &CI = getCompilerInstance();
136 ASSERT_FALSE(CI.hasSema());
137 CI.createSema(getTranslationUnitKind(), NULL);
138 ASSERT_TRUE(CI.hasDiagnostics());
139 DiagnosticsEngine &Diagnostics = CI.getDiagnostics();
140 DiagnosticConsumer *Client = Diagnostics.getClient();
141 if (Diagnostics.ownsClient())
142 OwnedClient.reset(Diagnostics.takeClient());
143 for (size_t I = 0, E = Watchers.size(); I < E; ++I)
144 Client = Watchers[I]->Chain(Client);
145 Diagnostics.setClient(Client, false);
146 for (size_t I = 0, E = Sources.size(); I < E; ++I) {
147 Sources[I]->InitializeSema(CI.getSema());
148 CI.getSema().addExternalSource(Sources[I]);
149 }
150 ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
151 CI.getFrontendOpts().SkipFunctionBodies);
152 }
153
154public:
155 void PushSource(clang::ExternalSemaSource *Source) {
156 Sources.push_back(Source);
157 }
158
159 void PushWatcher(NamespaceDiagnosticWatcher *Watcher) {
160 Watchers.push_back(Watcher);
161 }
162};
163
164// Make sure that the NamespaceDiagnosticWatcher is not miscounting.
165TEST(ExternalSemaSource, SanityCheck) {
166 llvm::OwningPtr<ExternalSemaSourceInstaller> Installer(
167 new ExternalSemaSourceInstaller);
168 NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
169 Installer->PushWatcher(&Watcher);
170 std::vector<std::string> Args(1, "-std=c++11");
171 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
172 Installer.take(), "namespace AAA { } using namespace AAB;", Args));
173 ASSERT_EQ(0, Watcher.SeenCount);
174}
175
176// Check that when we add a NamespaceTypeProvider, we use that suggestion
177// instead of the usual suggestion we would use above.
178TEST(ExternalSemaSource, ExternalTypoCorrectionPrioritized) {
179 llvm::OwningPtr<ExternalSemaSourceInstaller> Installer(
180 new ExternalSemaSourceInstaller);
181 NamespaceTypoProvider Provider("AAB", "BBB");
182 NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
183 Installer->PushSource(&Provider);
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_LE(0, Provider.CallCount);
189 ASSERT_EQ(1, Watcher.SeenCount);
190}
191
192// Check that we use the first successful TypoCorrection returned from an
193// ExternalSemaSource.
194TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) {
195 llvm::OwningPtr<ExternalSemaSourceInstaller> Installer(
196 new ExternalSemaSourceInstaller);
197 NamespaceTypoProvider First("XXX", "BBB");
198 NamespaceTypoProvider Second("AAB", "CCC");
199 NamespaceTypoProvider Third("AAB", "DDD");
200 NamespaceDiagnosticWatcher Watcher("AAB", "CCC");
201 Installer->PushSource(&First);
202 Installer->PushSource(&Second);
203 Installer->PushSource(&Third);
204 Installer->PushWatcher(&Watcher);
205 std::vector<std::string> Args(1, "-std=c++11");
206 ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
207 Installer.take(), "namespace AAA { } using namespace AAB;", Args));
208 ASSERT_LE(1, First.CallCount);
209 ASSERT_LE(1, Second.CallCount);
210 ASSERT_EQ(0, Third.CallCount);
211 ASSERT_EQ(1, Watcher.SeenCount);
212}
213
214} // anonymous namespace