The the typo count is propagated up the stack of
ExpressionEvaluationContextRecords when one is popped off of to
avoid accidentally dropping TypoExprs on the floor. For example,
the attempted correction of g() in test/CXX/class/class.mem/p5-0x.cpp
happens with an ExpressionEvaluationContextRecord that is popped off
the stack prior to ActOnFinishFullExpr being called and the tree
transform for TypoExprs being run.
---
 include/clang/Sema/Sema.h         |  25 ++++
 include/clang/Sema/SemaInternal.h |  15 +-
 lib/Sema/SemaExpr.cpp             |   7 +
 lib/Sema/SemaExprCXX.cpp          | 109 ++++++++++++++
 lib/Sema/SemaLookup.cpp           | 291 ++++++++++++++++++++++++--------------
 5 files changed, 340 insertions(+), 107 deletions(-)

diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index f9db796..2f5589d 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -741,6 +741,10 @@ public:
     /// this expression evaluation context.
     unsigned NumCleanupObjects;
 
+    /// \brief The number of typos encountered during this expression evaluation
+    /// context (i.e. the number of TypoExprs created).
+    unsigned NumTypos;
+
     llvm::SmallPtrSet<Expr*, 2> SavedMaybeODRUseExprs;
 
     /// \brief The lambdas that are present within this context, if it
@@ -774,6 +778,7 @@ public:
                                       bool IsDecltype)
       : Context(Context), ParentNeedsCleanups(ParentNeedsCleanups),
         IsDecltype(IsDecltype), NumCleanupObjects(NumCleanupObjects),
+        NumTypos(0),
         ManglingContextDecl(ManglingContextDecl), MangleNumbering() { }
 
     /// \brief Retrieve the mangling numbering context, used to consistently
@@ -2569,6 +2574,18 @@ private:
   /// source.
   bool LoadedExternalKnownNamespaces;
 
+  /// \brief Helper for CorrectTypo and CorrectTypoDelayed used to create and
+  /// populate a new TypoCorrectionConsumer. Returns nullptr if typo correction
+  /// should be skipped entirely.
+  std::unique_ptr<TypoCorrectionConsumer>
+  makeTypoCorrectionConsumer(const DeclarationNameInfo &Typo,
+                             Sema::LookupNameKind LookupKind, Scope *S,
+                             CXXScopeSpec *SS,
+                             std::unique_ptr<CorrectionCandidateCallback> CCC,
+                             DeclContext *MemberContext, bool EnteringContext,
+                             const ObjCObjectPointerType *OPT,
+                             bool ErrorRecovery, bool &IsUnqualifiedLookup);
+
 public:
   /// \brief Look up a name, looking for a single declaration.  Return
   /// null if the results were absent, ambiguous, or overloaded.
@@ -2646,6 +2663,14 @@ public:
                              const ObjCObjectPointerType *OPT = nullptr,
                              bool RecordFailure = true);
 
+  TypoExpr *CorrectTypoDelayed(
+      const DeclarationNameInfo &Typo, Sema::LookupNameKind LookupKind,
+      Scope *S, CXXScopeSpec *SS,
+      std::unique_ptr<CorrectionCandidateCallback> CCC,
+      std::unique_ptr<TypoDiagnosticGenerator> TDG, CorrectTypoKind Mode,
+      DeclContext *MemberContext = nullptr, bool EnteringContext = false,
+      const ObjCObjectPointerType *OPT = nullptr);
+
   void diagnoseTypo(const TypoCorrection &Correction,
                     const PartialDiagnostic &TypoDiag,
                     bool ErrorRecovery = true);
diff --git a/include/clang/Sema/SemaInternal.h b/include/clang/Sema/SemaInternal.h
index aaaa3e7..12b64ee 100644
--- a/include/clang/Sema/SemaInternal.h
+++ b/include/clang/Sema/SemaInternal.h
@@ -101,8 +101,10 @@ public:
                          DeclContext *MemberContext,
                          bool EnteringContext)
       : Typo(TypoName.getName().getAsIdentifierInfo()), CurrentTCIndex(0),
