From f0c1d8f804e7854bedf3f241409185951ee67866 Mon Sep 17 00:00:00 2001 From: Kaelyn Uhrain Date: Wed, 3 Aug 2011 20:36:05 +0000 Subject: [PATCH] Improve overloaded function handling in the typo correction code. Change TypoCorrection to store a set of NamedDecls instead of a single NamedDecl. Also add initial support for performing function overload resolution to Sema::DiagnoseEmptyLookup. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136807 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 3 +- include/clang/Sema/TypoCorrection.h | 71 ++++++++++++++----- lib/Sema/SemaExpr.cpp | 24 ++++++- lib/Sema/SemaLookup.cpp | 48 ++++++++++--- lib/Sema/SemaOverload.cpp | 3 +- test/FixIt/typo-crash.cpp | 11 +-- test/SemaCXX/function-overload-typo-crash.cpp | 4 +- 7 files changed, 126 insertions(+), 38 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 921cc716ef..d9b320d51d 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2236,7 +2236,8 @@ public: const TemplateArgumentListInfo *&TemplateArgs); bool DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, - CorrectTypoContext CTC = CTC_Unknown); + CorrectTypoContext CTC = CTC_Unknown, + Expr **Args = 0, unsigned NumArgs = 0); ExprResult LookupInObjCMethod(LookupResult &R, Scope *S, IdentifierInfo *II, bool AllowBuiltinCreation=false); diff --git a/include/clang/Sema/TypoCorrection.h b/include/clang/Sema/TypoCorrection.h index 9965953538..480a71af89 100644 --- a/include/clang/Sema/TypoCorrection.h +++ b/include/clang/Sema/TypoCorrection.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_SEMA_TYPOCORRECTION_H #include "clang/AST/DeclCXX.h" +#include "llvm/ADT/SmallVector.h" namespace clang { @@ -23,29 +24,31 @@ namespace clang { class TypoCorrection { public: TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl, - NestedNameSpecifier *NNS=NULL, unsigned distance=0) + NestedNameSpecifier *NNS=0, unsigned distance=0) : CorrectionName(Name), CorrectionNameSpec(NNS), - CorrectionDecl(NameDecl), - EditDistance(distance) {} + EditDistance(distance) { + if (NameDecl) + CorrectionDecls.push_back(NameDecl); + } - TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS=NULL, + TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS=0, unsigned distance=0) : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS), - CorrectionDecl(Name), - EditDistance(distance) {} + EditDistance(distance) { + if (Name) + CorrectionDecls.push_back(Name); + } - TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS=NULL, + TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS=0, unsigned distance=0) : CorrectionName(Name), CorrectionNameSpec(NNS), - CorrectionDecl(NULL), - EditDistance(distance) {} + EditDistance(distance) {} TypoCorrection() - : CorrectionName(), CorrectionNameSpec(NULL), CorrectionDecl(NULL), - EditDistance(0) {} + : CorrectionNameSpec(0), EditDistance(0) {} /// \brief Gets the DeclarationName of the typo correction DeclarationName getCorrection() const { return CorrectionName; } @@ -66,37 +69,67 @@ public: /// \brief Gets the pointer to the declaration of the typo correction NamedDecl* getCorrectionDecl() const { - return isKeyword() ? NULL : CorrectionDecl; + return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0; } template DeclClass *getCorrectionDeclAs() const { return dyn_cast_or_null(getCorrectionDecl()); } + /// \brief Clears the list of NamedDecls before adding the new one. void setCorrectionDecl(NamedDecl *CDecl) { - CorrectionDecl = CDecl; - if (!CorrectionName) - CorrectionName = CDecl->getDeclName(); + CorrectionDecls.clear(); + addCorrectionDecl(CDecl); } + /// \brief Add the given NamedDecl to the list of NamedDecls that are the + /// declarations associated with the DeclarationName of this TypoCorrection + void addCorrectionDecl(NamedDecl *CDecl); + std::string getAsString(const LangOptions &LO) const; std::string getQuoted(const LangOptions &LO) const { return "'" + getAsString(LO) + "'"; } + /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName operator bool() const { return bool(CorrectionName); } - static inline NamedDecl *KeywordDecl() { return (NamedDecl*)-1; } - bool isKeyword() const { return CorrectionDecl == KeywordDecl(); } + /// \brief Mark this TypoCorrection as being a keyword. + /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be + /// added to the list of the correction's NamedDecl pointers, NULL is added + /// as the only element in the list to mark this TypoCorrection as a keyword. + void makeKeyword() { + CorrectionDecls.clear(); + CorrectionDecls.push_back(0); + } + + // Check if this TypoCorrection is a keyword by checking if the first + // item in CorrectionDecls is NULL. + bool isKeyword() const { + return !CorrectionDecls.empty() && + CorrectionDecls.front() == 0; + } // Returns true if the correction either is a keyword or has a known decl. - bool isResolved() const { return CorrectionDecl != NULL; } + bool isResolved() const { return !CorrectionDecls.empty(); } + + bool isOverloaded() const { + return CorrectionDecls.size() > 1; + } + + typedef llvm::SmallVector::iterator decl_iterator; + decl_iterator begin() { return CorrectionDecls.begin(); } + decl_iterator end() { return CorrectionDecls.end(); } private: + bool hasCorrectionDecl() const { + return (!isKeyword() && !CorrectionDecls.empty()); + } + // Results. DeclarationName CorrectionName; NestedNameSpecifier *CorrectionNameSpec; - NamedDecl *CorrectionDecl; + llvm::SmallVector CorrectionDecls; unsigned EditDistance; }; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 55e04b79de..6ace3e92e6 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1364,7 +1364,8 @@ Sema::DecomposeUnqualifiedId(const UnqualifiedId &Id, /// /// \return false if new lookup candidates were found bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, - CorrectTypoContext CTC) { + CorrectTypoContext CTC, Expr **Args, + unsigned NumArgs) { DeclarationName Name = R.getLookupName(); unsigned diagnostic = diag::err_undeclared_var_use; @@ -1450,6 +1451,27 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, R.setLookupName(Corrected.getCorrection()); if (NamedDecl *ND = Corrected.getCorrectionDecl()) { + if (Corrected.isOverloaded()) { + OverloadCandidateSet OCS(R.getNameLoc()); + OverloadCandidateSet::iterator Best; + for (TypoCorrection::decl_iterator CD = Corrected.begin(), + CDEnd = Corrected.end(); + CD != CDEnd; ++CD) { + if (FunctionDecl *FD = dyn_cast(*CD)) + AddOverloadCandidate(FD, DeclAccessPair::make(*CD, AS_none), + Args, NumArgs, OCS); + // TODO: Handle FunctionTemplateDecl and other Decl types that + // support overloading and could be corrected by CorrectTypo. + } + switch (OCS.BestViableFunction(*this, R.getNameLoc(), Best)) { + case OR_Success: + ND = Best->Function; + break; + default: + // Don't try to recover; it won't work. + return true; + } + } R.addDecl(ND); if (isa(ND) || isa(ND)) { if (SS.isEmpty()) diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index c5ba95a1f4..240eb5f1bf 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -3020,7 +3020,7 @@ public: void FoundName(StringRef Name); void addKeywordResult(StringRef Keyword); void addName(StringRef Name, NamedDecl *ND, unsigned Distance, - NestedNameSpecifier *NNS=NULL); + NestedNameSpecifier *NNS=NULL, bool isKeyword=false); void addCorrection(TypoCorrection Correction); typedef TypoResultsMap::iterator result_iterator; @@ -3099,15 +3099,17 @@ void TypoCorrectionConsumer::addKeywordResult(StringRef Keyword) { return; } - addName(Keyword, TypoCorrection::KeywordDecl(), ED); + addName(Keyword, NULL, ED, NULL, true); } void TypoCorrectionConsumer::addName(StringRef Name, NamedDecl *ND, unsigned Distance, - NestedNameSpecifier *NNS) { - addCorrection(TypoCorrection(&SemaRef.Context.Idents.get(Name), - ND, NNS, Distance)); + NestedNameSpecifier *NNS, + bool isKeyword) { + TypoCorrection TC(&SemaRef.Context.Idents.get(Name), ND, NNS, Distance); + if (isKeyword) TC.makeKeyword(); + addCorrection(TC); } void TypoCorrectionConsumer::addCorrection(TypoCorrection Correction) { @@ -3677,12 +3679,19 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // We don't deal with ambiguities. return TypoCorrection(); + case LookupResult::FoundOverloaded: { + // Store all of the Decls for overloaded symbols + for (LookupResult::iterator TRD = TmpRes.begin(), + TRDEnd = TmpRes.end(); + TRD != TRDEnd; ++TRD) + I->second.addCorrectionDecl(*TRD); + ++I; + break; + } + case LookupResult::Found: - case LookupResult::FoundOverloaded: case LookupResult::FoundUnresolvedValue: I->second.setCorrectionDecl(TmpRes.getAsSingle()); - // FIXME: This sets the CorrectionDecl to NULL for overloaded functions. - // It would be nice to find the right one with overload resolution. ++I; break; } @@ -3718,11 +3727,20 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, switch (TmpRes.getResultKind()) { case LookupResult::Found: - case LookupResult::FoundOverloaded: case LookupResult::FoundUnresolvedValue: Consumer.addName((*QRI)->getName(), TmpRes.getAsSingle(), QualifiedED, NI->NameSpecifier); break; + case LookupResult::FoundOverloaded: { + TypoCorrection corr(&Context.Idents.get((*QRI)->getName()), NULL, + NI->NameSpecifier, QualifiedED); + for (LookupResult::iterator TRD = TmpRes.begin(), + TRDEnd = TmpRes.end(); + TRD != TRDEnd; ++TRD) + corr.addCorrectionDecl(*TRD); + Consumer.addCorrection(corr); + break; + } case LookupResult::NotFound: case LookupResult::NotFoundInCurrentInstantiation: case LookupResult::Ambiguous: @@ -3802,6 +3820,18 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, return TypoCorrection(); } +void TypoCorrection::addCorrectionDecl(NamedDecl *CDecl) { + if (!CDecl) return; + + if (isKeyword()) + CorrectionDecls.clear(); + + CorrectionDecls.push_back(CDecl); + + if (!CorrectionName) + CorrectionName = CDecl->getDeclName(); +} + std::string TypoCorrection::getAsString(const LangOptions &LO) const { if (CorrectionNameSpec) { std::string tmpBuffer; diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 79aa305b86..712720bf9e 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -8220,7 +8220,8 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, if (!DiagnoseTwoPhaseLookup(SemaRef, Fn->getExprLoc(), SS, R, ExplicitTemplateArgs, Args, NumArgs) && (!EmptyLookup || - SemaRef.DiagnoseEmptyLookup(S, SS, R, Sema::CTC_Expression))) + SemaRef.DiagnoseEmptyLookup(S, SS, R, Sema::CTC_Expression, + Args, NumArgs))) return ExprError(); assert(!R.empty() && "lookup results empty despite recovery"); diff --git a/test/FixIt/typo-crash.cpp b/test/FixIt/typo-crash.cpp index b156e1b44c..8f0c989cce 100644 --- a/test/FixIt/typo-crash.cpp +++ b/test/FixIt/typo-crash.cpp @@ -3,9 +3,10 @@ // FIXME: The diagnostics and recovery here are very, very poor. // PR10355 -template void template_id1() { - template_id2<> t; // expected-error 2{{use of undeclared identifier 'template_id2'; did you mean 'template_id1'?}} \ - // expected-error{{expected expression}} \ - // expected-error{{use of undeclared identifier 't'}} +template void template_id1() { // expected-note {{'template_id1' declared here}} \ + // expected-note {{candidate function}} + template_id2<> t; // expected-error {{no template named 'template_id2'; did you mean 'template_id1'?}} \ + // expected-error {{expected ';' after expression}} \ + // expected-error {{cannot resolve overloaded function 'template_id1' from context}} \ + // expected-error {{use of undeclared identifier 't'}} } - diff --git a/test/SemaCXX/function-overload-typo-crash.cpp b/test/SemaCXX/function-overload-typo-crash.cpp index 0fea312a97..580f27a12a 100644 --- a/test/SemaCXX/function-overload-typo-crash.cpp +++ b/test/SemaCXX/function-overload-typo-crash.cpp @@ -1,10 +1,10 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s // PR10283 -void min(); +void min(); //expected-note {{'min' declared here}} void min(int); -template void max(T); +template void max(T); //expected-note {{'max' declared here}} void f() { fin(); //expected-error {{use of undeclared identifier 'fin'; did you mean 'min'}} -- 2.40.0