r210669 - Move TypoCorrection filtering into the TypoCorrectionConsumer and make it incremental.

Kaelyn Takata rikka at google.com
Wed Jun 11 11:07:01 PDT 2014


Author: rikka
Date: Wed Jun 11 13:07:01 2014
New Revision: 210669

URL: http://llvm.org/viewvc/llvm-project?rev=210669&view=rev
Log:
Move TypoCorrection filtering into the TypoCorrectionConsumer and make it incremental.

The only external/visible functional change that fell out of this
refactoring is that there was one less case where the typo caching
and/or counting didn't work properly. The result is that a test case
had to be moved from typo-correction.cpp to typo-correction-pt2.cpp
to avoid the hard-coded limit on per file/TU typo correction attempts.

Modified:
    cfe/trunk/lib/Sema/SemaLookup.cpp
    cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp
    cfe/trunk/test/SemaCXX/typo-correction.cpp

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=210669&r1=210668&r2=210669&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Wed Jun 11 13:07:01 2014
@@ -3403,7 +3403,7 @@ static const unsigned MaxTypoDistanceRes
 
 class TypoCorrectionConsumer : public VisibleDeclConsumer {
   /// \brief The name written that is a typo in the source.
-  StringRef Typo;
+  IdentifierInfo *Typo;
 
   /// \brief The results found that have the smallest edit distance
   /// found (so far) with the typo name.
@@ -3413,11 +3413,31 @@ class TypoCorrectionConsumer : public Vi
   TypoEditDistanceMap CorrectionResults;
 
   Sema &SemaRef;
+  Scope *S;
+  CXXScopeSpec *SS;
+  CorrectionCandidateCallback &CorrectionValidator;
+  DeclContext *MemberContext;
+  LookupResult Result;
+  NamespaceSpecifierSet Namespaces;
+  bool EnteringContext;
+  bool SearchNamespaces = false;
 
 public:
-  explicit TypoCorrectionConsumer(Sema &SemaRef, IdentifierInfo *Typo)
-    : Typo(Typo->getName()),
-      SemaRef(SemaRef) {}
+  SmallVector<TypoCorrection, 16> QualifiedResults;
+  explicit TypoCorrectionConsumer(Sema &SemaRef,
+                                  const DeclarationNameInfo &TypoName,
+                                  Sema::LookupNameKind LookupKind,
+                                  Scope *S, CXXScopeSpec *SS,
+                                  CorrectionCandidateCallback &CCC,
+                                  DeclContext *MemberContext,
+                                  bool EnteringContext)
+      : Typo(TypoName.getName().getAsIdentifierInfo()), SemaRef(SemaRef), S(S),
+        SS(SS), CorrectionValidator(CCC), MemberContext(MemberContext),
+        Result(SemaRef, TypoName, LookupKind),
+        Namespaces(SemaRef.Context, SemaRef.CurContext, SS),
+        EnteringContext(EnteringContext) {
+    Result.suppressDiagnostics();
+  }
 
   bool includeHiddenDecls() const override { return true; }
 
@@ -3425,8 +3445,6 @@ public:
                  bool InBaseClass) override;
   void FoundName(StringRef Name);
   void addKeywordResult(StringRef Keyword);
-  void addName(StringRef Name, NamedDecl *ND,
-               NestedNameSpecifier *NNS = nullptr, bool isKeyword = false);
   void addCorrection(TypoCorrection Correction);
 
   typedef TypoResultsMap::iterator result_iterator;
@@ -3453,6 +3471,18 @@ public:
     return CorrectionResults.begin()->second;
   }
 
+  void
+  addNamespaces(const llvm::MapVector<NamespaceDecl *, bool> &KnownNamespaces);
+
+  TypoCorrection getNextCorrection();
+
+private:
+  void addName(StringRef Name, NamedDecl *ND,
+               NestedNameSpecifier *NNS = nullptr, bool isKeyword = false);
+
+  bool resolveCorrection(TypoCorrection &Candidate);
+
+  void performQualifiedLookups();
 };
 
 }
