From 784ae8e5c6b557e2395991c6008293660f5afe66 Mon Sep 17 00:00:00 2001 From: Kaelyn Uhrain Date: Fri, 1 Jun 2012 18:11:16 +0000 Subject: [PATCH] Don't allow multiple correction candidates that have the same identifier but different nested name specifiers to quietly clobber each other so only one remains if they do not refer to the same NamedDecl. Fixes PR12951. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157823 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaLookup.cpp | 78 ++++++++++++++++++++++---------- test/SemaCXX/typo-correction.cpp | 9 ++++ 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index fb7e463ad9..5af95d8168 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -3147,7 +3147,8 @@ LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc, namespace { -typedef llvm::StringMap TypoResultsMap; +typedef llvm::SmallVector TypoResultList; +typedef llvm::StringMap TypoResultsMap; typedef std::map TypoEditDistanceMap; static const unsigned MaxTypoDistanceResultSets = 5; @@ -3186,7 +3187,7 @@ public: unsigned size() const { return CorrectionResults.size(); } bool empty() const { return CorrectionResults.empty(); } - TypoCorrection &operator[](StringRef Name) { + TypoResultList &operator[](StringRef Name) { return CorrectionResults.begin()->second[Name]; } @@ -3256,16 +3257,28 @@ void TypoCorrectionConsumer::addName(StringRef Name, void TypoCorrectionConsumer::addCorrection(TypoCorrection Correction) { StringRef Name = Correction.getCorrectionAsIdentifierInfo()->getName(); - TypoResultsMap &Map = CorrectionResults[Correction.getEditDistance(false)]; - - TypoCorrection &CurrentCorrection = Map[Name]; - if (!CurrentCorrection || - // FIXME: The following should be rolled up into an operator< on - // TypoCorrection with a more principled definition. - CurrentCorrection.isKeyword() < Correction.isKeyword() || - Correction.getAsString(SemaRef.getLangOpts()) < - CurrentCorrection.getAsString(SemaRef.getLangOpts())) - CurrentCorrection = Correction; + TypoResultList &CList = + CorrectionResults[Correction.getEditDistance(false)][Name]; + + if (!CList.empty() && !CList.back().isResolved()) + CList.pop_back(); + if (NamedDecl *NewND = Correction.getCorrectionDecl()) { + std::string CorrectionStr = Correction.getAsString(SemaRef.getLangOpts()); + for (TypoResultList::iterator RI = CList.begin(), RIEnd = CList.end(); + RI != RIEnd; ++RI) { + // If the Correction refers to a decl already in the result list, + // replace the existing result if the string representation of Correction + // comes before the current result alphabetically, then stop as there is + // nothing more to be done to add Correction to the candidate set. + if (RI->getCorrectionDecl() == NewND) { + if (CorrectionStr < RI->getAsString(SemaRef.getLangOpts())) + *RI = Correction; + return; + } + } + } + if (CList.empty() || Correction.isResolved()) + CList.push_back(Correction); while (CorrectionResults.size() > MaxTypoDistanceResultSets) erase(llvm::prior(CorrectionResults.end())); @@ -3862,16 +3875,30 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // 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. - if (I->second.isResolved()) { + bool Viable = false; + for (TypoResultList::iterator RI = I->second.begin(), RIEnd = I->second.end(); + RI != RIEnd; /* Increment in loop. */) { + TypoResultList::iterator Prev = RI; + ++RI; + if (Prev->isResolved()) { + if (!isCandidateViable(CCC, *Prev)) + I->second.erase(Prev); + else + Viable = true; + } + } + if (Viable || I->second.empty()) { TypoCorrectionConsumer::result_iterator Prev = I; ++I; - if (!isCandidateViable(CCC, Prev->second)) + if (!Viable) DI->second.erase(Prev); continue; } + assert(I->second.size() == 1 && "Expected a single unresolved candidate"); // Perform name lookup on this name. - IdentifierInfo *Name = I->second.getCorrectionAsIdentifierInfo(); + TypoCorrection &Candidate = I->second.front(); + IdentifierInfo *Name = Candidate.getCorrectionAsIdentifierInfo(); LookupPotentialTypoResult(*this, TmpRes, Name, S, SS, MemberContext, EnteringContext, CCC.IsObjCIvarLookup); @@ -3879,7 +3906,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, case LookupResult::NotFound: case LookupResult::NotFoundInCurrentInstantiation: case LookupResult::FoundUnresolvedValue: - QualifiedResults.push_back(I->second); + QualifiedResults.push_back(Candidate); // We didn't find this name in our scope, or didn't like what we found; // ignore it. { @@ -3900,18 +3927,18 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, for (LookupResult::iterator TRD = TmpRes.begin(), TRDEnd = TmpRes.end(); TRD != TRDEnd; ++TRD) - I->second.addCorrectionDecl(*TRD); + Candidate.addCorrectionDecl(*TRD); ++I; - if (!isCandidateViable(CCC, Prev->second)) + if (!isCandidateViable(CCC, Candidate)) DI->second.erase(Prev); break; } case LookupResult::Found: { TypoCorrectionConsumer::result_iterator Prev = I; - I->second.setCorrectionDecl(TmpRes.getAsSingle()); + Candidate.setCorrectionDecl(TmpRes.getAsSingle()); ++I; - if (!isCandidateViable(CCC, Prev->second)) + if (!isCandidateViable(CCC, Candidate)) DI->second.erase(Prev); break; } @@ -3998,8 +4025,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // If only a single name remains, return that result. if (BestResults.size() == 1) { - const llvm::StringMapEntry &Correction = *(BestResults.begin()); - const TypoCorrection &Result = Correction.second; + const TypoResultList &CorrectionList = BestResults.begin()->second; + const TypoCorrection &Result = CorrectionList.front(); + if (CorrectionList.size() != 1) return TypoCorrection(); // Don't correct to a keyword that's the same as the typo; the keyword // wasn't actually in scope. @@ -4017,7 +4045,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // some instances of CTC_Unknown, while WantRemainingKeywords is true // for CTC_Unknown but not for CTC_ObjCMessageReceiver. && CCC.WantObjCSuper && !CCC.WantRemainingKeywords - && BestResults["super"].isKeyword()) { + && BestResults["super"].front().isKeyword()) { // Prefer 'super' when we're completing in a message-receiver // context. @@ -4027,9 +4055,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // Record the correction for unqualified lookup. if (IsUnqualifiedLookup) - UnqualifiedTyposCorrected[Typo] = BestResults["super"]; + UnqualifiedTyposCorrected[Typo] = BestResults["super"].front(); - return BestResults["super"]; + return BestResults["super"].front(); } // If this was an unqualified lookup and we believe the callback object did diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp index 4d6d0654f2..fb07ce127e 100644 --- a/test/SemaCXX/typo-correction.cpp +++ b/test/SemaCXX/typo-correction.cpp @@ -197,3 +197,12 @@ namespace ImplicitInt { unsinged : 4; // expected-error{{did you mean 'unsigned'}} }; } + +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'}} +} -- 2.40.0