-        SemaRef(SemaRef), S(S), SS(SS), CorrectionValidator(std::move(CCC)),
-        MemberContext(MemberContext), Result(SemaRef, TypoName, LookupKind),
+        SemaRef(SemaRef), S(S),
+        SS(SS ? llvm::make_unique<CXXScopeSpec>(*SS) : nullptr),
+        CorrectionValidator(std::move(CCC)), MemberContext(MemberContext),
+        Result(SemaRef, TypoName, LookupKind),
         Namespaces(SemaRef.Context, SemaRef.CurContext, SS),
         EnteringContext(EnteringContext), SearchNamespaces(false) {
     Result.suppressDiagnostics();
@@ -167,6 +169,13 @@ public:
     CurrentTCIndex = 0;
   }
 
+  /// \brief Return whether the end of the stream of corrections has been
+  /// reached.
+  bool finished() {
+    return CorrectionResults.empty() &&
+           CurrentTCIndex >= ValidatedCorrections.size();
+  }
+
   ASTContext &getContext() const { return SemaRef.Context; }
   const LookupResult &getLookupResult() const { return Result; }
 
@@ -246,7 +255,7 @@ private:
 
   Sema &SemaRef;
   Scope *S;
-  CXXScopeSpec *SS;
+  std::unique_ptr<CXXScopeSpec> SS;
   std::unique_ptr<CorrectionCandidateCallback> CorrectionValidator;
   DeclContext *MemberContext;
   LookupResult Result;
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 406aad2..973a040 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -11286,6 +11286,7 @@ Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
 
 void Sema::PopExpressionEvaluationContext() {
   ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back();
+  unsigned NumTypos = Rec.NumTypos;
 
   if (!Rec.Lambdas.empty()) {
     if (Rec.isUnevaluated() || Rec.Context == ConstantEvaluated) {
@@ -11338,6 +11339,12 @@ void Sema::PopExpressionEvaluationContext() {
 
   // Pop the current expression evaluation context off the stack.
   ExprEvalContexts.pop_back();
+
+  if (NumTypos > 0 && !ExprEvalContexts.empty())
+    ExprEvalContexts.back().NumTypos += NumTypos;
+  else
+    assert(NumTypos == 0 && "There are outstanding typos after popping the "
+                            "last ExpressionEvaluationContextRecord");
 }
 
 void Sema::DiscardCleanupsInEvaluationContext() {
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index fc3ede0..9dc0e01 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Sema/SemaInternal.h"
+#include "TreeTransform.h"
 #include "TypeLocBuilder.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTLambda.h"
@@ -5917,6 +5918,104 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(
   CurrentLSI->clearPotentialCaptures();
 }
 
+namespace {
+class TransformTypos : public TreeTransform<TransformTypos> {
+  typedef TreeTransform<TransformTypos> BaseTransform;
+
+  llvm::SmallVector<Expr *, 4> ExprStack;
+  llvm::SmallPtrSet<TypoExpr *, 2> TypoExprs;
+  llvm::SmallDenseMap<TypoExpr *, ExprResult, 2> TransformCache;
+  TypoExpr *FirstTypoExpr;
+
+public:
+  TransformTypos(Sema &SemaRef)
+      : BaseTransform(SemaRef), FirstTypoExpr(nullptr) {}
+
+  ExprResult TransformLambdaExpr(LambdaExpr *E) { return Owned(E); }
+
+  ExprResult TransformExpr(Expr *E) {
+    ExprStack.push_back(E);
+    ExprResult Res = BaseTransform::TransformExpr(E);
+    ExprStack.pop_back();
+    return Res;
+  }
+
+  ExprResult Transform(Expr *E) {
+    ExprResult res;
+    bool error = false;
+    while (true) {
+      Sema::SFINAETrap Trap(SemaRef);
+      res = TransformExpr(E);
+      error = Trap.hasErrorOccurred();
+
+      // Exit if either the transform was valid or if there were no TypoExprs
+      // to transform.
+      if (!(error || res.isInvalid()) || TypoExprs.empty())
+        break;
+
+      // If corrections for the first TypoExpr have been exhausted, retry them
+      // against the next combination of substitutions for all of the other
+      // TypoExprs.
+      if (FirstTypoExpr->Consumer->finished()) {
+        unsigned numFinished = 1;
+        for (auto TE : TypoExprs) {
+          if (TE != FirstTypoExpr) {
+            TransformCache.erase(TE);
+            if (TE->Consumer->finished()) {
+              ++numFinished;
+              TE->Consumer->resetCorrectionStream();
+            } else {
+              break;
+            }
+          }
+        }
+        FirstTypoExpr->Consumer->resetCorrectionStream();
+        if (numFinished >= TypoExprs.size())
+          break;
+      }
+    };
+
+    // Emit diagnostics for all of the TypoExprs encountered.
+    for (auto E : TypoExprs) {
+      TypoExpr *TE = cast<TypoExpr>(E);
+      if (TE->DiagHandler)
+        (*TE->DiagHandler)(TE->Consumer->getCurrentCorrection());
+      TE->clear();
+    }
+
+    return res;
+  }
+
+  ExprResult TransformTypoExpr(TypoExpr *E) {
+    assert(E->Consumer && "Cannot transform a cleared TypoExpr");
+
+    if (!TypoExprs.count(E)) {
+      // If the TypoExpr hasn't been seen before, record it. Also remember if it
+      // is the first TypoExpr seen by this TreeTransform.
+      if (!FirstTypoExpr)
+        FirstTypoExpr = E;
+      TypoExprs.insert(E);
+    } else if (E != FirstTypoExpr &&
+               TransformCache.find(E) != TransformCache.end()) {
+      // Return the cached replacement for the TypoExpr when appropriate.
+      return TransformCache[E];
+    }
+
+    // For the first TypoExpr and an uncached TypoExpr, find the next likely
+    // typo correction and return it.
+    while (TypoCorrection TC = E->Consumer->getNextCorrection()) {
+      LookupResult R(SemaRef,
+                     E->Consumer->getLookupResult().getLookupNameInfo(),
+                     E->Consumer->getLookupResult().getLookupKind());
+      if (!TC.isKeyword())
+        R.addDecl(TC.getCorrectionDecl());
+      return TransformCache[E] =
+                 SemaRef.BuildDeclarationNameExpr(CXXScopeSpec(), R, false);
+    }
+    return TransformCache[E] = ExprError();
+  }
+};
+}
 
 ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC,
                                      bool DiscardedValue,
@@ -5964,6 +6063,16 @@ ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC,
       return ExprError();
   }
 
+  if (ExprEvalContexts.back().NumTypos &&
+      (FullExpr.get()->isTypeDependent() ||
+       FullExpr.get()->isValueDependent() ||
+       FullExpr.get()->isInstantiationDependent())) {
+    FullExpr = TransformTypos(*this).Transform(FullExpr.get());
+    ExprEvalContexts.back().NumTypos = 0;
+    if (FullExpr.isInvalid())
+      return ExprError();
+  }
+
   CheckCompletedExpr(FullExpr.get(), CC, IsConstexpr);
 
   // At the end of this full expression (which could be a deeply nested 
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index c0fe2c3..64269a1 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -3476,7 +3476,7 @@ TypoCorrection &TypoCorrectionConsumer::getNextCorrection() {
 bool TypoCorrectionConsumer::resolveCorrection(TypoCorrection &Candidate) {
   IdentifierInfo *Name = Candidate.getCorrectionAsIdentifierInfo();
   DeclContext *TempMemberContext = MemberContext;
-  CXXScopeSpec *TempSS = SS;
+  CXXScopeSpec *TempSS = SS.get();
   if (Candidate.getCorrectionRange().isInvalid())
     Candidate.setCorrectionRange(TempSS, Result.getLookupNameInfo());
 retry_lookup:
@@ -3496,7 +3496,7 @@ retry_lookup:
     }
     if (TempMemberContext) {
       if (SS && !TempSS)
-        TempSS = SS;
+        TempSS = SS.get();
       TempMemberContext = nullptr;
       goto retry_lookup;
     }
@@ -3984,101 +3984,59 @@ static void checkCorrectionVisibility(Sema &SemaRef, TypoCorrection &TC) {
   }
 }
 
-/// \brief Try to "correct" a typo in the source code by finding
-/// visible declarations whose names are similar to the name that was
-/// present in the source code.
-///
-/// \param TypoName the \c DeclarationNameInfo structure that contains
-/// the name that was present in the source code along with its location.
-///
-/// \param LookupKind the name-lookup criteria used to search for the name.
-///
-/// \param S the scope in which name lookup occurs.
-///
-/// \param SS the nested-name-specifier that precedes the name we're
-/// looking for, if present.
-///
-/// \param CCC A CorrectionCandidateCallback object that provides further
-/// validation of typo correction candidates. It also provides flags for
-/// determining the set of keywords permitted.
-///
-/// \param MemberContext if non-NULL, the context in which to look for
-/// a member access expression.
-///
-/// \param EnteringContext whether we're entering the context described by
-/// the nested-name-specifier SS.
-///
-/// \param OPT when non-NULL, the search for visible declarations will
-/// also walk the protocols in the qualified interfaces of \p OPT.
-///
-/// \returns a \c TypoCorrection containing the corrected name if the typo
-/// along with information such as the \c NamedDecl where the corrected name
-/// was declared, and any additional \c NestedNameSpecifier needed to access
-/// it (C++ only). The \c TypoCorrection is empty if there is no correction.
-TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
-                                 Sema::LookupNameKind LookupKind,
-                                 Scope *S, CXXScopeSpec *SS,
-                                 std::unique_ptr<CorrectionCandidateCallback> CCC,
-                                 CorrectTypoKind Mode,
-                                 DeclContext *MemberContext,
-                                 bool EnteringContext,
-                                 const ObjCObjectPointerType *OPT,
-                                 bool RecordFailure) {
-  assert(CCC && "CorrectTypo requires a CorrectionCandidateCallback");
-
-  // Always let the ExternalSource have the first chance at correction, even
-  // if we would otherwise have given up.
-  if (ExternalSource) {
-    if (TypoCorrection Correction = ExternalSource->CorrectTypo(
-        TypoName, LookupKind, S, SS, *CCC, MemberContext, EnteringContext, OPT))
-      return Correction;
-  }
+std::unique_ptr<TypoCorrectionConsumer> Sema::makeTypoCorrectionConsumer(
+    const DeclarationNameInfo &TypoName, Sema::LookupNameKind LookupKind,
+    Scope *S, CXXScopeSpec *SS,
+    std::unique_ptr<CorrectionCandidateCallback> CCC,
+    DeclContext *MemberContext, bool EnteringContext,
+    const ObjCObjectPointerType *OPT, bool ErrorRecovery,
+    bool &IsUnqualifiedLookup) {
 
   if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking ||
       DisableTypoCorrection)
-    return TypoCorrection();
+    return nullptr;
 
   // In Microsoft mode, don't perform typo correction in a template member
   // function dependent context because it interferes with the "lookup into
   // dependent bases of class templates" feature.
   if (getLangOpts().MSVCCompat && CurContext->isDependentContext() &&
       isa<CXXMethodDecl>(CurContext))
-    return TypoCorrection();
+    return nullptr;
 
   // We only attempt to correct typos for identifiers.
   IdentifierInfo *Typo = TypoName.getName().getAsIdentifierInfo();
   if (!Typo)
-    return TypoCorrection();
+    return nullptr;
 
   // If the scope specifier itself was invalid, don't try to correct
   // typos.
   if (SS && SS->isInvalid())
-    return TypoCorrection();
+    return nullptr;
 
   // Never try to correct typos during template deduction or
   // instantiation.
   if (!ActiveTemplateInstantiations.empty())
-    return TypoCorrection();
+    return nullptr;
 
   // Don't try to correct 'super'.
   if (S && S->isInObjcMethodScope() && Typo == getSuperIdentifier())
-    return TypoCorrection();
+    return nullptr;
 
   // Abort if typo correction already failed for this specific typo.
   IdentifierSourceLocations::iterator locs = TypoCorrectionFailures.find(Typo);
   if (locs != TypoCorrectionFailures.end() &&
       locs->second.count(TypoName.getLoc()))
-    return TypoCorrection();
+    return nullptr;
 
   // Don't try to correct the identifier "vector" when in AltiVec mode.
   // TODO: Figure out why typo correction misbehaves in this case, fix it, and
   // remove this workaround.
   if (getLangOpts().AltiVec && Typo->isStr("vector"))
-    return TypoCorrection();
+    return nullptr;
 
   // If we're handling a missing symbol error, using modules, and the
   // special search all modules option is used, look for a missing import.
-  if ((Mode == CTK_ErrorRecovery) &&  getLangOpts().Modules &&
+  if (ErrorRecovery && getLangOpts().Modules &&
       getLangOpts().ModulesSearchAll) {
     // The following has the side effect of loading the missing module.
     getModuleLoader().lookupMissingImports(Typo->getName(),
@@ -4086,9 +4044,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
   }
 
   CorrectionCandidateCallback &CCCRef = *CCC;
-  TypoCorrectionConsumer Consumer(*this, TypoName, LookupKind, S, SS,
-                                  std::move(CCC), MemberContext,
-                                  EnteringContext);
+  auto Consumer = llvm::make_unique<TypoCorrectionConsumer>(
+      *this, TypoName, LookupKind, S, SS, std::move(CCC), MemberContext,
+      EnteringContext);
 
   // If a callback object considers an empty typo correction candidate to be
   // viable, assume it does not do any actual validation of the candidates.
@@ -4096,29 +4054,29 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
   bool ValidatingCallback = !isCandidateViable(CCCRef, EmptyCorrection);
 
   // Perform name lookup to find visible, similarly-named entities.
-  bool IsUnqualifiedLookup = false;
+  IsUnqualifiedLookup = false;
   DeclContext *QualifiedDC = MemberContext;
   if (MemberContext) {
-    LookupVisibleDecls(MemberContext, LookupKind, Consumer);
+    LookupVisibleDecls(MemberContext, LookupKind, *Consumer);
 
     // Look in qualified interfaces.
     if (OPT) {
       for (auto *I : OPT->quals())
-        LookupVisibleDecls(I, LookupKind, Consumer);
+        LookupVisibleDecls(I, LookupKind, *Consumer);
     }
   } else if (SS && SS->isSet()) {
     QualifiedDC = computeDeclContext(*SS, EnteringContext);
     if (!QualifiedDC)
-      return TypoCorrection();
+      return nullptr;
 
     // Provide a stop gap for files that are just seriously broken.  Trying
     // to correct all typos can turn into a HUGE performance penalty, causing
     // some files to take minutes to get rejected by the parser.
     if (TyposCorrected + UnqualifiedTyposCorrected.size() >= 20)
-      return TypoCorrection();
+      return nullptr;
     ++TyposCorrected;
 
-    LookupVisibleDecls(QualifiedDC, LookupKind, Consumer);
+    LookupVisibleDecls(QualifiedDC, LookupKind, *Consumer);
   } else {
     IsUnqualifiedLookup = true;
     UnqualifiedTyposCorrectedMap::iterator Cached
@@ -4135,13 +4093,13 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
                                        CorrectionDecl->getLocation());
           LookupResult R(*this, NameInfo, LookupOrdinaryName);
           if (LookupName(R, S))
-            Consumer.addCorrection(Cached->second);
+            Consumer->addCorrection(Cached->second);
         }
       } else {
         // Only honor no-correction cache hits when a callback that will validate
         // correction candidates is not being used.
         if (!ValidatingCallback)
-          return TypoCorrection();
+          return nullptr;
       }
     }
     if (Cached == UnqualifiedTyposCorrected.end()) {
@@ -4149,7 +4107,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
       // to correct all typos can turn into a HUGE performance penalty, causing
       // some files to take minutes to get rejected by the parser.
       if (TyposCorrected + UnqualifiedTyposCorrected.size() >= 20)
-        return TypoCorrection();
+        return nullptr;
     }
   }
 
@@ -4158,10 +4116,6 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
   bool SearchNamespaces
     = getLangOpts().CPlusPlus &&
       (IsUnqualifiedLookup || (SS && SS->isSet()));
-  // In a few cases we *only* want to search for corrections based on just
-  // adding or changing the nested name specifier.
-  unsigned TypoLen = Typo->getName().size();
-  bool AllowOnlyNNSChanges = TypoLen < 3;
 
   if (IsUnqualifiedLookup || SearchNamespaces) {
     // For unqualified lookup, look through all of the names that we have
@@ -4170,7 +4124,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
     for (IdentifierTable::iterator I = Context.Idents.begin(),
                                 IEnd = Context.Idents.end();
          I != IEnd; ++I)
-      Consumer.FoundName(I->getKey());
+      Consumer->FoundName(I->getKey());
 
     // Walk through identifiers in external identifier sources.
     // FIXME: Re-add the ability to skip very unlikely potential corrections.
@@ -4182,24 +4136,12 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
         if (Name.empty())
           break;
 
-        Consumer.FoundName(Name);
+        Consumer->FoundName(Name);
       } while (true);
     }
   }
 
-  AddKeywordsToConsumer(*this, Consumer, S, CCCRef, SS && SS->isNotEmpty());
-
-  // If we haven't found anything, we're done.
-  if (Consumer.empty())
-    return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure,
-                            IsUnqualifiedLookup);
-
-  // Make sure the best edit distance (prior to adding any namespace qualifiers)
-  // is not more that about a third of the length of the typo's identifier.
-  unsigned ED = Consumer.getBestEditDistance(true);
-  if (ED > 0 && TypoLen / ED < 3)
-    return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure,
-                            IsUnqualifiedLookup);
+  AddKeywordsToConsumer(*this, *Consumer, S, CCCRef, SS && SS->isNotEmpty());
 
   // Build the NestedNameSpecifiers for the KnownNamespaces, if we're going
   // to search those namespaces.
@@ -4213,17 +4155,101 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
         KnownNamespaces[ExternalKnownNamespaces[I]] = true;
     }
 
-    Consumer.addNamespaces(KnownNamespaces);
+    Consumer->addNamespaces(KnownNamespaces);
   }
 
-  TypoCorrection BestTC = Consumer.getNextCorrection();
-  TypoCorrection SecondBestTC = Consumer.getNextCorrection();
+  return Consumer;
+}
+
+/// \brief Try to "correct" a typo in the source code by finding
+/// visible declarations whose names are similar to the name that was
+/// present in the source code.
+///
+/// \param TypoName the \c DeclarationNameInfo structure that contains
+/// the name that was present in the source code along with its location.
+///
+/// \param LookupKind the name-lookup criteria used to search for the name.
+///
+/// \param S the scope in which name lookup occurs.
+///
+/// \param SS the nested-name-specifier that precedes the name we're
+/// looking for, if present.
+///
+/// \param CCC A CorrectionCandidateCallback object that provides further
+/// validation of typo correction candidates. It also provides flags for
+/// determining the set of keywords permitted.
+///
+/// \param MemberContext if non-NULL, the context in which to look for
+/// a member access expression.
+///
+/// \param EnteringContext whether we're entering the context described by
+/// the nested-name-specifier SS.
+///
+/// \param OPT when non-NULL, the search for visible declarations will
+/// also walk the protocols in the qualified interfaces of \p OPT.
+///
+/// \returns a \c TypoCorrection containing the corrected name if the typo
+/// along with information such as the \c NamedDecl where the corrected name
+/// was declared, and any additional \c NestedNameSpecifier needed to access
+/// it (C++ only). The \c TypoCorrection is empty if there is no correction.
+TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
+                                 Sema::LookupNameKind LookupKind,
+                                 Scope *S, CXXScopeSpec *SS,
+                                 std::unique_ptr<CorrectionCandidateCallback> CCC,
+                                 CorrectTypoKind Mode,
+                                 DeclContext *MemberContext,
+                                 bool EnteringContext,
+                                 const ObjCObjectPointerType *OPT,
+                                 bool RecordFailure) {
+  assert(CCC && "CorrectTypo requires a CorrectionCandidateCallback");
+
+  // Always let the ExternalSource have the first chance at correction, even
+  // if we would otherwise have given up.
+  if (ExternalSource) {
+    if (TypoCorrection Correction = ExternalSource->CorrectTypo(
+        TypoName, LookupKind, S, SS, *CCC, MemberContext, EnteringContext, OPT))
+      return Correction;
+  }
+
+  // Ugly hack equivalent to CTC == CTC_ObjCMessageReceiver;
+  // WantObjCSuper is only true for CTC_ObjCMessageReceiver and for
+  // some instances of CTC_Unknown, while WantRemainingKeywords is true
+  // for CTC_Unknown but not for CTC_ObjCMessageReceiver.
+  bool ObjCMessageReceiver = CCC->WantObjCSuper && !CCC->WantRemainingKeywords;
+
+  TypoCorrection EmptyCorrection;
+  bool ValidatingCallback = !isCandidateViable(*CCC, EmptyCorrection);
+
+  IdentifierInfo *Typo = TypoName.getName().getAsIdentifierInfo();
+  bool IsUnqualifiedLookup = false;
+  auto Consumer = makeTypoCorrectionConsumer(
+      TypoName, LookupKind, S, SS, std::move(CCC), MemberContext,
+      EnteringContext, OPT, Mode == CTK_ErrorRecovery, IsUnqualifiedLookup);
+
+  if (!Consumer)
+    return TypoCorrection();
+
+  // If we haven't found anything, we're done.
+  if (Consumer->empty())
+    return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure,
+                            IsUnqualifiedLookup);
+
+  // Make sure the best edit distance (prior to adding any namespace qualifiers)
+  // is not more that about a third of the length of the typo's identifier.
+  unsigned ED = Consumer->getBestEditDistance(true);
+  unsigned TypoLen = Typo->getName().size();
+  if (ED > 0 && TypoLen / ED < 3)
+    return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure,
+                            IsUnqualifiedLookup);
+
+  TypoCorrection BestTC = Consumer->getNextCorrection();
+  TypoCorrection SecondBestTC = Consumer->getNextCorrection();
   if (!BestTC)
     return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
 
   ED = BestTC.getEditDistance();
 
