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

Reply via email to