I just finished up a new version of my patch earlier this week, which no
longer uses the RecursiveASTVisitor (per chandlerc's suggestion).  I've
rebased the patch against the current SVN and have attached it for you to
test.  With the new version only the FixIt/typo.cpp and
SemaObjC/synth-provisional-ivars.m tests are failing, and for those two I'm
not entirely sure which parts of the behavior are correct/acceptable and
which might be a bug in my changes.

Cheers,
Kaelyn

On Tue, Mar 8, 2011 at 7:46 PM, Douglas Gregor <[email protected]> wrote:

>
> On Feb 22, 2011, at 11:21 AM, Kaelyn Uhrain wrote:
>
> > This patch implements checking available C++ namespaces when trying to
> find typo corrections for unknown identifiers.  There are currently 4 broken
> unit tests (CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp,
> CXX/class/class.local/p1.cpp, FixIt/typo.cpp, and
> SemaObjC/synth-provisional-ivars.m) which I'm fairly sure are cases where
> the tests need to be updated to reflect the new behavior instead of being
> indicative of actual code breakage, but I'm not 100% certain.  The new unit
> test, test/Sema/missing-namespace-qualifier-typo-corrections.cpp, is a good
> demonstration of the improved suggestions provided by this patch as most of
> the errors found by "clang -fsyntax-only" lack suggestions without this
> patch..
>
> This is really cool, and is be a great step forward in usability.
>
> My only really concern is about performance, so I did a quick measurement.
> My test was to put all of the C++ Standard Library headers into a PCH file,
> then include it in a program that amounted to, basically,
>
>        int main() {
>                vector<int> v;
>                std::Vector<int> v2;
>        }
>
> so there are two typo corrections. And here is some timing data from
> before/after the patch:
>
> Before:
>
> *** AST File Statistics:
>  18 stat cache hits
>  2 stat cache misses
>  97/39174 source location entries read (0.247613%)
>  11142/18925 types read (58.874504%)
>  16712/32353 declarations read (51.655178%)
>  1734/6785 identifiers read (25.556374%)
>  1861/70867 statements read (2.626046%)
>  0/1766 macros read (0.000000%)
>  188/3464 lexical declcontexts read (5.427252%)
>  43/695 visible declcontexts read (6.187050%)
>
> Wall time: 0m0.051s
>
> After:
>
> *** AST File Statistics:
>  18 stat cache hits
>  2 stat cache misses
>  97/39174 source location entries read (0.247613%)
>  17162/18925 types read (90.684280%)
>  28421/32353 declarations read (87.846565%)
>  3943/6785 identifiers read (58.113487%)
>  70371/70867 statements read (99.300095%)
>  0/1766 macros read (0.000000%)
>  3383/3464 lexical declcontexts read (97.661659%)
>  6080/695 visible declcontexts read (874.820129%)
>
> Wall time: 0m0.115s
>
> So it's a little over 2x slower, and (more importantly) deserializes a
> whole lot more from the PCH file. That's actually a concern, since typo
> correction still needs to be fast for many applications, and the additional
> disk I/O really becomes a problem as the PCH files get larger (e.g.,
> containing significant chunks of Boost).
>
> I have a recommendation. Rather than using the recursive AST visitor to
> walk the entire AST, can we instead
>
>  (1) First do the "identifier" lookup that we already do, to find the best
> name that we know about (which could be anywhere in the translation unit),
> then
>  (2) If lookup for that identifier fails, go look in all of the namespaces
> that we know about and see if the identifier is located in one of those.
>
> We'd have to have some list of known namespaces stored somewhere (and saved
> in the PCH file for fast retrieval), but at least this way we'd be paying
> the cost we already pay now to look at the identifiers + a cost that's
> O(number of unique namespaces).
>
> Does that seem reasonable?
>
>
>        - Doug
Index: test/Sema/missing-namespace-qualifier-typo-corrections.cpp
===================================================================
--- test/Sema/missing-namespace-qualifier-typo-corrections.cpp	(revision 0)
+++ test/Sema/missing-namespace-qualifier-typo-corrections.cpp	(revision 0)
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+namespace fizbin { class Foobar; } // expected-note{{'fizbin::Foobar' declared here}}
+Foobar *my_bar = new Foobar; // expected-error{{unknown type name 'Foobar'; did you mean 'fizbin::Foobar'?}} \
+                             // expected-error{{expected a type}}
+
+namespace barstool { int toFoobar() { return 1; } } // expected-note 3 {{'barstool::toFoobar' declared here}}
+int Double(int x) { return x + x; }
+void empty() {
+  Double(toFoobar()); // expected-error{{{use of undeclared identifier 'toFoobar'; did you mean 'barstool::toFoobar'?}}
+}
+
+namespace fizbin {
+  namespace baztool { bool toFoobar() { return true; } } // expected-note{{'fizbin::baztool' declared here}}
+  namespace nested { bool moreFoobar() { return true; } } // expected-note{{'fizbin::nested' declared here}} \
+                                                          // expected-note{{'fizbin::nested::moreFoobar' declared here}}
+  namespace nested { bool lessFoobar() { return true; } } // expected-note{{'fizbin::nested::lessFoobar' declared here}}
+  class dummy { // expected-note 2 {{'fizbin::dummy' declared here}}
+   public:
+    static bool moreFoobar() { return false; } // expected-note{{'fizbin::dummy::moreFoobar' declared here}}
+  };
+}
+void Check() { // expected-note{{'Check' declared here}}
+  if (toFoobar()) Double(7); // expected-error{{use of undeclared identifier 'toFoobar'; did you mean 'barstool::toFoobar'?}}
+  if (noFoobar()) Double(7); // expected-error{{use of undeclared identifier 'noFoobar'; did you mean 'barstool::toFoobar'?}}
+  if (moreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'moreFoobar'; did you mean 'fizbin::nested::moreFoobar'}}
+  if (lessFoobar()) Double(7); // expected-error{{use of undeclared identifier 'lessFoobar'; did you mean 'fizbin::nested::lessFoobar'?}}
+  if (baztool::toFoobar()) Double(7); // expected-error{{use of undeclared identifier 'baztool'; did you mean 'fizbin::baztool'?}}
+  if (nested::moreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'nested'; did you mean 'fizbin::nested'?}}
+  if (dummy::moreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}}
+  if (dummy::mreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}} \
+                                     // expected-error{{no member named 'mreFoobar' in 'fizbin::dummy'; did you mean 'fizbin::dummy::moreFoobar'?}}
+  if (moFoobin()) Double(7); // expected-error{{use of undeclared identifier 'moFoobin'}}
+}
+
+void Alt() {
+  Cleck(); // expected-error{{{use of undeclared identifier 'Cleck'; did you mean 'Check'?}}
+}
Index: test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp
===================================================================
--- test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp	(revision 127346)
+++ test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp	(working copy)
@@ -19,8 +19,9 @@
 }
 
 namespace C {
-  class C {};
-  void func(C);
+  class C {}; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B::B' to 'const C::C &' for 1st argument}}
+  void func(C); // expected-note {{'C::func' declared here}} \
+                // expected-note {{passing argument to parameter here}}
   C operator+(C,C);
   D::D operator+(D::D,D::D);
 }
@@ -32,7 +33,8 @@
 namespace Test {
   void test() {
     func(A::A());
-    func(B::B()); // expected-error {{use of undeclared identifier 'func'}}
+    func(B::B()); // expected-error {{use of undeclared identifier 'func'}} \
+                  // expected-error {{no viable conversion from 'B::B' to 'C::C'}}
     func(C::C());
     A::A() + A::A();
     B::B() + B::B();
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp	(revision 127346)
+++ lib/Sema/SemaDecl.cpp	(working copy)
@@ -264,24 +264,24 @@
 
   if (DeclarationName Corrected = CorrectTypo(Lookup, S, SS, 0, 0, CTC_Type)) {
     if (NamedDecl *Result = Lookup.getAsSingle<NamedDecl>()) {
+      std::string Suggestion = Result->getQualifiedNameAsString();
+      // FIXME: Silly formatting rules make us have to quote strings ourselves
+      // to keep quoting consistent between strings and IdentiferInfos.
+      std::string QuotedSug = "'" + Suggestion + "'";
       if ((isa<TypeDecl>(Result) || isa<ObjCInterfaceDecl>(Result)) &&
           !Result->isInvalidDecl()) {
         // We found a similarly-named type or interface; suggest that.
         if (!SS || !SS->isSet())
-          Diag(IILoc, diag::err_unknown_typename_suggest)
-            << &II << Lookup.getLookupName()
-            << FixItHint::CreateReplacement(SourceRange(IILoc),
-                                            Result->getNameAsString());
+          Diag(IILoc, diag::err_unknown_typename_suggest) << &II << QuotedSug
+            << FixItHint::CreateReplacement(SourceRange(IILoc), Suggestion);
         else if (DeclContext *DC = computeDeclContext(*SS, false))
-          Diag(IILoc, diag::err_unknown_nested_typename_suggest) 
-            << &II << DC << Lookup.getLookupName() << SS->getRange()
-            << FixItHint::CreateReplacement(SourceRange(IILoc),
-                                            Result->getNameAsString());
+          Diag(IILoc, diag::err_unknown_nested_typename_suggest)
+            << &II << DC << QuotedSug << SS->getRange()
+            << FixItHint::CreateReplacement(SourceRange(IILoc), Suggestion);
         else
           llvm_unreachable("could not have corrected a typo here");
 
-        Diag(Result->getLocation(), diag::note_previous_decl)
-          << Result->getDeclName();
+        Diag(Result->getLocation(), diag::note_previous_decl) << QuotedSug;
         
         SuggestedType = getTypeName(*Result->getIdentifier(), IILoc, S, SS,
                                     false, false, ParsedType(),
Index: lib/Sema/SemaCXXScopeSpec.cpp
===================================================================
--- lib/Sema/SemaCXXScopeSpec.cpp	(revision 127346)
+++ lib/Sema/SemaCXXScopeSpec.cpp	(working copy)
@@ -460,24 +460,26 @@
     // We haven't found anything, and we're not recovering from a
     // different kind of error, so look for typos.
     DeclarationName Name = Found.getLookupName();
-    if (CorrectTypo(Found, S, &SS, LookupCtx, EnteringContext,  
+    if (CorrectTypo(Found, S, &SS, LookupCtx, EnteringContext,
                     CTC_NoKeywords) &&
         Found.isSingleResult() &&
         isAcceptableNestedNameSpecifier(Found.getAsSingle<NamedDecl>())) {
+      std::string Suggestion =
+          Found.getAsSingle<NamedDecl>()->getQualifiedNameAsString();
+      // FIXME: Silly formatting rules make us have to quote strings ourselves
+      // to keep quoting consistent between strings and IdentiferInfos.
+      std::string QuotedSug = "'" + Suggestion + "'";
       if (LookupCtx)
         Diag(Found.getNameLoc(), diag::err_no_member_suggest)
-          << Name << LookupCtx << Found.getLookupName() << SS.getRange()
-          << FixItHint::CreateReplacement(Found.getNameLoc(),
-                                          Found.getLookupName().getAsString());
+          << Name << LookupCtx << QuotedSug << SS.getRange()
+          << FixItHint::CreateReplacement(Found.getNameLoc(), Suggestion);
       else
         Diag(Found.getNameLoc(), diag::err_undeclared_var_use_suggest)
-          << Name << Found.getLookupName()
-          << FixItHint::CreateReplacement(Found.getNameLoc(),
-                                          Found.getLookupName().getAsString());
-      
+          << Name << QuotedSug
+          << FixItHint::CreateReplacement(Found.getNameLoc(), Suggestion);
+
       if (NamedDecl *ND = Found.getAsSingle<NamedDecl>())
-        Diag(ND->getLocation(), diag::note_previous_decl)
-          << ND->getDeclName();
+        Diag(ND->getLocation(), diag::note_previous_decl) << QuotedSug;
     } else {
       Found.clear();
       Found.setLookupName(&Identifier);
Index: lib/Sema/SemaLookup.cpp
===================================================================
--- lib/Sema/SemaLookup.cpp	(revision 127346)
+++ lib/Sema/SemaLookup.cpp	(working copy)
@@ -41,6 +41,7 @@
 #include <iterator>
 #include <utility>
 #include <algorithm>
+#include <deque>
 
 using namespace clang;
 using namespace sema;
@@ -2802,69 +2803,80 @@
 //===----------------------------------------------------------------------===//
 
 namespace {
+typedef std::pair<DeclContext*, NamedDecl*> DeclSpecPair;
+typedef std::deque<DeclSpecPair> DeclSpecList;
+
 class TypoCorrectionConsumer : public VisibleDeclConsumer {
   /// \brief The name written that is a typo in the source.
   llvm::StringRef Typo;
 
+  /// \brief The chain of DeclContexts from the top of the translation unit to
+  /// the current context, inclusive.
+  DeclContext* CurContext;
+
   /// \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;
+  /// A NULL pointer at the front of the std::deque<DeclContext*> indicates there is
+  /// a keyword with this name.
+  llvm::StringMap<DeclSpecList, llvm::BumpPtrAllocator> BestResults;
 
   /// \brief The best edit distance found so far.
   unsigned BestEditDistance;
 
 public:
-  explicit TypoCorrectionConsumer(IdentifierInfo *Typo)
-    : Typo(Typo->getName()),
-      BestEditDistance((std::numeric_limits<unsigned>::max)()) { }
+  explicit TypoCorrectionConsumer(IdentifierInfo *Typo, DeclContext *CurContext)
+    : Typo(Typo->getName()), CurContext(CurContext),
+      BestEditDistance((std::numeric_limits<unsigned>::max)() - 1) { }
 
-  virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, bool InBaseClass);
+  virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, bool InBaseClass) {
+    FoundDecl(ND, Hiding, InBaseClass, 0);
+  };
+  void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, bool InBaseClass,
+                 unsigned DistOffset);
   void FoundName(llvm::StringRef Name);
   void addKeywordResult(ASTContext &Context, llvm::StringRef Keyword);
 
-  typedef llvm::StringMap<bool, llvm::BumpPtrAllocator>::iterator iterator;
+  typedef llvm::StringMap<DeclSpecList,
+                          llvm::BumpPtrAllocator>::iterator iterator;
   iterator begin() { return BestResults.begin(); }
   iterator end()  { return BestResults.end(); }
+  iterator find(llvm::StringRef Key) { return BestResults.find(Key); }
   void erase(iterator I) { BestResults.erase(I); }
   unsigned size() const { return BestResults.size(); }
   bool empty() const { return BestResults.empty(); }
 
-  bool &operator[](llvm::StringRef Name) {
+  DeclSpecList &operator[](llvm::StringRef Name) {
     return BestResults[Name];
   }
 
   unsigned getBestEditDistance() const { return BestEditDistance; }
-};
 
-}
+  static bool isKeyword(iterator I) {
+    return I->second.front().first == NULL;
+  }
 
-void TypoCorrectionConsumer::FoundDecl(NamedDecl *ND, NamedDecl *Hiding,
-                                       bool InBaseClass) {
-  // Don't consider hidden names for typo correction.
-  if (Hiding)
-    return;
-
-  // Only consider entities with identifiers for names, ignoring
-  // special names (constructors, overloaded operators, selectors,
-  // etc.).
-  IdentifierInfo *Name = ND->getIdentifier();
-  if (!Name)
-    return;
-
-  FoundName(Name->getName());
+private:
+  unsigned getEstimatedMinDistance(llvm::StringRef Name) const;
+  void CheckEditDistanceAndStore(llvm::StringRef Name, DeclSpecPair DSPair,
+                                 unsigned DistOffset);
+};
 }
 
-void TypoCorrectionConsumer::FoundName(llvm::StringRef Name) {
-  using namespace std;
-
+unsigned TypoCorrectionConsumer::getEstimatedMinDistance(
+    llvm::StringRef Name) const {
   // 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());
+  unsigned MinED = std::abs((int)Name.size() - (int)Typo.size());
   if (MinED > BestEditDistance || (MinED && Typo.size() / MinED < 3))
-    return;
+    return BestEditDistance + 1;
+  return MinED;
+}
 
+void TypoCorrectionConsumer::CheckEditDistanceAndStore(
+    llvm::StringRef Name, DeclSpecPair DSPair, unsigned DistOffset) {
+  using namespace std;
+
   // Compute an upper bound on the allowable edit distance, so that the
   // edit-distance algorithm can short-circuit.
   unsigned UpperBound = min(unsigned((Typo.size() + 2) / 3), BestEditDistance);
@@ -2872,7 +2884,7 @@
   // 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);
+  unsigned ED = Typo.edit_distance(Name, true, UpperBound) + DistOffset;
   if (ED == 0)
     return;
 
@@ -2891,9 +2903,47 @@
   // 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];
+  BestResults[Name].push_front(DSPair);
 }
 
+void TypoCorrectionConsumer::FoundDecl(NamedDecl *ND, NamedDecl *Hiding,
+                                       bool InBaseClass, unsigned DistOffset) {
+  // Don't consider hidden names for typo correction.
+  if (Hiding)
+    return;
+
+  // Only consider entities with identifiers for names, ignoring
+  // special names (constructors, overloaded operators, selectors,
+  // etc.).
+  IdentifierInfo *Name = ND->getIdentifier();
+  if (!Name)
+    return;
+
+  llvm::StringRef ShortName = Name->getName();
+  unsigned MinED = getEstimatedMinDistance(ShortName);
+
+  if (MinED + DistOffset > BestEditDistance)
+    return;
+
+  // Only consider entities that haven't already been found.
+  DeclContext *NDContext = ND->getDeclContext()->getPrimaryContext();
+  iterator N = BestResults.find(ShortName);
+  if (N != BestResults.end()) {
+    for (DeclSpecList::iterator D = N->second.begin(), DEnd = N->second.end();
+         D != DEnd; ++D) {
+      if (NDContext == D->first) return;
+    }
+  }
+
+  CheckEditDistanceAndStore(ShortName, DeclSpecPair(NDContext, ND), DistOffset);
+}
+
+void TypoCorrectionConsumer::FoundName(llvm::StringRef Name) {
+  if (getEstimatedMinDistance(Name) <= BestEditDistance)
+    CheckEditDistanceAndStore(Name,
+                              DeclSpecPair(CurContext, NULL), 0);
+}
+
 void TypoCorrectionConsumer::addKeywordResult(ASTContext &Context,
                                               llvm::StringRef Keyword) {
   // Compute the edit distance between the typo and this keyword.
@@ -2909,7 +2959,7 @@
     return;
   }
 
-  BestResults[Keyword] = true;
+  BestResults[Keyword].push_front(DeclSpecPair(NULL, NULL));
 }
 
 /// \brief Perform name lookup for a possible result for typo correction.
@@ -2963,6 +3013,32 @@
   }
 }
 
+void ScanNamespaceForCorrections(DeclContext *DC,
+                                 TypoCorrectionConsumer &Consumer,
+                                 unsigned DistanceOffset, bool DownOnly) {
+
+  if (DistanceOffset > Consumer.getBestEditDistance())
+    return;
+
+  DeclContext *ParentDC = DC->getLookupParent();
+
+  for (DC = DC->getPrimaryContext(); DC != NULL; DC = DC->getNextContext()) {
+    for (DeclContext::decl_iterator D = DC->decls_begin(),
+         DEnd = DC->decls_end(); D != DEnd; ++D) {
+      NamedDecl *ChildDecl = dyn_cast<NamedDecl>(D->getCanonicalDecl());
+      NamespaceDecl *ChildND = dyn_cast_or_null<NamespaceDecl>(*D);
+      if (ChildDecl)
+        Consumer.FoundDecl(ChildDecl, NULL, false, DistanceOffset);
+      if (ChildND)
+        ScanNamespaceForCorrections(ChildND, Consumer,
+                                    DistanceOffset + 1, true);
+    }
+  }
+
+  if (DownOnly || !ParentDC) return;
+
+  ScanNamespaceForCorrections(ParentDC, Consumer, DistanceOffset, false);
+}
 /// \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.
@@ -3016,7 +3092,8 @@
   if (!ActiveTemplateInstantiations.empty())
     return DeclarationName();
 
-  TypoCorrectionConsumer Consumer(Typo);
+  TypoCorrectionConsumer Consumer(Typo,
+                                  MemberContext ? MemberContext : CurContext);
 
   // Perform name lookup to find visible, similarly-named entities.
   bool IsUnqualifiedLookup = false;
@@ -3047,6 +3124,11 @@
     IsUnqualifiedLookup = true;
     UnqualifiedTyposCorrectedMap::iterator Cached
       = UnqualifiedTyposCorrected.find(Typo);
+
+    // Add namespace-qualifier corrections
+    if (getLangOptions().CPlusPlus)
+      ScanNamespaceForCorrections(CurContext, Consumer, 0, false);
+
     if (Cached == UnqualifiedTyposCorrected.end()) {
       // Provide a stop gap for files that are just seriously broken.  Trying
       // to correct all typos can turn into a HUGE performance penalty, causing
@@ -3054,13 +3136,19 @@
       if (TyposCorrected + UnqualifiedTyposCorrected.size() >= 20)
         return DeclarationName();
 
+      LookupResult R(*this, Res.getLookupNameInfo(), Sema::LookupAnyName);
+      R.suppressDiagnostics();
+
       // For unqualified lookup, look through all of the names that we have
       // seen in this translation unit.
       for (IdentifierTable::iterator I = Context.Idents.begin(),
                                   IEnd = Context.Idents.end();
-           I != IEnd; ++I)
-        Consumer.FoundName(I->getKey());
-
+           I != IEnd; ++I) {
+        llvm::StringRef Name = I->getKey();
+        // Only add names the Consumer doesn't yet know about
+        if (Consumer.find(Name) == Consumer.end())
+          Consumer.FoundName(Name);
+      }
       // Walk through identifiers in external identifier sources.
       if (IdentifierInfoLookup *External
                               = Context.Idents.getExternalIdentifierLookup()) {
@@ -3076,7 +3164,7 @@
     } 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())
+      if (Cached->second.first.empty() || !Cached->second.first.front())
         return DeclarationName();
 
       if (!Cached->second.second)
@@ -3286,22 +3374,58 @@
                                      IEnd = Consumer.end();
        I != IEnd; /* Increment in loop. */) {
     // Keywords are always found.
-    if (I->second) {
+    if (TypoCorrectionConsumer::isKeyword(I)) {
       ++I;
       continue;
     }
 
-    // Perform name lookup on this name.
-    IdentifierInfo *Name = &Context.Idents.get(I->getKey());
-    LookupPotentialTypoResult(*this, Res, Name, S, SS, MemberContext,
-                              EnteringContext, CTC);
+    for (DeclSpecList::iterator DSI = I->second.begin(),
+                                DSIEnd = I->second.end();
+         DSI != DSIEnd; /* Increment in loop, */) {
+      // Perform name lookup on this name.
+      IdentifierInfo *Name = &Context.Idents.get(I->getKey());
+      LookupPotentialTypoResult(*this, Res, Name, S, SS,
+                                DSI->second ? DSI->first : 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.
+      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();
+        {
+          DeclSpecList::iterator Next = DSI;
+          ++Next;
+          I->second.erase(DSI);
+          DSI = Next;
+        }
+        LastLookupWasAccepted = false;
+        break;
+
+      case LookupResult::Found:
+      case LookupResult::FoundOverloaded:
+      case LookupResult::FoundUnresolvedValue:
+        ++DSI;
+        LastLookupWasAccepted = true;
+        break;
+      }
+
+      /*if (Res.isAmbiguous()) {
+        // We don't deal with ambiguities.
+        Res.suppressDiagnostics();
+        {
+          DeclSpecList::iterator Next = DSI;
+          ++Next;
+          I->second.erase(DSI);
+          DSI = Next;
+        }
+        LastLookupWasAccepted = false;
+      }*/
+    }
+
+    if (I->second.empty()) {
       Res.suppressDiagnostics();
       {
         TypoCorrectionConsumer::iterator Next = I;
@@ -3309,29 +3433,24 @@
         Consumer.erase(I);
         I = Next;
       }
-      LastLookupWasAccepted = false;
-      break;
-
-    case LookupResult::Found:
-    case LookupResult::FoundOverloaded:
-    case LookupResult::FoundUnresolvedValue:
+    } else {
       ++I;
-      LastLookupWasAccepted = true;
-      break;
     }
-
-    if (Res.isAmbiguous()) {
-      // We don't deal with ambiguities.
-      Res.suppressDiagnostics();
-      Res.clear();
-      return DeclarationName();
-    }
   }
 
   // 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) {
+  if (Consumer.size() == 1 && Consumer.begin()->second.size() == 1) {
+    TypoCorrectionConsumer::iterator NameEntry = Consumer.begin();
+    DeclSpecPair DSPair = NameEntry->second.front();
+    IdentifierInfo *Name = DSPair.second ? DSPair.second->getIdentifier()
+        : &Context.Idents.get(NameEntry->getKey());
+    if (DSPair.second) {
+      DeclarationName DName = DSPair.second->getDeclName();
+      const char *dn = DName.getAsString().c_str();
+      if (dn)
+        DName.getAsString();
+    }
+    if (TypoCorrectionConsumer::isKeyword(NameEntry)) {
       Res.suppressDiagnostics();
       Res.clear();
 
@@ -3344,19 +3463,28 @@
 
     } else if (!LastLookupWasAccepted) {
       // Perform name lookup on this name.
-      LookupPotentialTypoResult(*this, Res, Name, S, SS, MemberContext,
+      LookupPotentialTypoResult(*this, Res, Name, S, SS,
+                                DSPair.second ? DSPair.first : MemberContext,
                                 EnteringContext, CTC);
     }
 
+    if (DSPair.second) {
+      Res.clear();
+      Res.addDecl(DSPair.second);
+      Res.resolveKind();
+    }
+
     // Record the correction for unqualified lookup.
     if (IsUnqualifiedLookup)
       UnqualifiedTyposCorrected[Typo]
-        = std::make_pair(Name->getName(), Consumer.begin()->second);
+        = std::make_pair(Name->getName(),
+                         TypoCorrectionConsumer::isKeyword(NameEntry));
 
-    return &Context.Idents.get(Consumer.begin()->getKey());
+    return Name;
   }
   else if (Consumer.size() > 1 && CTC == CTC_ObjCMessageReceiver
-           && Consumer["super"]) {
+           && !Consumer["super"].empty()
+           && Consumer["super"].front().first == NULL) {
     // Prefix 'super' when we're completing in a message-receiver
     // context.
     Res.suppressDiagnostics();
@@ -3371,8 +3499,7 @@
 
     // Record the correction for unqualified lookup.
     if (IsUnqualifiedLookup)
-      UnqualifiedTyposCorrected[Typo]
-        = std::make_pair("super", Consumer.begin()->second);
+      UnqualifiedTyposCorrected[Typo] = std::make_pair("super", true);
 
     return &Context.Idents.get("super");
   }
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp	(revision 127346)
+++ lib/Sema/SemaExpr.cpp	(working copy)
@@ -1383,20 +1383,23 @@
   DeclarationName Corrected;
   if (S && (Corrected = CorrectTypo(R, S, &SS, 0, false, CTC))) {
     if (!R.empty()) {
+      const NamedDecl *ND = R.getAsSingle<NamedDecl>();
+      std::string Suggestion = R.getLookupName().getAsString();
+      if (ND) Suggestion = ND->getQualifiedNameAsString();
+      // FIXME: Silly formatting rules make us have to quote strings ourselves
+      // to keep quoting consistent between strings and IdentiferInfos.
+      std::string QuotedSug = "'" + Suggestion + "'";
+
       if (isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin())) {
         if (SS.isEmpty())
-          Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName()
-            << FixItHint::CreateReplacement(R.getNameLoc(),
-                                            R.getLookupName().getAsString());
+          Diag(R.getNameLoc(), diagnostic_suggest) << Name << QuotedSug
+            << FixItHint::CreateReplacement(R.getNameLoc(), Suggestion);
         else
-          Diag(R.getNameLoc(), diag::err_no_member_suggest)
-            << Name << computeDeclContext(SS, false) << R.getLookupName()
-            << SS.getRange()
-            << FixItHint::CreateReplacement(R.getNameLoc(),
-                                            R.getLookupName().getAsString());
-        if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
-          Diag(ND->getLocation(), diag::note_previous_decl)
-            << ND->getDeclName();
+          Diag(R.getNameLoc(), diag::err_no_member_suggest) << Name
+            << computeDeclContext(SS, false) << QuotedSug << SS.getRange()
+            << FixItHint::CreateReplacement(R.getNameLoc(), Suggestion);
+        if (ND)
+          Diag(ND->getLocation(), diag::note_previous_decl) << QuotedSug;
 
         // Tell the callee to try to recover.
         return false;
@@ -1409,11 +1412,10 @@
         // correction, but don't make it a fix-it since we're not going
         // to recover well anyway.
         if (SS.isEmpty())
-          Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName();
+          Diag(R.getNameLoc(), diagnostic_suggest) << Name << QuotedSug;
         else
-          Diag(R.getNameLoc(), diag::err_no_member_suggest)
-            << Name << computeDeclContext(SS, false) << R.getLookupName()
-            << SS.getRange();
+          Diag(R.getNameLoc(), diag::err_no_member_suggest) << Name
+            << computeDeclContext(SS, false) << QuotedSug << SS.getRange();
 
         // Don't try to recover; it won't work.
         return true;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to