ExternalSemaSourceTest.cpp 9.4 KB

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