From 38d99f6be1ba2f56c118c3ed970018a386de5add Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Thu, 19 May 2016 10:46:10 +0000 Subject: [PATCH] [Sema] Allow an external sema source to handle delayed typo corrections. This probably isn't perfectly perfect but allows correcting function calls again. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@270039 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaLookup.cpp | 12 ++- unittests/Sema/ExternalSemaSourceTest.cpp | 101 +++++++++++++++++----- 2 files changed, 90 insertions(+), 23 deletions(-) diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 647055f5f0..e538f3cb57 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -4781,11 +4781,19 @@ TypoExpr *Sema::CorrectTypoDelayed( const ObjCObjectPointerType *OPT) { assert(CCC && "CorrectTypoDelayed requires a CorrectionCandidateCallback"); - TypoCorrection Empty; auto Consumer = makeTypoCorrectionConsumer( TypoName, LookupKind, S, SS, std::move(CCC), MemberContext, EnteringContext, OPT, Mode == CTK_ErrorRecovery); + // Give the external sema source a chance to correct the typo. + TypoCorrection ExternalTypo; + if (ExternalSource && Consumer) { + ExternalTypo = ExternalSource->CorrectTypo( + TypoName, LookupKind, S, SS, *CCC, MemberContext, EnteringContext, OPT); + if (ExternalTypo) + Consumer->addCorrection(ExternalTypo); + } + if (!Consumer || Consumer->empty()) return nullptr; @@ -4793,7 +4801,7 @@ TypoExpr *Sema::CorrectTypoDelayed( // is not more that about a third of the length of the typo's identifier. unsigned ED = Consumer->getBestEditDistance(true); IdentifierInfo *Typo = TypoName.getName().getAsIdentifierInfo(); - if (ED > 0 && Typo->getName().size() / ED < 3) + if (!ExternalTypo && ED > 0 && Typo->getName().size() / ED < 3) return nullptr; ExprEvalContexts.back().NumTypos++; diff --git a/unittests/Sema/ExternalSemaSourceTest.cpp b/unittests/Sema/ExternalSemaSourceTest.cpp index 703e97b4ac..d2cdd633fa 100644 --- a/unittests/Sema/ExternalSemaSourceTest.cpp +++ b/unittests/Sema/ExternalSemaSourceTest.cpp @@ -39,19 +39,18 @@ public: bool Result; }; -// \brief Counts the number of err_using_directive_member_suggest diagnostics -// correcting from one namespace to another while still passing all diagnostics -// along a chain of consumers. -class NamespaceDiagnosticWatcher : public clang::DiagnosticConsumer { +/// Counts the number of typo-correcting diagnostics correcting from one name to +/// another while still passing all diagnostics along a chain of consumers. +class DiagnosticWatcher : public clang::DiagnosticConsumer { DiagnosticConsumer *Chained; - std::string FromNS; - std::string ToNS; + std::string FromName; + std::string ToName; public: - NamespaceDiagnosticWatcher(StringRef From, StringRef To) - : Chained(nullptr), FromNS(From), ToNS("'"), SeenCount(0) { - ToNS.append(To); - ToNS.append("'"); + DiagnosticWatcher(StringRef From, StringRef To) + : Chained(nullptr), FromName(From), ToName("'"), SeenCount(0) { + ToName.append(To); + ToName.append("'"); } void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, @@ -61,7 +60,12 @@ public: if (Info.getID() - 1 == diag::err_using_directive_member_suggest) { const IdentifierInfo *Ident = Info.getArgIdentifier(0); const std::string &CorrectedQuotedStr = Info.getArgStdStr(1); - if (Ident->getName() == FromNS && CorrectedQuotedStr == ToNS) + if (Ident->getName() == FromName && CorrectedQuotedStr == ToName) + ++SeenCount; + } else if (Info.getID() == diag::err_no_member_suggest) { + auto Ident = DeclarationName::getFromOpaqueInteger(Info.getRawArg(0)); + const std::string &CorrectedQuotedStr = Info.getArgStdStr(3); + if (Ident.getAsString() == FromName && CorrectedQuotedStr == ToName) ++SeenCount; } } @@ -78,7 +82,7 @@ public: return false; } - NamespaceDiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) { + DiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) { Chained = ToChain; return this; } @@ -130,11 +134,53 @@ public: int CallCount; }; -// \brief Chains together a vector of NamespaceDiagnosticWatchers and +class FunctionTypoProvider : public clang::ExternalSemaSource { + std::string CorrectFrom; + std::string CorrectTo; + Sema *CurrentSema; + +public: + FunctionTypoProvider(StringRef From, StringRef To) + : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {} + + void InitializeSema(Sema &S) override { CurrentSema = &S; } + + void ForgetSema() override { CurrentSema = nullptr; } + + TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind, + Scope *S, CXXScopeSpec *SS, + CorrectionCandidateCallback &CCC, + DeclContext *MemberContext, bool EnteringContext, + const ObjCObjectPointerType *OPT) override { + ++CallCount; + if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) { + DeclContext *DestContext = nullptr; + ASTContext &Context = CurrentSema->getASTContext(); + if (SS) + DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext); + if (!DestContext) + DestContext = Context.getTranslationUnitDecl(); + IdentifierInfo *ToIdent = + CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo); + auto *NewFunction = FunctionDecl::Create( + Context, DestContext, SourceLocation(), SourceLocation(), ToIdent, + Context.getFunctionType(Context.VoidTy, {}, {}), nullptr, SC_Static); + DestContext->addDecl(NewFunction); + TypoCorrection Correction(ToIdent); + Correction.addCorrectionDecl(NewFunction); + return Correction; + } + return TypoCorrection(); + } + + int CallCount; +}; + +// \brief Chains together a vector of DiagnosticWatchers and // adds a vector of ExternalSemaSources to the CompilerInstance before // performing semantic analysis. class ExternalSemaSourceInstaller : public clang::ASTFrontendAction { - std::vector Watchers; + std::vector Watchers; std::vector Sources; std::unique_ptr OwnedClient; @@ -170,16 +216,14 @@ public: Sources.push_back(Source); } - void PushWatcher(NamespaceDiagnosticWatcher *Watcher) { - Watchers.push_back(Watcher); - } + void PushWatcher(DiagnosticWatcher *Watcher) { Watchers.push_back(Watcher); } }; -// Make sure that the NamespaceDiagnosticWatcher is not miscounting. +// Make sure that the DiagnosticWatcher is not miscounting. TEST(ExternalSemaSource, SanityCheck) { std::unique_ptr Installer( new ExternalSemaSourceInstaller); - NamespaceDiagnosticWatcher Watcher("AAB", "BBB"); + DiagnosticWatcher Watcher("AAB", "BBB"); Installer->PushWatcher(&Watcher); std::vector Args(1, "-std=c++11"); ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs( @@ -193,7 +237,7 @@ TEST(ExternalSemaSource, ExternalTypoCorrectionPrioritized) { std::unique_ptr Installer( new ExternalSemaSourceInstaller); NamespaceTypoProvider Provider("AAB", "BBB"); - NamespaceDiagnosticWatcher Watcher("AAB", "BBB"); + DiagnosticWatcher Watcher("AAB", "BBB"); Installer->PushSource(&Provider); Installer->PushWatcher(&Watcher); std::vector Args(1, "-std=c++11"); @@ -211,7 +255,7 @@ TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) { NamespaceTypoProvider First("XXX", "BBB"); NamespaceTypoProvider Second("AAB", "CCC"); NamespaceTypoProvider Third("AAB", "DDD"); - NamespaceDiagnosticWatcher Watcher("AAB", "CCC"); + DiagnosticWatcher Watcher("AAB", "CCC"); Installer->PushSource(&First); Installer->PushSource(&Second); Installer->PushSource(&Third); @@ -225,6 +269,21 @@ TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) { ASSERT_EQ(1, Watcher.SeenCount); } +TEST(ExternalSemaSource, ExternalDelayedTypoCorrection) { + std::unique_ptr Installer( + new ExternalSemaSourceInstaller); + FunctionTypoProvider Provider("aaa", "bbb"); + DiagnosticWatcher Watcher("aaa", "bbb"); + Installer->PushSource(&Provider); + Installer->PushWatcher(&Watcher); + std::vector Args(1, "-std=c++11"); + ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs( + Installer.release(), "namespace AAA { } void foo() { AAA::aaa(); }", + Args)); + ASSERT_LE(0, Provider.CallCount); + ASSERT_EQ(1, Watcher.SeenCount); +} + // We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise // solve the problem. TEST(ExternalSemaSource, TryOtherTacticsBeforeDiagnosing) { -- 2.40.0