This patch improves how typo correction deals with overloaded functions,
e.g. Sema::CorrectTypo now returns a set of Decls in the TypoCorrection if
the lookup of a correction has a result kind of
LookupResult::FoundOverloaded, and Sema::DiagnoseEmptyLookup has been
modified to try to choose the best Decl from the set. With these changes,
TypoCorrection is one step closer to never returning a non-keyword
correction with a NULL Decl. I also ran memcheck using
test/FixIt/typo-crash.cpp as a quick sanity check that I didn't accidentally
introduce any memory leaks with the changes to the TypoCorrection class
(though I think dynamically allocating the UnresolvedSet may be a bit of
overkill and generally unnecessary--thoughts?).
The changes can also be reviewed at: http://codereview.appspot.com/4747047
Cheers,
Kaelyn
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 263c1bd..d979192 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -2205,7 +2205,8 @@ public:
const TemplateArgumentListInfo *&TemplateArgs);
bool DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
- CorrectTypoContext CTC = CTC_Unknown);
+ CorrectTypoContext CTC = CTC_Unknown,
+ Expr **Args = NULL, 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 9965953..bd33591 100644
--- a/include/clang/Sema/TypoCorrection.h
+++ b/include/clang/Sema/TypoCorrection.h
@@ -16,36 +16,72 @@
#define LLVM_CLANG_SEMA_TYPOCORRECTION_H
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/UnresolvedSet.h"
namespace clang {
/// @brief Simple class containing the result of Sema::CorrectTypo
class TypoCorrection {
+ typedef UnresolvedSet<4> CorrectionDeclSet;
public:
TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
NestedNameSpecifier *NNS=NULL, unsigned distance=0)
: CorrectionName(Name),
CorrectionNameSpec(NNS),
- CorrectionDecl(NameDecl),
- EditDistance(distance) {}
+ CorrectionDecls(NULL),
+ EditDistance(distance),
+ IsKeyword(false) {
+ if (NameDecl == KeywordDecl())
+ IsKeyword = true;
+ else if (NameDecl) {
+ CorrectionDecls = new CorrectionDeclSet;
+ CorrectionDecls->addDecl(NameDecl);
+ }
+ }
TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS=NULL,
unsigned distance=0)
: CorrectionName(Name->getDeclName()),
CorrectionNameSpec(NNS),
- CorrectionDecl(Name),
- EditDistance(distance) {}
+ CorrectionDecls(NULL),
+ EditDistance(distance),
+ IsKeyword(false) {
+ if (Name == KeywordDecl())
+ IsKeyword = true;
+ else if (Name) {
+ CorrectionDecls = new CorrectionDeclSet;
+ CorrectionDecls->addDecl(Name);
+ }
+ }
TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS=NULL,
unsigned distance=0)
: CorrectionName(Name),
CorrectionNameSpec(NNS),
- CorrectionDecl(NULL),
- EditDistance(distance) {}
+ CorrectionDecls(NULL),
+ EditDistance(distance),
+ IsKeyword(false) {}
TypoCorrection()
- : CorrectionName(), CorrectionNameSpec(NULL), CorrectionDecl(NULL),
- EditDistance(0) {}
+ : CorrectionName(), CorrectionNameSpec(NULL), CorrectionDecls(NULL),
+ EditDistance(0), IsKeyword(false) {}
+
+ TypoCorrection(const TypoCorrection &other)
+ : CorrectionName(other.CorrectionName),
+ CorrectionNameSpec(other.CorrectionNameSpec),
+ CorrectionDecls(NULL),
+ EditDistance(other.EditDistance),
+ IsKeyword(other.IsKeyword) {
+ if (other.CorrectionDecls)
+ CorrectionDecls = new CorrectionDeclSet(*other.CorrectionDecls);
+ }
+
+ ~TypoCorrection() {
+ if (CorrectionDecls)
+ delete CorrectionDecls;
+ }
+
+ TypoCorrection &operator=(const TypoCorrection &other);
/// \brief Gets the DeclarationName of the typo correction
DeclarationName getCorrection() const { return CorrectionName; }
@@ -66,18 +102,15 @@ public:
/// \brief Gets the pointer to the declaration of the typo correction
NamedDecl* getCorrectionDecl() const {
- return isKeyword() ? NULL : CorrectionDecl;
+ return hasCorrectionDecl() ? *(CorrectionDecls->begin()) : NULL;
}
template <class DeclClass>
DeclClass *getCorrectionDeclAs() const {
return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
}
- void setCorrectionDecl(NamedDecl *CDecl) {
- CorrectionDecl = CDecl;
- if (!CorrectionName)
- CorrectionName = CDecl->getDeclName();
- }
+ void setCorrectionDecl(NamedDecl *CDecl);
+ void addCorrectionDecl(NamedDecl *CDecl);
std::string getAsString(const LangOptions &LO) const;
std::string getQuoted(const LangOptions &LO) const {
@@ -87,17 +120,31 @@ public:
operator bool() const { return bool(CorrectionName); }
static inline NamedDecl *KeywordDecl() { return (NamedDecl*)-1; }
- bool isKeyword() const { return CorrectionDecl == KeywordDecl(); }
+ bool isKeyword() const { return IsKeyword; }
// Returns true if the correction either is a keyword or has a known decl.
- bool isResolved() const { return CorrectionDecl != NULL; }
+ bool isResolved() const { return isKeyword() || hasCorrectionDecl(); }
+
+ bool isOverloaded() const {
+ return hasCorrectionDecl() && CorrectionDecls->size() > 1;
+ }
+
+ const CorrectionDeclSet *getCorrectionDecls() const { return CorrectionDecls; }
+ typedef CorrectionDeclSet::iterator decl_iterator;
+ decl_iterator begin() { return CorrectionDecls->begin(); }
+ decl_iterator end() { return CorrectionDecls->end(); }
private:
+ bool hasCorrectionDecl() const {
+ return (!isKeyword() && CorrectionDecls && !CorrectionDecls->empty());
+ }
+
// Results.
DeclarationName CorrectionName;
NestedNameSpecifier *CorrectionNameSpec;
- NamedDecl *CorrectionDecl;
+ CorrectionDeclSet *CorrectionDecls;
unsigned EditDistance;
+ bool IsKeyword;
};
}
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 5efc365..875692e 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -1319,7 +1319,8 @@ void 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;
@@ -1405,6 +1406,20 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
R.setLookupName(Corrected.getCorrection());
if (NamedDecl *ND = Corrected.getCorrectionDecl()) {
+ if (Args && Corrected.isOverloaded()) {
+ OverloadCandidateSet OCS(R.getNameLoc());
+ OverloadCandidateSet::iterator Best;
+ AddFunctionCandidates(*Corrected.getCorrectionDecls(), Args, NumArgs, OCS);
+ switch (OCS.BestViableFunction(*this, R.getNameLoc(), Best)) {
+ case OR_Success:
+ case OR_Ambiguous:
+ ND = Best->Function;
+ break;
+ default:
+ // Don't try to recover; it won't work.
+ return true;
+ }
+ }
R.addDecl(ND);
if (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)) {
if (SS.isEmpty())
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index 0e448e3..3e82432 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -3745,8 +3745,17 @@ 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<NamedDecl>());
// FIXME: This sets the CorrectionDecl to NULL for overloaded functions.
@@ -3786,11 +3795,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<NamedDecl>(),
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:
@@ -3870,6 +3888,38 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
return TypoCorrection();
}
+TypoCorrection& TypoCorrection::operator=(const TypoCorrection &other) {
+ CorrectionName = other.CorrectionName;
+ CorrectionNameSpec = other.CorrectionNameSpec;
+ EditDistance = other.EditDistance;
+ IsKeyword = other.IsKeyword;
+ if (other.CorrectionDecls)
+ CorrectionDecls = new CorrectionDeclSet(*other.CorrectionDecls);
+ else
+ CorrectionDecls = NULL;
+ return *this;
+}
+
+void TypoCorrection::setCorrectionDecl(NamedDecl *CDecl) {
+ if (CDecl == KeywordDecl()) {
+ IsKeyword = true;
+ return;
+ } else if (CorrectionDecls)
+ CorrectionDecls->clear();
+
+ addCorrectionDecl(CDecl);
+}
+
+void TypoCorrection::addCorrectionDecl(NamedDecl *CDecl) {
+ if (!CDecl || CDecl == KeywordDecl()) return;
+ if (!CorrectionDecls)
+ CorrectionDecls = new CorrectionDeclSet;
+
+ CorrectionDecls->addDecl(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 437b2b5..db45e79 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -8164,7 +8164,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 b156e1b..8f0c989 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<typename T> 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<typename T> 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 0fea312..580f27a 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 <typename T> void max(T);
+template <typename T> void max(T); //expected-note {{'max' declared here}}
void f() {
fin(); //expected-error {{use of undeclared identifier 'fin'; did you mean 'min'}}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits