Part of the infrastructure is a map from a TypoExpr to the Sema-specific
state needed to correct it, along with helpers to ease dealing with the
state.

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           |  44 +++++
 include/clang/Sema/SemaInternal.h   |  15 +-
 include/clang/Sema/TypoCorrection.h |   2 +-
 lib/Sema/SemaExpr.cpp               |   7 +
 lib/Sema/SemaExprCXX.cpp            | 108 ++++++++++++
 lib/Sema/SemaLookup.cpp             | 316 ++++++++++++++++++++++++------------
 6 files changed, 384 insertions(+), 108 deletions(-)

diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index fb5ff35..74d5cde 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -169,6 +169,8 @@ namespace clang {
   class TypedefDecl;
   class TypedefNameDecl;
   class TypeLoc;
+  class TypoCorrectionConsumer;
+  class TypoDiagnosticGenerator;
   class UnqualifiedId;
   class UnresolvedLookupExpr;
   class UnresolvedMemberExpr;
@@ -745,6 +747,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
@@ -778,6 +784,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
@@ -2563,6 +2570,18 @@ public:
 private:
   bool CppLookupName(LookupResult &R, Scope *S);
 
+  struct TypoExprState {
+    std::unique_ptr<TypoCorrectionConsumer> Consumer;
+    std::unique_ptr<TypoDiagnosticGenerator> DiagHandler;
+  };
+
+  /// \brief The set of unhandled TypoExprs and their associated state.
+  llvm::MapVector<TypoExpr *, TypoExprState> DelayedTypos;
+
+  /// \brief Creates a new TypoExpr AST node.
+  TypoExpr *createDelayedTypo(std::unique_ptr<TypoCorrectionConsumer> TCC,
+                              std::unique_ptr<TypoDiagnosticGenerator> TDG);
+
   // \brief The set of known/encountered (unique, canonicalized) NamespaceDecls.
   //
   // The boolean value will be true to indicate that the namespace was loaded
@@ -2573,7 +2592,24 @@ 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:
+  const TypoExprState &getTypoExprState(TypoExpr *TE);
+
+  /// \brief Clears the state of the given TypoExpr.
+  void clearDelayedTypo(TypoExpr *TE);
+
   /// \brief Look up a name, looking for a single declaration.  Return
   /// null if the results were absent, ambiguous, or overloaded.
   ///
@@ -2650,6 +2686,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 f40d331..513cbf1 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/include/clang/Sema/TypoCorrection.h b/include/clang/Sema/TypoCorrection.h
index d78d99e..abe3827 100644
--- a/include/clang/Sema/TypoCorrection.h
+++ b/include/clang/Sema/TypoCorrection.h
@@ -338,7 +338,7 @@ public:
 
 class TypoDiagnosticGenerator {
  public:
-  virtual void operator()(TypoCorrection &TC) = 0;
+  virtual void operator()(const TypoCorrection &TC) = 0;
   virtual ~TypoDiagnosticGenerator() = default;
 };
 }
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 29d800d..10ac00a 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -11299,6 +11299,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) {
@@ -11351,6 +11352,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 425297c..4d2b727 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"
@@ -5906,6 +5907,100 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(
   CurrentLSI->clearPotentialCaptures();
 }
 
+namespace {
+class TransformTypos : public TreeTransform<TransformTypos> {
+  typedef TreeTransform<TransformTypos> BaseTransform;
+
+  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 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.
+      auto &FTEState = SemaRef.getTypoExprState(FirstTypoExpr);
+      if (FTEState.Consumer->finished()) {
+        unsigned numFinished = 1;
+        for (auto TE : TypoExprs) {
+          if (TE != FirstTypoExpr) {
+            auto &State = SemaRef.getTypoExprState(TE);
+            TransformCache.erase(TE);
+            if (State.Consumer->finished()) {
+              ++numFinished;
+              State.Consumer->resetCorrectionStream();
+            } else {
+              break;
+            }
+          }
+        }
+        FTEState.Consumer->resetCorrectionStream();
+        if (numFinished >= TypoExprs.size())
+          break;
+      }
+    };
+
+    // Emit diagnostics for all of the TypoExprs encountered.
+    for (auto E : TypoExprs) {
+      TypoExpr *TE = cast<TypoExpr>(E);
+      auto &State = SemaRef.getTypoExprState(TE);
+      if (State.DiagHandler)
+        (*State.DiagHandler)(State.Consumer->getCurrentCorrection());
+      SemaRef.clearDelayedTypo(TE);
+    }
+
+    return res;
+  }
+
+  ExprResult TransformTypoExpr(TypoExpr *E) {
+    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];
+    }
+
+    auto &State = SemaRef.getTypoExprState(E);
+    assert(State.Consumer && "Cannot transform a cleared TypoExpr");
+
+    // For the first TypoExpr and an uncached TypoExpr, find the next likely
+    // typo correction and return it.
+    while (TypoCorrection TC = State.Consumer->getNextCorrection()) {
+      LookupResult R(SemaRef,
+                     State.Consumer->getLookupResult().getLookupNameInfo(),
+                     State.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,
@@ -5953,6 +6048,19 @@ ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC,
       return ExprError();
   }
 
+  if (ExprEvalContexts.back().NumTypos &&
+      (FullExpr.get()->isTypeDependent() ||
+       FullExpr.get()->isValueDependent() ||
+       FullExpr.get()->isInstantiationDependent())) {
+    auto TyposResolved = DelayedTypos.size();
+    FullExpr = TransformTypos(*this).Transform(FullExpr.get());
+    TyposResolved -= DelayedTypos.size();
+    if (TyposResolved)
+      ExprEvalContexts.back().NumTypos -= TyposResolved;
+    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 e04c8c6..acbee34 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -3459,7 +3459,7 @@ const 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:
@@ -3479,7 +3479,7 @@ retry_lookup:
     }
     if (TempMemberContext) {
       if (SS && !TempSS)
-        TempSS = SS;
+        TempSS = SS.get();
       TempMemberContext = nullptr;
       goto retry_lookup;
     }
@@ -3972,101 +3972,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(),
@@ -4074,9 +4032,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.
@@ -4084,29 +4042,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
@@ -4123,13 +4081,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()) {
@@ -4137,7 +4095,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;
     }
   }
 
