diff options
Diffstat (limited to 'lib/Sema/SemaLookup.cpp')
-rw-r--r-- | lib/Sema/SemaLookup.cpp | 990 |
1 files changed, 646 insertions, 344 deletions
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 92ade1e..0e448e3 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -20,6 +20,7 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/ExternalSemaSource.h" +#include "clang/Sema/TypoCorrection.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" @@ -42,6 +43,7 @@ #include <iterator> #include <utility> #include <algorithm> +#include <map> using namespace clang; using namespace sema; @@ -207,6 +209,7 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind, bool Redeclaration) { unsigned IDNS = 0; switch (NameKind) { + case Sema::LookupObjCImplicitSelfParam: case Sema::LookupOrdinaryName: case Sema::LookupRedeclarationWithLinkage: IDNS = Decl::IDNS_Ordinary; @@ -1095,7 +1098,10 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) { if (LeftStartingScope && !((*I)->hasLinkage())) continue; } - + else if (NameKind == LookupObjCImplicitSelfParam && + !isa<ImplicitParamDecl>(*I)) + continue; + R.addDecl(*I); if ((*I)->getAttr<OverloadableAttr>()) { @@ -1379,6 +1385,7 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, // Look for this member in our base classes CXXRecordDecl::BaseMatchesCallback *BaseCallback = 0; switch (R.getLookupKind()) { + case LookupObjCImplicitSelfParam: case LookupOrdinaryName: case LookupMemberName: case LookupRedeclarationWithLinkage: @@ -2137,15 +2144,15 @@ void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S, } } -Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, +Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD, CXXSpecialMember SM, bool ConstArg, bool VolatileArg, bool RValueThis, bool ConstThis, bool VolatileThis) { - D = D->getDefinition(); - assert((D && !D->isBeingDefined()) && + RD = RD->getDefinition(); + assert((RD && !RD->isBeingDefined()) && "doing special member lookup into record that isn't fully complete"); if (RValueThis || ConstThis || VolatileThis) assert((SM == CXXCopyAssignment || SM == CXXMoveAssignment) && @@ -2155,7 +2162,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, "parameter-less special members can't have qualified arguments"); llvm::FoldingSetNodeID ID; - ID.AddPointer(D); + ID.AddPointer(RD); ID.AddInteger(SM); ID.AddInteger(ConstArg); ID.AddInteger(VolatileArg); @@ -2176,9 +2183,9 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, SpecialMemberCache.InsertNode(Result, InsertPoint); if (SM == CXXDestructor) { - if (!D->hasDeclaredDestructor()) - DeclareImplicitDestructor(D); - CXXDestructorDecl *DD = D->getDestructor(); + if (!RD->hasDeclaredDestructor()) + DeclareImplicitDestructor(RD); + CXXDestructorDecl *DD = RD->getDestructor(); assert(DD && "record without a destructor"); Result->setMethod(DD); Result->setSuccess(DD->isDeleted()); @@ -2188,7 +2195,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, // Prepare for overload resolution. Here we construct a synthetic argument // if necessary and make sure that implicit functions are declared. - CanQualType CanTy = Context.getCanonicalType(Context.getTagDeclType(D)); + CanQualType CanTy = Context.getCanonicalType(Context.getTagDeclType(RD)); DeclarationName Name; Expr *Arg = 0; unsigned NumArgs; @@ -2196,18 +2203,18 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, if (SM == CXXDefaultConstructor) { Name = Context.DeclarationNames.getCXXConstructorName(CanTy); NumArgs = 0; - if (D->needsImplicitDefaultConstructor()) - DeclareImplicitDefaultConstructor(D); + if (RD->needsImplicitDefaultConstructor()) + DeclareImplicitDefaultConstructor(RD); } else { if (SM == CXXCopyConstructor || SM == CXXMoveConstructor) { Name = Context.DeclarationNames.getCXXConstructorName(CanTy); - if (!D->hasDeclaredCopyConstructor()) - DeclareImplicitCopyConstructor(D); + if (!RD->hasDeclaredCopyConstructor()) + DeclareImplicitCopyConstructor(RD); // TODO: Move constructors } else { Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); - if (!D->hasDeclaredCopyAssignment()) - DeclareImplicitCopyAssignment(D); + if (!RD->hasDeclaredCopyAssignment()) + DeclareImplicitCopyAssignment(RD); // TODO: Move assignment } @@ -2225,7 +2232,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, // there is no semantic difference for class types in this restricted // case. ExprValueKind VK; - if (SM == CXXCopyAssignment || SM == CXXMoveAssignment) + if (SM == CXXCopyConstructor || SM == CXXCopyAssignment) VK = VK_LValue; else VK = VK_RValue; @@ -2240,7 +2247,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, ThisTy.addConst(); if (VolatileThis) ThisTy.addVolatile(); - Expr::Classification ObjectClassification = + Expr::Classification Classification = (new (Context) OpaqueValueExpr(SourceLocation(), ThisTy, RValueThis ? VK_RValue : VK_LValue))-> Classify(Context); @@ -2252,16 +2259,33 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, DeclContext::lookup_iterator I, E; Result->setConstParamMatch(false); - llvm::tie(I, E) = D->lookup(Name); + llvm::tie(I, E) = RD->lookup(Name); assert((I != E) && "lookup for a constructor or assignment operator was empty"); for ( ; I != E; ++I) { - if ((*I)->isInvalidDecl()) + Decl *Cand = *I; + + if (Cand->isInvalidDecl()) continue; - if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(*I)) { - AddOverloadCandidate(M, DeclAccessPair::make(M, AS_public), &Arg, NumArgs, - OCS, true); + if (UsingShadowDecl *U = dyn_cast<UsingShadowDecl>(Cand)) { + // FIXME: [namespace.udecl]p15 says that we should only consider a + // using declaration here if it does not match a declaration in the + // derived class. We do not implement this correctly in other cases + // either. + Cand = U->getTargetDecl(); + + if (Cand->isInvalidDecl()) + continue; + } + + if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand)) { + if (SM == CXXCopyAssignment || SM == CXXMoveAssignment) + AddMethodCandidate(M, DeclAccessPair::make(M, AS_public), RD, ThisTy, + Classification, &Arg, NumArgs, OCS, true); + else + AddOverloadCandidate(M, DeclAccessPair::make(M, AS_public), &Arg, + NumArgs, OCS, true); // Here we're looking for a const parameter to speed up creation of // implicit copy methods. @@ -2269,13 +2293,21 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, (SM == CXXCopyConstructor && cast<CXXConstructorDecl>(M)->isCopyConstructor())) { QualType ArgType = M->getType()->getAs<FunctionProtoType>()->getArgType(0); - if (ArgType->getPointeeType().isConstQualified()) + if (!ArgType->isReferenceType() || + ArgType->getPointeeType().isConstQualified()) Result->setConstParamMatch(true); } + } else if (FunctionTemplateDecl *Tmpl = + dyn_cast<FunctionTemplateDecl>(Cand)) { + if (SM == CXXCopyAssignment || SM == CXXMoveAssignment) + AddMethodTemplateCandidate(Tmpl, DeclAccessPair::make(Tmpl, AS_public), + RD, 0, ThisTy, Classification, &Arg, NumArgs, + OCS, true); + else + AddTemplateOverloadCandidate(Tmpl, DeclAccessPair::make(Tmpl, AS_public), + 0, &Arg, NumArgs, OCS, true); } else { - FunctionTemplateDecl *Tmpl = cast<FunctionTemplateDecl>(*I); - AddTemplateOverloadCandidate(Tmpl, DeclAccessPair::make(Tmpl, AS_public), - 0, &Arg, NumArgs, OCS, true); + assert(isa<UsingDecl>(Cand) && "illegal Kind of operator = Decl"); } } @@ -2310,10 +2342,10 @@ CXXConstructorDecl *Sema::LookupDefaultConstructor(CXXRecordDecl *Class) { return cast_or_null<CXXConstructorDecl>(Result->getMethod()); } -/// \brief Look up the copy constructor for the given class. -CXXConstructorDecl *Sema::LookupCopyConstructor(CXXRecordDecl *Class, - unsigned Quals, - bool *ConstParamMatch) { +/// \brief Look up the copying constructor for the given class. +CXXConstructorDecl *Sema::LookupCopyingConstructor(CXXRecordDecl *Class, + unsigned Quals, + bool *ConstParamMatch) { assert(!(Quals & ~(Qualifiers::Const | Qualifiers::Volatile)) && "non-const, non-volatile qualifiers for copy ctor arg"); SpecialMemberOverloadResult *Result = @@ -2341,6 +2373,27 @@ DeclContext::lookup_result Sema::LookupConstructors(CXXRecordDecl *Class) { return Class->lookup(Name); } +/// \brief Look up the copying assignment operator for the given class. +CXXMethodDecl *Sema::LookupCopyingAssignment(CXXRecordDecl *Class, + unsigned Quals, bool RValueThis, + unsigned ThisQuals, + bool *ConstParamMatch) { + assert(!(Quals & ~(Qualifiers::Const | Qualifiers::Volatile)) && + "non-const, non-volatile qualifiers for copy assignment arg"); + assert(!(ThisQuals & ~(Qualifiers::Const | Qualifiers::Volatile)) && + "non-const, non-volatile qualifiers for copy assignment this"); + SpecialMemberOverloadResult *Result = + LookupSpecialMember(Class, CXXCopyAssignment, Quals & Qualifiers::Const, + Quals & Qualifiers::Volatile, RValueThis, + ThisQuals & Qualifiers::Const, + ThisQuals & Qualifiers::Volatile); + + if (ConstParamMatch) + *ConstParamMatch = Result->hasConstParamMatch(); + + return Result->getMethod(); +} + /// \brief Look for the destructor of the given class. /// /// During semantic analysis, this routine should be used in lieu of @@ -2995,6 +3048,12 @@ LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc, //===----------------------------------------------------------------------===// namespace { + +typedef llvm::StringMap<TypoCorrection, llvm::BumpPtrAllocator> TypoResultsMap; +typedef std::map<unsigned, TypoResultsMap *> TypoEditDistanceMap; + +static const unsigned MaxTypoDistanceResultSets = 5; + class TypoCorrectionConsumer : public VisibleDeclConsumer { /// \brief The name written that is a typo in the source. llvm::StringRef Typo; @@ -3002,33 +3061,55 @@ class TypoCorrectionConsumer : public VisibleDeclConsumer { /// \brief The results found that have the smallest edit distance /// found (so far) with the typo name. /// - /// The boolean value indicates whether there is a keyword with this name. - llvm::StringMap<bool, llvm::BumpPtrAllocator> BestResults; + /// The pointer value being set to the current DeclContext indicates + /// whether there is a keyword with this name. + TypoEditDistanceMap BestResults; + + /// \brief The worst of the best N edit distances found so far. + unsigned MaxEditDistance; - /// \brief The best edit distance found so far. - unsigned BestEditDistance; + Sema &SemaRef; public: - explicit TypoCorrectionConsumer(IdentifierInfo *Typo) + explicit TypoCorrectionConsumer(Sema &SemaRef, IdentifierInfo *Typo) : Typo(Typo->getName()), - BestEditDistance((std::numeric_limits<unsigned>::max)()) { } + MaxEditDistance((std::numeric_limits<unsigned>::max)()), + SemaRef(SemaRef) { } + ~TypoCorrectionConsumer() { + for (TypoEditDistanceMap::iterator I = BestResults.begin(), + IEnd = BestResults.end(); + I != IEnd; + ++I) + delete I->second; + } + virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, bool InBaseClass); void FoundName(llvm::StringRef Name); - void addKeywordResult(ASTContext &Context, llvm::StringRef Keyword); - - typedef llvm::StringMap<bool, llvm::BumpPtrAllocator>::iterator iterator; - iterator begin() { return BestResults.begin(); } - iterator end() { return BestResults.end(); } - void erase(iterator I) { BestResults.erase(I); } + void addKeywordResult(llvm::StringRef Keyword); + void addName(llvm::StringRef Name, NamedDecl *ND, unsigned Distance, + NestedNameSpecifier *NNS=NULL); + void addCorrection(TypoCorrection Correction); + + typedef TypoResultsMap::iterator result_iterator; + typedef TypoEditDistanceMap::iterator distance_iterator; + distance_iterator begin() { return BestResults.begin(); } + distance_iterator end() { return BestResults.end(); } + void erase(distance_iterator I) { BestResults.erase(I); } unsigned size() const { return BestResults.size(); } bool empty() const { return BestResults.empty(); } - bool &operator[](llvm::StringRef Name) { - return BestResults[Name]; + TypoCorrection &operator[](llvm::StringRef Name) { + return (*BestResults.begin()->second)[Name]; + } + + unsigned getMaxEditDistance() const { + return MaxEditDistance; } - unsigned getBestEditDistance() const { return BestEditDistance; } + unsigned getBestEditDistance() { + return (BestResults.empty()) ? MaxEditDistance : BestResults.begin()->first; + } }; } @@ -3053,55 +3134,181 @@ void TypoCorrectionConsumer::FoundName(llvm::StringRef Name) { // 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 > BestEditDistance || (MinED && Typo.size() / MinED < 3)) + if (MinED > MaxEditDistance || (MinED && Typo.size() / MinED < 3)) return; // Compute an upper bound on the allowable edit distance, so that the // edit-distance algorithm can short-circuit. unsigned UpperBound = - std::min(unsigned((Typo.size() + 2) / 3), BestEditDistance); + std::min(unsigned((Typo.size() + 2) / 3), MaxEditDistance); // Compute the edit distance between the typo and the name of this // entity. If this edit distance is not worse than the best edit // distance we've seen so far, add it to the list of results. unsigned ED = Typo.edit_distance(Name, true, UpperBound); - if (ED == 0) - return; - if (ED < BestEditDistance) { - // This result is better than any we've seen before; clear out - // the previous results. - BestResults.clear(); - BestEditDistance = ED; - } else if (ED > BestEditDistance) { + if (ED > MaxEditDistance) { // This result is worse than the best results we've seen so far; // ignore it. return; } - // Add this name to the list of results. By not assigning a value, we - // keep the current value if we've seen this name before (either as a - // keyword or as a declaration), or get the default value (not a keyword) - // if we haven't seen it before. - (void)BestResults[Name]; + addName(Name, NULL, ED); } -void TypoCorrectionConsumer::addKeywordResult(ASTContext &Context, - llvm::StringRef Keyword) { +void TypoCorrectionConsumer::addKeywordResult(llvm::StringRef Keyword) { // Compute the edit distance between the typo and this keyword. // If this edit distance is not worse than the best edit // distance we've seen so far, add it to the list of results. unsigned ED = Typo.edit_distance(Keyword); - if (ED < BestEditDistance) { - BestResults.clear(); - BestEditDistance = ED; - } else if (ED > BestEditDistance) { + if (ED > MaxEditDistance) { // This result is worse than the best results we've seen so far; // ignore it. return; } - BestResults[Keyword] = true; + addName(Keyword, TypoCorrection::KeywordDecl(), ED); +} + +void TypoCorrectionConsumer::addName(llvm::StringRef Name, + NamedDecl *ND, + unsigned Distance, + NestedNameSpecifier *NNS) { + addCorrection(TypoCorrection(&SemaRef.Context.Idents.get(Name), + ND, NNS, Distance)); +} + +void TypoCorrectionConsumer::addCorrection(TypoCorrection Correction) { + llvm::StringRef Name = Correction.getCorrectionAsIdentifierInfo()->getName(); + TypoResultsMap *& Map = BestResults[Correction.getEditDistance()]; + if (!Map) + Map = new TypoResultsMap; + + 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.getLangOptions()) < + CurrentCorrection.getAsString(SemaRef.getLangOptions())) + CurrentCorrection = Correction; + + while (BestResults.size() > MaxTypoDistanceResultSets) { + TypoEditDistanceMap::iterator Last = BestResults.end(); + --Last; + delete Last->second; + BestResults.erase(Last); + } +} + +namespace { + +class SpecifierInfo { + public: + DeclContext* DeclCtx; + NestedNameSpecifier* NameSpecifier; + unsigned EditDistance; + + SpecifierInfo(DeclContext *Ctx, NestedNameSpecifier *NNS, unsigned ED) + : DeclCtx(Ctx), NameSpecifier(NNS), EditDistance(ED) {} +}; + +typedef llvm::SmallVector<DeclContext*, 4> DeclContextList; +typedef llvm::SmallVector<SpecifierInfo, 16> SpecifierInfoList; + +class NamespaceSpecifierSet { + ASTContext &Context; + DeclContextList CurContextChain; + bool isSorted; + + SpecifierInfoList Specifiers; + llvm::SmallSetVector<unsigned, 4> Distances; + llvm::DenseMap<unsigned, SpecifierInfoList> DistanceMap; + + /// \brief Helper for building the list of DeclContexts between the current + /// context and the top of the translation unit + static DeclContextList BuildContextChain(DeclContext *Start); + + void SortNamespaces(); + + public: + explicit NamespaceSpecifierSet(ASTContext &Context, DeclContext *CurContext) + : Context(Context), CurContextChain(BuildContextChain(CurContext)), + isSorted(true) {} + + /// \brief Add the namespace to the set, computing the corresponding + /// NestedNameSpecifier and its distance in the process. + void AddNamespace(NamespaceDecl *ND); + + typedef SpecifierInfoList::iterator iterator; + iterator begin() { + if (!isSorted) SortNamespaces(); + return Specifiers.begin(); + } + iterator end() { return Specifiers.end(); } +}; + +} + +DeclContextList NamespaceSpecifierSet::BuildContextChain(DeclContext *Start) { + assert(Start && "Bulding a context chain from a null context"); + DeclContextList Chain; + for (DeclContext *DC = Start->getPrimaryContext(); DC != NULL; + DC = DC->getLookupParent()) { + NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(DC); + if (!DC->isInlineNamespace() && !DC->isTransparentContext() && + !(ND && ND->isAnonymousNamespace())) + Chain.push_back(DC->getPrimaryContext()); + } + return Chain; +} + +void NamespaceSpecifierSet::SortNamespaces() { + llvm::SmallVector<unsigned, 4> sortedDistances; + sortedDistances.append(Distances.begin(), Distances.end()); + + if (sortedDistances.size() > 1) + std::sort(sortedDistances.begin(), sortedDistances.end()); + + Specifiers.clear(); + for (llvm::SmallVector<unsigned, 4>::iterator DI = sortedDistances.begin(), + DIEnd = sortedDistances.end(); + DI != DIEnd; ++DI) { + SpecifierInfoList &SpecList = DistanceMap[*DI]; + Specifiers.append(SpecList.begin(), SpecList.end()); + } + + isSorted = true; +} + +void NamespaceSpecifierSet::AddNamespace(NamespaceDecl *ND) { + DeclContext *Ctx = cast<DeclContext>(ND); + NestedNameSpecifier *NNS = NULL; + unsigned NumSpecifiers = 0; + DeclContextList NamespaceDeclChain(BuildContextChain(Ctx)); + + // Eliminate common elements from the two DeclContext chains + for (DeclContextList::reverse_iterator C = CurContextChain.rbegin(), + CEnd = CurContextChain.rend(); + C != CEnd && !NamespaceDeclChain.empty() && + NamespaceDeclChain.back() == *C; ++C) { + NamespaceDeclChain.pop_back(); + } + + // Build the NestedNameSpecifier from what is left of the NamespaceDeclChain + for (DeclContextList::reverse_iterator C = NamespaceDeclChain.rbegin(), + CEnd = NamespaceDeclChain.rend(); + C != CEnd; ++C) { + NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(*C); + if (ND) { + NNS = NestedNameSpecifier::Create(Context, NNS, ND); + ++NumSpecifiers; + } + } + + isSorted = false; + Distances.insert(NumSpecifiers); + DistanceMap[NumSpecifiers].push_back(SpecifierInfo(Ctx, NNS, NumSpecifiers)); } /// \brief Perform name lookup for a possible result for typo correction. @@ -3155,14 +3362,193 @@ static void LookupPotentialTypoResult(Sema &SemaRef, } } +/// \brief Add keywords to the consumer as possible typo corrections. +static void AddKeywordsToConsumer(Sema &SemaRef, + TypoCorrectionConsumer &Consumer, + Scope *S, Sema::CorrectTypoContext CTC) { + // Add context-dependent keywords. + bool WantTypeSpecifiers = false; + bool WantExpressionKeywords = false; + bool WantCXXNamedCasts = false; + bool WantRemainingKeywords = false; + switch (CTC) { + case Sema::CTC_Unknown: + WantTypeSpecifiers = true; + WantExpressionKeywords = true; + WantCXXNamedCasts = true; + WantRemainingKeywords = true; + + if (ObjCMethodDecl *Method = SemaRef.getCurMethodDecl()) + if (Method->getClassInterface() && + Method->getClassInterface()->getSuperClass()) + Consumer.addKeywordResult("super"); + + break; + + case Sema::CTC_NoKeywords: + break; + + case Sema::CTC_Type: + WantTypeSpecifiers = true; + break; + + case Sema::CTC_ObjCMessageReceiver: + Consumer.addKeywordResult("super"); + // Fall through to handle message receivers like expressions. + + case Sema::CTC_Expression: + if (SemaRef.getLangOptions().CPlusPlus) + WantTypeSpecifiers = true; + WantExpressionKeywords = true; + // Fall through to get C++ named casts. + + case Sema::CTC_CXXCasts: + WantCXXNamedCasts = true; + break; + + case Sema::CTC_ObjCPropertyLookup: + // FIXME: Add "isa"? + break; + + case Sema::CTC_MemberLookup: + if (SemaRef.getLangOptions().CPlusPlus) + Consumer.addKeywordResult("template"); + break; + + case Sema::CTC_ObjCIvarLookup: + break; + } + + if (WantTypeSpecifiers) { + // Add type-specifier keywords to the set of results. + const char *CTypeSpecs[] = { + "char", "const", "double", "enum", "float", "int", "long", "short", + "signed", "struct", "union", "unsigned", "void", "volatile", + "_Complex", "_Imaginary", + // storage-specifiers as well + "extern", "inline", "static", "typedef" + }; + + const unsigned NumCTypeSpecs = sizeof(CTypeSpecs) / sizeof(CTypeSpecs[0]); + for (unsigned I = 0; I != NumCTypeSpecs; ++I) + Consumer.addKeywordResult(CTypeSpecs[I]); + + if (SemaRef.getLangOptions().C99) + Consumer.addKeywordResult("restrict"); + if (SemaRef.getLangOptions().Bool || SemaRef.getLangOptions().CPlusPlus) + Consumer.addKeywordResult("bool"); + else if (SemaRef.getLangOptions().C99) + Consumer.addKeywordResult("_Bool"); + + if (SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("class"); + Consumer.addKeywordResult("typename"); + Consumer.addKeywordResult("wchar_t"); + + if (SemaRef.getLangOptions().CPlusPlus0x) { + Consumer.addKeywordResult("char16_t"); + Consumer.addKeywordResult("char32_t"); + Consumer.addKeywordResult("constexpr"); + Consumer.addKeywordResult("decltype"); + Consumer.addKeywordResult("thread_local"); + } + } + + if (SemaRef.getLangOptions().GNUMode) + Consumer.addKeywordResult("typeof"); + } + + if (WantCXXNamedCasts && SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("const_cast"); + Consumer.addKeywordResult("dynamic_cast"); + Consumer.addKeywordResult("reinterpret_cast"); + Consumer.addKeywordResult("static_cast"); + } + + if (WantExpressionKeywords) { + Consumer.addKeywordResult("sizeof"); + if (SemaRef.getLangOptions().Bool || SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("false"); + Consumer.addKeywordResult("true"); + } + + if (SemaRef.getLangOptions().CPlusPlus) { + const char *CXXExprs[] = { + "delete", "new", "operator", "throw", "typeid" + }; + const unsigned NumCXXExprs = sizeof(CXXExprs) / sizeof(CXXExprs[0]); + for (unsigned I = 0; I != NumCXXExprs; ++I) + Consumer.addKeywordResult(CXXExprs[I]); + + if (isa<CXXMethodDecl>(SemaRef.CurContext) && + cast<CXXMethodDecl>(SemaRef.CurContext)->isInstance()) + Consumer.addKeywordResult("this"); + + if (SemaRef.getLangOptions().CPlusPlus0x) { + Consumer.addKeywordResult("alignof"); + Consumer.addKeywordResult("nullptr"); + } + } + } + + if (WantRemainingKeywords) { + if (SemaRef.getCurFunctionOrMethodDecl() || SemaRef.getCurBlock()) { + // Statements. + const char *CStmts[] = { + "do", "else", "for", "goto", "if", "return", "switch", "while" }; + const unsigned NumCStmts = sizeof(CStmts) / sizeof(CStmts[0]); + for (unsigned I = 0; I != NumCStmts; ++I) + Consumer.addKeywordResult(CStmts[I]); + + if (SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("catch"); + Consumer.addKeywordResult("try"); + } + + if (S && S->getBreakParent()) + Consumer.addKeywordResult("break"); + + if (S && S->getContinueParent()) + Consumer.addKeywordResult("continue"); + + if (!SemaRef.getCurFunction()->SwitchStack.empty()) { + Consumer.addKeywordResult("case"); + Consumer.addKeywordResult("default"); + } + } else { + if (SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("namespace"); + Consumer.addKeywordResult("template"); + } + + if (S && S->isClassScope()) { + Consumer.addKeywordResult("explicit"); + Consumer.addKeywordResult("friend"); + Consumer.addKeywordResult("mutable"); + Consumer.addKeywordResult("private"); + Consumer.addKeywordResult("protected"); + Consumer.addKeywordResult("public"); + Consumer.addKeywordResult("virtual"); + } + } + + if (SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("using"); + + if (SemaRef.getLangOptions().CPlusPlus0x) + Consumer.addKeywordResult("static_assert"); + } + } +} + /// \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 Res the \c LookupResult structure that contains the name -/// that was present in the source code along with the name-lookup -/// criteria used to search for the name. On success, this structure -/// will contain the results of name lookup. +/// \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. /// @@ -3181,60 +3567,64 @@ static void LookupPotentialTypoResult(Sema &SemaRef, /// \param OPT when non-NULL, the search for visible declarations will /// also walk the protocols in the qualified interfaces of \p OPT. /// -/// \returns the corrected name if the typo was corrected, otherwise returns an -/// empty \c DeclarationName. When a typo was corrected, the result structure -/// may contain the results of name lookup for the correct name or it may be -/// empty. -DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS, - DeclContext *MemberContext, - bool EnteringContext, - CorrectTypoContext CTC, - const ObjCObjectPointerType *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, + DeclContext *MemberContext, + bool EnteringContext, + CorrectTypoContext CTC, + const ObjCObjectPointerType *OPT) { if (Diags.hasFatalErrorOccurred() || !getLangOptions().SpellChecking) - return DeclarationName(); + return TypoCorrection(); // We only attempt to correct typos for identifiers. - IdentifierInfo *Typo = Res.getLookupName().getAsIdentifierInfo(); + IdentifierInfo *Typo = TypoName.getName().getAsIdentifierInfo(); if (!Typo) - return DeclarationName(); + return TypoCorrection(); // If the scope specifier itself was invalid, don't try to correct // typos. if (SS && SS->isInvalid()) - return DeclarationName(); + return TypoCorrection(); // Never try to correct typos during template deduction or // instantiation. if (!ActiveTemplateInstantiations.empty()) - return DeclarationName(); + return TypoCorrection(); - TypoCorrectionConsumer Consumer(Typo); + NamespaceSpecifierSet Namespaces(Context, CurContext); + + TypoCorrectionConsumer Consumer(*this, Typo); // Perform name lookup to find visible, similarly-named entities. bool IsUnqualifiedLookup = false; if (MemberContext) { - LookupVisibleDecls(MemberContext, Res.getLookupKind(), Consumer); + LookupVisibleDecls(MemberContext, LookupKind, Consumer); // Look in qualified interfaces. if (OPT) { for (ObjCObjectPointerType::qual_iterator I = OPT->qual_begin(), E = OPT->qual_end(); I != E; ++I) - LookupVisibleDecls(*I, Res.getLookupKind(), Consumer); + LookupVisibleDecls(*I, LookupKind, Consumer); } } else if (SS && SS->isSet()) { DeclContext *DC = computeDeclContext(*SS, EnteringContext); if (!DC) - return DeclarationName(); + return TypoCorrection(); // 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 DeclarationName(); + return TypoCorrection(); ++TyposCorrected; - LookupVisibleDecls(DC, Res.getLookupKind(), Consumer); + LookupVisibleDecls(DC, LookupKind, Consumer); } else { IsUnqualifiedLookup = true; UnqualifiedTyposCorrectedMap::iterator Cached @@ -3244,7 +3634,7 @@ DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS, // 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 DeclarationName(); + return TypoCorrection(); // For unqualified lookup, look through all of the names that we have // seen in this translation unit. @@ -3268,313 +3658,225 @@ DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS, } else { // Use the cached value, unless it's a keyword. In the keyword case, we'll // end up adding the keyword below. - if (Cached->second.first.empty()) - return DeclarationName(); + if (!Cached->second) + return TypoCorrection(); - if (!Cached->second.second) - Consumer.FoundName(Cached->second.first); + if (!Cached->second.isKeyword()) + Consumer.addCorrection(Cached->second); } } - // Add context-dependent keywords. - bool WantTypeSpecifiers = false; - bool WantExpressionKeywords = false; - bool WantCXXNamedCasts = false; - bool WantRemainingKeywords = false; - switch (CTC) { - case CTC_Unknown: - WantTypeSpecifiers = true; - WantExpressionKeywords = true; - WantCXXNamedCasts = true; - WantRemainingKeywords = true; - - if (ObjCMethodDecl *Method = getCurMethodDecl()) - if (Method->getClassInterface() && - Method->getClassInterface()->getSuperClass()) - Consumer.addKeywordResult(Context, "super"); - - break; - - case CTC_NoKeywords: - break; - - case CTC_Type: - WantTypeSpecifiers = true; - break; - - case CTC_ObjCMessageReceiver: - Consumer.addKeywordResult(Context, "super"); - // Fall through to handle message receivers like expressions. - - case CTC_Expression: - if (getLangOptions().CPlusPlus) - WantTypeSpecifiers = true; - WantExpressionKeywords = true; - // Fall through to get C++ named casts. - - case CTC_CXXCasts: - WantCXXNamedCasts = true; - break; - - case CTC_ObjCPropertyLookup: - // FIXME: Add "isa"? - break; + AddKeywordsToConsumer(*this, Consumer, S, CTC); - case CTC_MemberLookup: - if (getLangOptions().CPlusPlus) - Consumer.addKeywordResult(Context, "template"); - break; + // If we haven't found anything, we're done. + if (Consumer.empty()) { + // If this was an unqualified lookup, note that no correction was found. + if (IsUnqualifiedLookup) + (void)UnqualifiedTyposCorrected[Typo]; - case CTC_ObjCIvarLookup: - break; + return TypoCorrection(); } - if (WantTypeSpecifiers) { - // Add type-specifier keywords to the set of results. - const char *CTypeSpecs[] = { - "char", "const", "double", "enum", "float", "int", "long", "short", - "signed", "struct", "union", "unsigned", "void", "volatile", "_Bool", - "_Complex", "_Imaginary", - // storage-specifiers as well - "extern", "inline", "static", "typedef" - }; - - const unsigned NumCTypeSpecs = sizeof(CTypeSpecs) / sizeof(CTypeSpecs[0]); - for (unsigned I = 0; I != NumCTypeSpecs; ++I) - Consumer.addKeywordResult(Context, CTypeSpecs[I]); - - if (getLangOptions().C99) - Consumer.addKeywordResult(Context, "restrict"); - if (getLangOptions().Bool || getLangOptions().CPlusPlus) - Consumer.addKeywordResult(Context, "bool"); - - if (getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "class"); - Consumer.addKeywordResult(Context, "typename"); - Consumer.addKeywordResult(Context, "wchar_t"); - - if (getLangOptions().CPlusPlus0x) { - Consumer.addKeywordResult(Context, "char16_t"); - Consumer.addKeywordResult(Context, "char32_t"); - Consumer.addKeywordResult(Context, "constexpr"); - Consumer.addKeywordResult(Context, "decltype"); - Consumer.addKeywordResult(Context, "thread_local"); - } - } + // Make sure that the user typed at least 3 characters for each correction + // made. Otherwise, we don't even both looking at the results. + unsigned ED = Consumer.getBestEditDistance(); + if (ED > 0 && Typo->getName().size() / ED < 3) { + // If this was an unqualified lookup, note that no correction was found. + if (IsUnqualifiedLookup) + (void)UnqualifiedTyposCorrected[Typo]; - if (getLangOptions().GNUMode) - Consumer.addKeywordResult(Context, "typeof"); + return TypoCorrection(); } - if (WantCXXNamedCasts && getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "const_cast"); - Consumer.addKeywordResult(Context, "dynamic_cast"); - Consumer.addKeywordResult(Context, "reinterpret_cast"); - Consumer.addKeywordResult(Context, "static_cast"); + // Build the NestedNameSpecifiers for the KnownNamespaces + if (getLangOptions().CPlusPlus) { + // Load any externally-known namespaces. + if (ExternalSource && !LoadedExternalKnownNamespaces) { + llvm::SmallVector<NamespaceDecl *, 4> ExternalKnownNamespaces; + LoadedExternalKnownNamespaces = true; + ExternalSource->ReadKnownNamespaces(ExternalKnownNamespaces); + for (unsigned I = 0, N = ExternalKnownNamespaces.size(); I != N; ++I) + KnownNamespaces[ExternalKnownNamespaces[I]] = true; + } + + for (llvm::DenseMap<NamespaceDecl*, bool>::iterator + KNI = KnownNamespaces.begin(), + KNIEnd = KnownNamespaces.end(); + KNI != KNIEnd; ++KNI) + Namespaces.AddNamespace(KNI->first); } - if (WantExpressionKeywords) { - Consumer.addKeywordResult(Context, "sizeof"); - if (getLangOptions().Bool || getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "false"); - Consumer.addKeywordResult(Context, "true"); - } + // Weed out any names that could not be found by name lookup. + llvm::SmallPtrSet<IdentifierInfo*, 16> QualifiedResults; + LookupResult TmpRes(*this, TypoName, LookupKind); + TmpRes.suppressDiagnostics(); + while (!Consumer.empty()) { + TypoCorrectionConsumer::distance_iterator DI = Consumer.begin(); + unsigned ED = DI->first; + for (TypoCorrectionConsumer::result_iterator I = DI->second->begin(), + IEnd = DI->second->end(); + I != IEnd; /* Increment in loop. */) { + // If the item already has been looked up or is a keyword, keep it + if (I->second.isResolved()) { + ++I; + continue; + } - if (getLangOptions().CPlusPlus) { - const char *CXXExprs[] = { - "delete", "new", "operator", "throw", "typeid" - }; - const unsigned NumCXXExprs = sizeof(CXXExprs) / sizeof(CXXExprs[0]); - for (unsigned I = 0; I != NumCXXExprs; ++I) - Consumer.addKeywordResult(Context, CXXExprs[I]); + // Perform name lookup on this name. + IdentifierInfo *Name = I->second.getCorrectionAsIdentifierInfo(); + LookupPotentialTypoResult(*this, TmpRes, Name, S, SS, MemberContext, + EnteringContext, CTC); - if (isa<CXXMethodDecl>(CurContext) && - cast<CXXMethodDecl>(CurContext)->isInstance()) - Consumer.addKeywordResult(Context, "this"); + switch (TmpRes.getResultKind()) { + case LookupResult::NotFound: + case LookupResult::NotFoundInCurrentInstantiation: + QualifiedResults.insert(Name); + // 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; - if (getLangOptions().CPlusPlus0x) { - Consumer.addKeywordResult(Context, "alignof"); - Consumer.addKeywordResult(Context, "nullptr"); + case LookupResult::Ambiguous: + // We don't deal with ambiguities. + return TypoCorrection(); + + case LookupResult::Found: + case LookupResult::FoundOverloaded: + case LookupResult::FoundUnresolvedValue: + I->second.setCorrectionDecl(TmpRes.getAsSingle<NamedDecl>()); + // FIXME: This sets the CorrectionDecl to NULL for overloaded functions. + // It would be nice to find the right one with overload resolution. + ++I; + break; } } - } - - if (WantRemainingKeywords) { - if (getCurFunctionOrMethodDecl() || getCurBlock()) { - // Statements. - const char *CStmts[] = { - "do", "else", "for", "goto", "if", "return", "switch", "while" }; - const unsigned NumCStmts = sizeof(CStmts) / sizeof(CStmts[0]); - for (unsigned I = 0; I != NumCStmts; ++I) - Consumer.addKeywordResult(Context, CStmts[I]); - - if (getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "catch"); - Consumer.addKeywordResult(Context, "try"); - } - if (S && S->getBreakParent()) - Consumer.addKeywordResult(Context, "break"); - - if (S && S->getContinueParent()) - Consumer.addKeywordResult(Context, "continue"); - - if (!getCurFunction()->SwitchStack.empty()) { - Consumer.addKeywordResult(Context, "case"); - Consumer.addKeywordResult(Context, "default"); - } - } else { - if (getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "namespace"); - Consumer.addKeywordResult(Context, "template"); - } + if (DI->second->empty()) + Consumer.erase(DI); + else if (!getLangOptions().CPlusPlus || QualifiedResults.empty() || !ED) + // If there are results in the closest possible bucket, stop + break; - if (S && S->isClassScope()) { - Consumer.addKeywordResult(Context, "explicit"); - Consumer.addKeywordResult(Context, "friend"); - Consumer.addKeywordResult(Context, "mutable"); - Consumer.addKeywordResult(Context, "private"); - Consumer.addKeywordResult(Context, "protected"); - Consumer.addKeywordResult(Context, "public"); - Consumer.addKeywordResult(Context, "virtual"); + // Only perform the qualified lookups for C++ + if (getLangOptions().CPlusPlus) { + TmpRes.suppressDiagnostics(); + for (llvm::SmallPtrSet<IdentifierInfo*, + 16>::iterator QRI = QualifiedResults.begin(), + QRIEnd = QualifiedResults.end(); + QRI != QRIEnd; ++QRI) { + for (NamespaceSpecifierSet::iterator NI = Namespaces.begin(), + NIEnd = Namespaces.end(); + NI != NIEnd; ++NI) { + DeclContext *Ctx = NI->DeclCtx; + unsigned QualifiedED = ED + NI->EditDistance; + + // Stop searching once the namespaces are too far away to create + // acceptable corrections for this identifier (since the namespaces + // are sorted in ascending order by edit distance) + if (QualifiedED > Consumer.getMaxEditDistance()) break; + + TmpRes.clear(); + TmpRes.setLookupName(*QRI); + if (!LookupQualifiedName(TmpRes, Ctx)) continue; + + switch (TmpRes.getResultKind()) { + case LookupResult::Found: + case LookupResult::FoundOverloaded: + case LookupResult::FoundUnresolvedValue: + Consumer.addName((*QRI)->getName(), TmpRes.getAsSingle<NamedDecl>(), + QualifiedED, NI->NameSpecifier); + break; + case LookupResult::NotFound: + case LookupResult::NotFoundInCurrentInstantiation: + case LookupResult::Ambiguous: + break; + } + } } } - if (getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "using"); - - if (getLangOptions().CPlusPlus0x) - Consumer.addKeywordResult(Context, "static_assert"); - } + QualifiedResults.clear(); } - // If we haven't found anything, we're done. - if (Consumer.empty()) { - // If this was an unqualified lookup, note that no correction was found. - if (IsUnqualifiedLookup) - (void)UnqualifiedTyposCorrected[Typo]; + // No corrections remain... + if (Consumer.empty()) return TypoCorrection(); - return DeclarationName(); - } + TypoResultsMap &BestResults = *Consumer.begin()->second; + ED = Consumer.begin()->first; - // Make sure that the user typed at least 3 characters for each correction - // made. Otherwise, we don't even both looking at the results. - - // We also suppress exact matches; those should be handled by a - // different mechanism (e.g., one that introduces qualification in - // C++). - unsigned ED = Consumer.getBestEditDistance(); if (ED > 0 && Typo->getName().size() / ED < 3) { // If this was an unqualified lookup, note that no correction was found. if (IsUnqualifiedLookup) (void)UnqualifiedTyposCorrected[Typo]; - return DeclarationName(); + return TypoCorrection(); } - // Weed out any names that could not be found by name lookup. - bool LastLookupWasAccepted = false; - for (TypoCorrectionConsumer::iterator I = Consumer.begin(), - IEnd = Consumer.end(); - I != IEnd; /* Increment in loop. */) { - // Keywords are always found. - if (I->second) { - ++I; - continue; - } - - // Perform name lookup on this name. - IdentifierInfo *Name = &Context.Idents.get(I->getKey()); - LookupPotentialTypoResult(*this, Res, Name, S, SS, MemberContext, - EnteringContext, CTC); - - switch (Res.getResultKind()) { - case LookupResult::NotFound: - case LookupResult::NotFoundInCurrentInstantiation: - case LookupResult::Ambiguous: - // We didn't find this name in our scope, or didn't like what we found; - // ignore it. - Res.suppressDiagnostics(); - { - TypoCorrectionConsumer::iterator Next = I; - ++Next; - Consumer.erase(I); - I = Next; - } - LastLookupWasAccepted = false; - break; - - case LookupResult::Found: - case LookupResult::FoundOverloaded: - case LookupResult::FoundUnresolvedValue: - ++I; - LastLookupWasAccepted = true; - break; - } - - if (Res.isAmbiguous()) { - // We don't deal with ambiguities. - Res.suppressDiagnostics(); - Res.clear(); - return DeclarationName(); + // If we have multiple possible corrections, eliminate the ones where we + // added namespace qualifiers to try to resolve the ambiguity (and to favor + // corrections without additional namespace qualifiers) + if (getLangOptions().CPlusPlus && BestResults.size() > 1) { + TypoCorrectionConsumer::distance_iterator DI = Consumer.begin(); + for (TypoCorrectionConsumer::result_iterator I = DI->second->begin(), + IEnd = DI->second->end(); + I != IEnd; /* Increment in loop. */) { + if (I->second.getCorrectionSpecifier() != NULL) { + TypoCorrectionConsumer::result_iterator Cur = I; + ++I; + DI->second->erase(Cur); + } else ++I; } } // If only a single name remains, return that result. - if (Consumer.size() == 1) { - IdentifierInfo *Name = &Context.Idents.get(Consumer.begin()->getKey()); - if (Consumer.begin()->second) { - Res.suppressDiagnostics(); - Res.clear(); - - // Don't correct to a keyword that's the same as the typo; the keyword - // wasn't actually in scope. - if (ED == 0) { - Res.setLookupName(Typo); - return DeclarationName(); - } + if (BestResults.size() == 1) { + const llvm::StringMapEntry<TypoCorrection> &Correction = *(BestResults.begin()); + const TypoCorrection &Result = Correction.second; - } else if (!LastLookupWasAccepted) { - // Perform name lookup on this name. - LookupPotentialTypoResult(*this, Res, Name, S, SS, MemberContext, - EnteringContext, CTC); - } + // Don't correct to a keyword that's the same as the typo; the keyword + // wasn't actually in scope. + if (ED == 0 && Result.isKeyword()) return TypoCorrection(); // Record the correction for unqualified lookup. if (IsUnqualifiedLookup) - UnqualifiedTyposCorrected[Typo] - = std::make_pair(Name->getName(), Consumer.begin()->second); + UnqualifiedTyposCorrected[Typo] = Result; - return &Context.Idents.get(Consumer.begin()->getKey()); + return Result; } - else if (Consumer.size() > 1 && CTC == CTC_ObjCMessageReceiver - && Consumer["super"]) { - // Prefix 'super' when we're completing in a message-receiver + else if (BestResults.size() > 1 && CTC == CTC_ObjCMessageReceiver + && BestResults["super"].isKeyword()) { + // Prefer 'super' when we're completing in a message-receiver // context. - Res.suppressDiagnostics(); - Res.clear(); // Don't correct to a keyword that's the same as the typo; the keyword // wasn't actually in scope. - if (ED == 0) { - Res.setLookupName(Typo); - return DeclarationName(); - } + if (ED == 0) return TypoCorrection(); // Record the correction for unqualified lookup. if (IsUnqualifiedLookup) - UnqualifiedTyposCorrected[Typo] - = std::make_pair("super", Consumer.begin()->second); + UnqualifiedTyposCorrected[Typo] = BestResults["super"]; - return &Context.Idents.get("super"); + return BestResults["super"]; } - Res.suppressDiagnostics(); - Res.setLookupName(Typo); - Res.clear(); - // Record the correction for unqualified lookup. if (IsUnqualifiedLookup) (void)UnqualifiedTyposCorrected[Typo]; - return DeclarationName(); + return TypoCorrection(); +} + +std::string TypoCorrection::getAsString(const LangOptions &LO) const { + if (CorrectionNameSpec) { + std::string tmpBuffer; + llvm::raw_string_ostream PrefixOStream(tmpBuffer); + CorrectionNameSpec->print(PrefixOStream, PrintingPolicy(LO)); + return PrefixOStream.str() + CorrectionName.getAsString(); + } + + return CorrectionName.getAsString(); } |