@@ -3472,7 +3502,7 @@ void TypoCorrectionConsumer::FoundDecl(N
 
   // Only consider visible declarations and declarations from modules with
   // names that exactly match.
-  if (!LookupResult::isVisible(SemaRef, ND) && Name->getName() != Typo &&
+  if (!LookupResult::isVisible(SemaRef, ND) && Name != Typo &&
       !findAcceptableDecl(SemaRef, ND))
     return;
 
@@ -3495,14 +3525,15 @@ void TypoCorrectionConsumer::addName(Str
                                      NestedNameSpecifier *NNS, bool isKeyword) {
   // Use a simple length-based heuristic to determine the minimum possible
   // edit distance. If the minimum isn't good enough, bail out early.
-  unsigned MinED = abs((int)Name.size() - (int)Typo.size());
-  if (MinED && Typo.size() / MinED < 3)
+  StringRef TypoStr = Typo->getName();
+  unsigned MinED = abs((int)Name.size() - (int)TypoStr.size());
+  if (MinED && TypoStr.size() / MinED < 3)
     return;
 
   // Compute an upper bound on the allowable edit distance, so that the
   // edit-distance algorithm can short-circuit.
-  unsigned UpperBound = (Typo.size() + 2) / 3 + 1;
-  unsigned ED = Typo.edit_distance(Name, true, UpperBound);
+  unsigned UpperBound = (TypoStr.size() + 2) / 3 + 1;
+  unsigned ED = TypoStr.edit_distance(Name, true, UpperBound);
   if (ED >= UpperBound) return;
 
   TypoCorrection TC(&SemaRef.Context.Idents.get(Name), ND, NNS, ED);
@@ -3511,7 +3542,21 @@ void TypoCorrectionConsumer::addName(Str
 }
 
 void TypoCorrectionConsumer::addCorrection(TypoCorrection Correction) {
+  StringRef TypoStr = Typo->getName();
   StringRef Name = Correction.getCorrectionAsIdentifierInfo()->getName();
+
+  // For very short typos, ignore potential corrections that have a different
+  // base identifier from the typo or which have a normalized edit distance
+  // longer than the typo itself.
+  if (TypoStr.size() < 3 &&
+      (Name != TypoStr || Correction.getEditDistance(true) > TypoStr.size()))
+    return;
+
+  // If the correction is resolved but is not viable, ignore it.
+  if (Correction.isResolved() &&
+      !isCandidateViable(CorrectionValidator, Correction))
+    return;
+
   TypoResultList &CList =
       CorrectionResults[Correction.getEditDistance(false)][Name];
 
@@ -3536,7 +3581,177 @@ void TypoCorrectionConsumer::addCorrecti
     CList.push_back(Correction);
 
   while (CorrectionResults.size() > MaxTypoDistanceResultSets)
-    erase(std::prev(CorrectionResults.end()));
+    CorrectionResults.erase(std::prev(CorrectionResults.end()));
+}
+
+void TypoCorrectionConsumer::addNamespaces(
+    const llvm::MapVector<NamespaceDecl *, bool> &KnownNamespaces) {
+  SearchNamespaces = true;
+
+  for (auto KNPair : KnownNamespaces)
+    Namespaces.addNameSpecifier(KNPair.first);
+
+  bool SSIsTemplate = false;
+  if (NestedNameSpecifier *NNS =
+          (SS && SS->isValid()) ? SS->getScopeRep() : nullptr) {
+    if (const Type *T = NNS->getAsType())
+      SSIsTemplate = T->getTypeClass() == Type::TemplateSpecialization;
+  }
+  for (const auto *TI : SemaRef.getASTContext().types()) {
+    if (CXXRecordDecl *CD = TI->getAsCXXRecordDecl()) {
+      CD = CD->getCanonicalDecl();
+      if (!CD->isDependentType() && !CD->isAnonymousStructOrUnion() &&
+          !CD->isUnion() && CD->getIdentifier() &&
+          (SSIsTemplate || !isa<ClassTemplateSpecializationDecl>(CD)) &&
+          (CD->isBeingDefined() || CD->isCompleteDefinition()))
+        Namespaces.addNameSpecifier(CD);
+    }
+  }
+}
+
+TypoCorrection TypoCorrectionConsumer::getNextCorrection() {
+  while (!CorrectionResults.empty()) {
+    auto DI = CorrectionResults.begin();
+    if (DI->second.empty()) {
+      CorrectionResults.erase(DI);
+      continue;
+    }
+
+    auto RI = DI->second.begin();
+    if (RI->second.empty()) {
+      DI->second.erase(RI);
+      performQualifiedLookups();
+      continue;
+    }
+
+    TypoCorrection TC = RI->second.pop_back_val();
+    if (TC.isResolved() || resolveCorrection(TC))
+      return TC;
+  }
+  return TypoCorrection();
+}
+
+bool TypoCorrectionConsumer::resolveCorrection(TypoCorrection &Candidate) {
+  IdentifierInfo *Name = Candidate.getCorrectionAsIdentifierInfo();
+  DeclContext *TempMemberContext = MemberContext;
+  CXXScopeSpec *TempSS = SS;
+retry_lookup:
+  LookupPotentialTypoResult(SemaRef, Result, Name, S, TempSS, TempMemberContext,
+                            EnteringContext,
+                            CorrectionValidator.IsObjCIvarLookup,
+                            Name == Typo && !Candidate.WillReplaceSpecifier());
+  switch (Result.getResultKind()) {
+  case LookupResult::NotFound:
+  case LookupResult::NotFoundInCurrentInstantiation:
+  case LookupResult::FoundUnresolvedValue:
+    if (TempSS) {
+      // Immediately retry the lookup without the given CXXScopeSpec
+      TempSS = nullptr;
+      Candidate.WillReplaceSpecifier(true);
+      goto retry_lookup;
+    }
+    if (TempMemberContext) {
+      if (SS && !TempSS)
+        TempSS = SS;
+      TempMemberContext = nullptr;
+      goto retry_lookup;
+    }
+    if (SearchNamespaces)
+      QualifiedResults.push_back(Candidate);
+    break;
+
+  case LookupResult::Ambiguous:
+    // We don't deal with ambiguities.
+    break;
+
+  case LookupResult::Found:
+  case LookupResult::FoundOverloaded:
+    // Store all of the Decls for overloaded symbols
+    for (auto *TRD : Result)
+      Candidate.addCorrectionDecl(TRD);
+    if (!isCandidateViable(CorrectionValidator, Candidate)) {
+      if (SearchNamespaces)
+        QualifiedResults.push_back(Candidate);
+      break;
+    }
+    return true;
+  }
+  return false;
+}
+
+void TypoCorrectionConsumer::performQualifiedLookups() {
+  unsigned TypoLen = Typo->getName().size();
+  for (auto QR : QualifiedResults) {
+    for (auto NSI : Namespaces) {
+      DeclContext *Ctx = NSI.DeclCtx;
+      const Type *NSType = NSI.NameSpecifier->getAsType();
+
+      // If the current NestedNameSpecifier refers to a class and the
+      // current correction candidate is the name of that class, then skip
+      // it as it is unlikely a qualified version of the class' constructor
+      // is an appropriate correction.
+      if (CXXRecordDecl *NSDecl = NSType ? NSType->getAsCXXRecordDecl() : 0) {
+        if (NSDecl->getIdentifier() == QR.getCorrectionAsIdentifierInfo())
+          continue;
+      }
+
+      TypoCorrection TC(QR);
+      TC.ClearCorrectionDecls();
+      TC.setCorrectionSpecifier(NSI.NameSpecifier);
+      TC.setQualifierDistance(NSI.EditDistance);
+      TC.setCallbackDistance(0); // Reset the callback distance
+
+      // If the current correction candidate and namespace combination are
+      // too far away from the original typo based on the normalized edit
+      // distance, then skip performing a qualified name lookup.
+      unsigned TmpED = TC.getEditDistance(true);
+      if (QR.getCorrectionAsIdentifierInfo() != Typo && TmpED &&
+          TypoLen / TmpED < 3)
+        continue;
+
+      Result.clear();
+      Result.setLookupName(QR.getCorrectionAsIdentifierInfo());
+      if (!SemaRef.LookupQualifiedName(Result, Ctx))
+        continue;
+
+      // Any corrections added below will be validated in subsequent
+      // iterations of the main while() loop over the Consumer's contents.
+      switch (Result.getResultKind()) {
+      case LookupResult::Found:
+      case LookupResult::FoundOverloaded: {
+        if (SS && SS->isValid()) {
+          std::string NewQualified = TC.getAsString(SemaRef.getLangOpts());
+          std::string OldQualified;
+          llvm::raw_string_ostream OldOStream(OldQualified);
+          SS->getScopeRep()->print(OldOStream, SemaRef.getPrintingPolicy());
+          OldOStream << Typo->getName();
+          // If correction candidate would be an identical written qualified
+          // identifer, then the existing CXXScopeSpec probably included a
+          // typedef that didn't get accounted for properly.
+          if (OldOStream.str() == NewQualified)
+            break;
+        }
+        for (LookupResult::iterator TRD = Result.begin(), TRDEnd = Result.end();
+             TRD != TRDEnd; ++TRD) {
+          if (SemaRef.CheckMemberAccess(TC.getCorrectionRange().getBegin(),
+                                        NSType ? NSType->getAsCXXRecordDecl()
+                                               : nullptr,
+                                        TRD.getPair()) == Sema::AR_accessible)
+            TC.addCorrectionDecl(*TRD);
+        }
+        if (TC.isResolved())
+          addCorrection(TC);
+        break;
+      }
+      case LookupResult::NotFound:
+      case LookupResult::NotFoundInCurrentInstantiation:
+      case LookupResult::Ambiguous:
+      case LookupResult::FoundUnresolvedValue:
+        break;
+      }
+    }
+  }
+  QualifiedResults.clear();
 }
 
 DeclContextList NamespaceSpecifierSet::buildContextChain(DeclContext *Start) {
@@ -3985,8 +4200,6 @@ TypoCorrection Sema::CorrectTypo(const D
   if (getLangOpts().AltiVec && Typo->isStr("vector"))
     return TypoCorrection();
 
-  TypoCorrectionConsumer Consumer(*this, Typo);
-
   // 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 &&
@@ -3996,7 +4209,8 @@ TypoCorrection Sema::CorrectTypo(const D
                                            TypoName.getLocStart());
   }
 
-  NamespaceSpecifierSet Namespaces(Context, CurContext, SS);
+  TypoCorrectionConsumer Consumer(*this, TypoName, LookupKind, S, SS, 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.
@@ -4121,236 +4335,16 @@ TypoCorrection Sema::CorrectTypo(const D
         KnownNamespaces[ExternalKnownNamespaces[I]] = true;
     }
 
-    for (auto KNPair : KnownNamespaces)
-      Namespaces.addNameSpecifier(KNPair.first);
-
-    bool SSIsTemplate = false;
-    if (NestedNameSpecifier *NNS =
-            (SS && SS->isValid()) ? SS->getScopeRep() : nullptr) {
-      if (const Type *T = NNS->getAsType())
-        SSIsTemplate = T->getTypeClass() == Type::TemplateSpecialization;
-    }
-    for (const auto *TI : Context.types()) {
-      if (CXXRecordDecl *CD = TI->getAsCXXRecordDecl()) {
-        CD = CD->getCanonicalDecl();
-        if (!CD->isDependentType() && !CD->isAnonymousStructOrUnion() &&
-            !CD->isUnion() && CD->getIdentifier() &&
-            (SSIsTemplate || !isa<ClassTemplateSpecializationDecl>(CD)) &&
-            (CD->isBeingDefined() || CD->isCompleteDefinition()))
-          Namespaces.addNameSpecifier(CD);
-      }
-    }
+    Consumer.addNamespaces(KnownNamespaces);
   }
 
-  // Weed out any names that could not be found by name lookup or, if a
-  // CorrectionCandidateCallback object was provided, failed validation.
-  SmallVector<TypoCorrection, 16> QualifiedResults;
-  LookupResult TmpRes(*this, TypoName, LookupKind);
-  TmpRes.suppressDiagnostics();
-  while (!Consumer.empty()) {
-    TypoCorrectionConsumer::distance_iterator DI = Consumer.begin();
-    for (TypoCorrectionConsumer::result_iterator I = DI->second.begin(),
-                                              IEnd = DI->second.end();
-         I != IEnd; /* Increment in loop. */) {
-      // If we only want nested name specifier corrections, ignore potential
-      // corrections that have a different base identifier from the typo or
-      // which have a normalized edit distance longer than the typo itself.
-      if (AllowOnlyNNSChanges) {
-        TypoCorrection &TC = I->second.front();
-        if (TC.getCorrectionAsIdentifierInfo() != Typo ||
-            TC.getEditDistance(true) > TypoLen) {
-          TypoCorrectionConsumer::result_iterator Prev = I;
-          ++I;
-          DI->second.erase(Prev);
-          continue;
-        }
-      }
-
-      // If the item already has been looked up or is a keyword, keep it.
-      // If a validator callback object was given, drop the correction
-      // unless it passes validation.
-      bool Viable = false;
-      for (TypoResultList::iterator RI = I->second.begin();
-           RI != I->second.end(); /* Increment in loop. */) {
-        TypoResultList::iterator Prev = RI;
-        ++RI;
-        if (Prev->isResolved()) {
-          if (!isCandidateViable(CCC, *Prev))
-            RI = I->second.erase(Prev);
-          else
-            Viable = true;
-        }
-      }
-      if (Viable || I->second.empty()) {
-        TypoCorrectionConsumer::result_iterator Prev = I;
-        ++I;
-        if (!Viable)
-          DI->second.erase(Prev);
-        continue;
-      }
-      assert(I->second.size() == 1 && "Expected a single unresolved candidate");
-
-      // Perform name lookup on this name.
-      TypoCorrection &Candidate = I->second.front();
-      IdentifierInfo *Name = Candidate.getCorrectionAsIdentifierInfo();
-      DeclContext *TempMemberContext = MemberContext;
-      CXXScopeSpec *TempSS = SS;
-retry_lookup:
-      LookupPotentialTypoResult(*this, TmpRes, Name, S, TempSS,
-                                TempMemberContext, EnteringContext,
-                                CCC.IsObjCIvarLookup,
-                                Name == TypoName.getName() &&
-                                  !Candidate.WillReplaceSpecifier());
-
-      switch (TmpRes.getResultKind()) {
-      case LookupResult::NotFound:
-      case LookupResult::NotFoundInCurrentInstantiation:
-      case LookupResult::FoundUnresolvedValue:
-        if (TempSS) {
-          // Immediately retry the lookup without the given CXXScopeSpec
-          TempSS = nullptr;
-          Candidate.WillReplaceSpecifier(true);
-          goto retry_lookup;
-        }
-        if (TempMemberContext) {
-          if (SS && !TempSS)
-            TempSS = SS;
-          TempMemberContext = nullptr;
-          goto retry_lookup;
-        }
-        QualifiedResults.push_back(Candidate);
-        // We didn't find this name in our scope, or didn't like what we found;
-        // ignore it.
-        {
-          TypoCorrectionConsumer::result_iterator Next = I;
-          ++Next;
-          DI->second.erase(I);
-          I = Next;
-        }
-        break;
-
-      case LookupResult::Ambiguous:
-        // We don't deal with ambiguities.
-        return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
-
-      case LookupResult::FoundOverloaded: {
-        TypoCorrectionConsumer::result_iterator Prev = I;
-        // Store all of the Decls for overloaded symbols
-        for (auto *TRD : TmpRes)
-          Candidate.addCorrectionDecl(TRD);
-        ++I;
-        if (!isCandidateViable(CCC, Candidate)) {
-          QualifiedResults.push_back(Candidate);
-          DI->second.erase(Prev);
-        }
-        break;
-      }
-
-      case LookupResult::Found: {
-        TypoCorrectionConsumer::result_iterator Prev = I;
-        Candidate.setCorrectionDecl(TmpRes.getAsSingle<NamedDecl>());
-        ++I;
-        if (!isCandidateViable(CCC, Candidate)) {
-          QualifiedResults.push_back(Candidate);
-          DI->second.erase(Prev);
-        }
-        break;
-      }
-
-      }
-    }
-
-    if (DI->second.empty())
-      Consumer.erase(DI);
-    else if (!getLangOpts().CPlusPlus || QualifiedResults.empty() || !DI->first)
-      // If there are results in the closest possible bucket, stop
-      break;
-
-    // Only perform the qualified lookups for C++
-    if (SearchNamespaces) {
-      TmpRes.suppressDiagnostics();
-      for (auto QR : QualifiedResults) {
-        for (auto NSI : Namespaces) {
-          DeclContext *Ctx = NSI.DeclCtx;
-          const Type *NSType = NSI.NameSpecifier->getAsType();
-
-          // If the current NestedNameSpecifier refers to a class and the
-          // current correction candidate is the name of that class, then skip
-          // it as it is unlikely a qualified version of the class' constructor
-          // is an appropriate correction.
-          if (CXXRecordDecl *NSDecl =
-                  NSType ? NSType->getAsCXXRecordDecl() : nullptr) {
-            if (NSDecl->getIdentifier() == QR.getCorrectionAsIdentifierInfo())
-              continue;
-          }
-
-          TypoCorrection TC(QR);
-          TC.ClearCorrectionDecls();
-          TC.setCorrectionSpecifier(NSI.NameSpecifier);
-          TC.setQualifierDistance(NSI.EditDistance);
-          TC.setCallbackDistance(0); // Reset the callback distance
-
-          // If the current correction candidate and namespace combination are
-          // too far away from the original typo based on the normalized edit
-          // distance, then skip performing a qualified name lookup.
-          unsigned TmpED = TC.getEditDistance(true);
-          if (QR.getCorrectionAsIdentifierInfo() != Typo &&
-              TmpED && TypoLen / TmpED < 3)
-            continue;
-
-          TmpRes.clear();
-          TmpRes.setLookupName(QR.getCorrectionAsIdentifierInfo());
-          if (!LookupQualifiedName(TmpRes, Ctx)) continue;
-
-          // Any corrections added below will be validated in subsequent
-          // iterations of the main while() loop over the Consumer's contents.
-          switch (TmpRes.getResultKind()) {
-          case LookupResult::Found:
-          case LookupResult::FoundOverloaded: {
-            if (SS && SS->isValid()) {
-              std::string NewQualified = TC.getAsString(getLangOpts());
-              std::string OldQualified;
-              llvm::raw_string_ostream OldOStream(OldQualified);
-              SS->getScopeRep()->print(OldOStream, getPrintingPolicy());
-              OldOStream << TypoName;
-              // If correction candidate would be an identical written qualified
-              // identifer, then the existing CXXScopeSpec probably included a
-              // typedef that didn't get accounted for properly.
-              if (OldOStream.str() == NewQualified)
-                break;
-            }
-            for (LookupResult::iterator TRD = TmpRes.begin(),
-                                     TRDEnd = TmpRes.end();
-                 TRD != TRDEnd; ++TRD) {
-              if (CheckMemberAccess(TC.getCorrectionRange().getBegin(),
-                                    NSType ? NSType->getAsCXXRecordDecl()
-                                           : nullptr,
-                                    TRD.getPair()) == AR_accessible)
-                TC.addCorrectionDecl(*TRD);
-            }
-            if (TC.isResolved())
-              Consumer.addCorrection(TC);
-            break;
-          }
-          case LookupResult::NotFound:
-          case LookupResult::NotFoundInCurrentInstantiation:
-          case LookupResult::Ambiguous:
-          case LookupResult::FoundUnresolvedValue:
-            break;
-          }
-        }
-      }
-    }
-
-    QualifiedResults.clear();
-  }
-
-  // No corrections remain...
-  if (Consumer.empty())
+  TypoCorrection BestTC = Consumer.getNextCorrection();
+  TypoCorrection SecondBestTC = Consumer.getNextCorrection();
+  if (!BestTC)
     return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
 
   TypoResultsMap &BestResults = Consumer.getBestResults();
-  ED = Consumer.getBestEditDistance(true);
+  ED = BestTC.getEditDistance();
 
   if (!AllowOnlyNNSChanges && ED > 0 && TypoLen / ED < 3) {
     // If this was an unqualified lookup and we believe the callback
@@ -4361,11 +4355,9 @@ retry_lookup:
   }
 
   // If only a single name remains, return that result.
-  if (BestResults.size() == 1) {
-    const TypoResultList &CorrectionList = BestResults.begin()->second;
-    const TypoCorrection &Result = CorrectionList.front();
-    if (CorrectionList.size() != 1)
-      return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
+  if (!SecondBestTC ||
+      SecondBestTC.getEditDistance(false) > BestTC.getEditDistance(false)) {
+    const TypoCorrection &Result = BestTC;
 
     // Don't correct to a keyword that's the same as the typo; the keyword
     // wasn't actually in scope.
@@ -4381,28 +4373,32 @@ retry_lookup:
     checkCorrectionVisibility(*this, TC, TypoName.getName());
     return TC;
   }
-  else if (BestResults.size() > 1
-           // 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.
-           && CCC.WantObjCSuper && !CCC.WantRemainingKeywords
-           && BestResults["super"].front().isKeyword()) {
+  // 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 && CCC.WantObjCSuper && !CCC.WantRemainingKeywords) {
     // 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 (BestResults["super"].front().isKeyword())
+        BestTC = BestResults["super"].front();
+    }
     // Don't correct to a keyword that's the same as the typo; the keyword
     // wasn't actually in scope.
-    if (ED == 0)
+    if (BestTC.getEditDistance() == 0 ||
+        BestTC.getCorrection().getAsString() != "super")
       return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
 
     // Record the correction for unqualified lookup.
     if (IsUnqualifiedLookup)
-      UnqualifiedTyposCorrected[Typo] = BestResults["super"].front();
+      UnqualifiedTyposCorrected[Typo] = BestTC;
 
-    TypoCorrection TC = BestResults["super"].front();
-    TC.setCorrectionRange(SS, TypoName);
-    return TC;
+    BestTC.setCorrectionRange(SS, TypoName);
+    return BestTC;
   }
 
   // Record the failure's location if needed and return an empty correction. If

Modified: cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp?rev=210669&r1=210668&r2=210669&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp (original)
+++ cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp Wed Jun 11 13:07:01 2014
@@ -5,6 +5,15 @@
 // attempt within a single file (which is to avoid having very broken files take
 // minutes to finally be rejected by the parser).
 
+namespace PR12951 {
+// If there are two corrections that have the same identifier and edit distance
+// and only differ by their namespaces, don't suggest either as a correction
+// since both are equally likely corrections.
+namespace foobar { struct Thing {}; }
+namespace bazquux { struct Thing {}; }
+void f() { Thing t; } // expected-error{{unknown type name 'Thing'}}
+}
+
 namespace bogus_keyword_suggestion {
 void test() {
    status = "OK";  // expected-error-re {{use of undeclared identifier 'status'{{$}}}}

Modified: cfe/trunk/test/SemaCXX/typo-correction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/typo-correction.cpp?rev=210669&r1=210668&r2=210669&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/typo-correction.cpp (original)
+++ cfe/trunk/test/SemaCXX/typo-correction.cpp Wed Jun 11 13:07:01 2014
@@ -202,15 +202,6 @@ namespace ImplicitInt {
   };
 }
 
-namespace PR12951 {
-// If there are two corrections that have the same identifier and edit distance
-// and only differ by their namespaces, don't suggest either as a correction
-// since both are equally likely corrections.
-namespace foobar { struct Thing {}; }
-namespace bazquux { struct Thing {}; }
-void f() { Thing t; } // expected-error{{unknown type name 'Thing'}}
-}
-
 namespace PR13051 {
   template<typename T> struct S {
     template<typename U> void f();





More information about the cfe-commits mailing list