@@ -4146,17 +4104,13 @@ 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
     // seen in this translation unit.
     // FIXME: Re-add the ability to skip very unlikely potential corrections.
     for (const auto &I : Context.Idents)
-      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.
@@ -4168,24 +4122,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.
@@ -4199,17 +4141,101 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
         KnownNamespaces[N] = true;
     }
 
-    Consumer.addNamespaces(KnownNamespaces);
+    Consumer->addNamespaces(KnownNamespaces);
+  }
+
+  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;
   }
 
-  TypoCorrection BestTC = Consumer.getNextCorrection();
-  TypoCorrection SecondBestTC = Consumer.getNextCorrection();
+  // 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.
@@ -4235,21 +4261,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.
@@ -4272,6 +4292,73 @@ 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, or nullptr if
+/// typo correction is not possible. If nullptr is returned, no diagnostics will
+/// be emitted and it is the responsibility of the caller to emit any that are
+/// needed.
+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");
+
+  TypoCorrection Empty;
+  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 createDelayedTypo(std::move(Consumer), std::move(TDG));
+}
+
 void TypoCorrection::addCorrectionDecl(NamedDecl *CDecl) {
   if (!CDecl) return;
 
@@ -4468,3 +4555,24 @@ void Sema::diagnoseTypo(const TypoCorrection &Correction,
     Diag(ChosenDecl->getLocation(), PrevNote)
       << CorrectedQuotedStr << (ErrorRecovery ? FixItHint() : FixTypo);
 }
+
+TypoExpr *
+Sema::createDelayedTypo(std::unique_ptr<TypoCorrectionConsumer> TCC,
+                        std::unique_ptr<TypoDiagnosticGenerator> TDG) {
+  assert(TCC && "createDelayedTypo requires a valid TypoCorrectionConsumer");
+  auto TE = new (Context) TypoExpr(Context.DependentTy);
+  auto &State = DelayedTypos[TE];
+  State.Consumer = std::move(TCC);
+  State.DiagHandler = std::move(TDG);
+  return TE;
+}
+
+const Sema::TypoExprState &Sema::getTypoExprState(TypoExpr *TE) {
+  return DelayedTypos[TE];
+}
+
+void Sema::clearDelayedTypo(TypoExpr *TE) {
+  auto I = DelayedTypos.find(TE);
+  if (I != DelayedTypos.end())
+    DelayedTypos.erase(I);
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to