-  if (!AllowOnlyNNSChanges && ED > 0 && TypoLen / ED < 3) {
+  if (TypoLen >= 3 && ED > 0 && TypoLen / ED < 3) {
     // If this was an unqualified lookup and we believe the callback
     // object wouldn't have filtered out possible corrections, note
     // that no correction was found.
@@ -4249,21 +4275,15 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
     TC.setCorrectionRange(SS, TypoName);
     checkCorrectionVisibility(*this, TC);
     return TC;
-  }
-  // Ugly hack equivalent to CTC == CTC_ObjCMessageReceiver;
-  // WantObjCSuper is only true for CTC_ObjCMessageReceiver and for
-  // some instances of CTC_Unknown, while WantRemainingKeywords is true
-  // for CTC_Unknown but not for CTC_ObjCMessageReceiver.
-  else if (SecondBestTC && CCCRef.WantObjCSuper &&
-           !CCCRef.WantRemainingKeywords) {
+  } else if (SecondBestTC && ObjCMessageReceiver) {
     // Prefer 'super' when we're completing in a message-receiver
     // context.
 
     if (BestTC.getCorrection().getAsString() != "super") {
       if (SecondBestTC.getCorrection().getAsString() == "super")
         BestTC = SecondBestTC;
-      else if (Consumer["super"].front().isKeyword())
-        BestTC = Consumer["super"].front();
+      else if ((*Consumer)["super"].front().isKeyword())
+        BestTC = (*Consumer)["super"].front();
     }
     // Don't correct to a keyword that's the same as the typo; the keyword
     // wasn't actually in scope.
@@ -4286,6 +4306,69 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
                           IsUnqualifiedLookup && !ValidatingCallback);
 }
 
+/// \brief Try to "correct" a typo in the source code by finding
+/// visible declarations whose names are similar to the name that was
+/// present in the source code.
+///
+/// \param TypoName the \c DeclarationNameInfo structure that contains
+/// the name that was present in the source code along with its location.
+///
+/// \param LookupKind the name-lookup criteria used to search for the name.
+///
+/// \param S the scope in which name lookup occurs.
+///
+/// \param SS the nested-name-specifier that precedes the name we're
+/// looking for, if present.
+///
+/// \param CCC A CorrectionCandidateCallback object that provides further
+/// validation of typo correction candidates. It also provides flags for
+/// determining the set of keywords permitted.
+///
+/// \param TDG A TypoDiagnosticGenerator object that will be used to print
+/// diagnostics when the actual typo correction is attempted.
+///
+/// \param MemberContext if non-NULL, the context in which to look for
+/// a member access expression.
+///
+/// \param EnteringContext whether we're entering the context described by
+/// the nested-name-specifier SS.
+///
+/// \param OPT when non-NULL, the search for visible declarations will
+/// also walk the protocols in the qualified interfaces of \p OPT.
+///
+/// \returns a new \c TypoExpr that will later be replaced in the AST with an
+/// Expr representing the result of performing typo correction.
+TypoExpr *Sema::CorrectTypoDelayed(
+    const DeclarationNameInfo &TypoName, Sema::LookupNameKind LookupKind,
+    Scope *S, CXXScopeSpec *SS,
+    std::unique_ptr<CorrectionCandidateCallback> CCC,
+    std::unique_ptr<TypoDiagnosticGenerator> TDG, CorrectTypoKind Mode,
+    DeclContext *MemberContext, bool EnteringContext,
+    const ObjCObjectPointerType *OPT) {
+  assert(CCC && "CorrectTypoDelayed requires a CorrectionCandidateCallback");
+
+  bool IsUnqualifiedLookup = false;
+  auto Consumer = makeTypoCorrectionConsumer(
+      TypoName, LookupKind, S, SS, std::move(CCC), MemberContext,
+      EnteringContext, OPT,
+      /*SearchModules=*/(Mode == CTK_ErrorRecovery) && getLangOpts().Modules &&
+          getLangOpts().ModulesSearchAll,
+      IsUnqualifiedLookup);
+
+  if (!Consumer || Consumer->empty())
+    return nullptr;
+
+  // Make sure the best edit distance (prior to adding any namespace qualifiers)
+  // 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)
+    return nullptr;
+
+  ExprEvalContexts.back().NumTypos++;
+  return new (Context) TypoExpr(std::move(Consumer), std::move(TDG));
+}
+
 void TypoCorrection::addCorrectionDecl(NamedDecl *CDecl) {
   if (!CDecl) return;
 
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to