r191449 - Teach typo correction to look inside of classes like it does namespaces.

Kaelyn Uhrain rikka at google.com
Thu Sep 26 12:10:30 PDT 2013


Author: rikka
Date: Thu Sep 26 14:10:29 2013
New Revision: 191449

URL: http://llvm.org/viewvc/llvm-project?rev=191449&view=rev
Log:
Teach typo correction to look inside of classes like it does namespaces.

Unlike with namespaces, searching inside of classes requires also
checking the access to correction candidates (i.e. don't suggest a
correction to a private class member for a correction occurring outside
that class and its methods or friends).

Included is a small (one line) fix for a bug, that was uncovered while
cleaning up the unit tests, where the decls from a TypoCorrection candidate
were preserved in new TypoCorrection candidates that are derived (copied)
from the old TypoCorrection--notably when creating a new candidate by
changing the NestedNameSpecifier associated with the base idenitifer.

Modified:
    cfe/trunk/include/clang/Sema/TypoCorrection.h
    cfe/trunk/lib/Sema/SemaAccess.cpp
    cfe/trunk/lib/Sema/SemaLookup.cpp
    cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
    cfe/trunk/test/CXX/class/class.nested.type/p1.cpp
    cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp
    cfe/trunk/test/FixIt/typo-using.cpp
    cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
    cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp

Modified: cfe/trunk/include/clang/Sema/TypoCorrection.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/TypoCorrection.h?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/TypoCorrection.h (original)
+++ cfe/trunk/include/clang/Sema/TypoCorrection.h Thu Sep 26 14:10:29 2013
@@ -137,6 +137,11 @@ public:
     return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
   }
 
+  /// \brief Clears the list of NamedDecls.
+  void ClearCorrectionDecls() {
+    CorrectionDecls.clear();
+  }
+
   /// \brief Clears the list of NamedDecls before adding the new one.
   void setCorrectionDecl(NamedDecl *CDecl) {
     CorrectionDecls.clear();

Modified: cfe/trunk/lib/Sema/SemaAccess.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaAccess.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaAccess.cpp (original)
+++ cfe/trunk/lib/Sema/SemaAccess.cpp Thu Sep 26 14:10:29 2013
@@ -1390,6 +1390,8 @@ static AccessResult IsAccessible(Sema &S
   CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths);
   if (!Path)
     return AR_dependent;
+  if (Path->Access == AS_none)  // This can happen during typo correction.
+    return AR_inaccessible;
 
   assert(Path->Access <= UnprivilegedAccess &&
          "access along best path worse than direct?");

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Thu Sep 26 14:10:29 2013
@@ -3601,6 +3601,10 @@ class NamespaceSpecifierSet {
   /// NestedNameSpecifier and its distance in the process.
   void AddNamespace(NamespaceDecl *ND);
 
+  /// \brief Add the record to the set, computing the corresponding
+  /// NestedNameSpecifier and its distance in the process.
+  void AddRecord(RecordDecl *RD);
+
   typedef SpecifierInfoList::iterator iterator;
   iterator begin() {
     if (!isSorted) SortNamespaces();
@@ -3702,6 +3706,72 @@ void NamespaceSpecifierSet::AddNamespace
   DistanceMap[NumSpecifiers].push_back(SpecifierInfo(Ctx, NNS, NumSpecifiers));
 }
 
+void NamespaceSpecifierSet::AddRecord(RecordDecl *RD) {
+  if (!RD->isBeingDefined() && !RD->isCompleteDefinition())
+    return;
+
+  DeclContext *Ctx = cast<DeclContext>(RD);
+  NestedNameSpecifier *NNS = NULL;
+  unsigned NumSpecifiers = 0;
+  DeclContextList NamespaceDeclChain(BuildContextChain(Ctx));
+  DeclContextList FullNamespaceDeclChain(NamespaceDeclChain);
+
+  // 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();
+  }
+
+  // Add an explicit leading '::' specifier if needed.
+  if (NamespaceDeclChain.empty()) {
+    NamespaceDeclChain = FullNamespaceDeclChain;
+    NNS = NestedNameSpecifier::GlobalSpecifier(Context);
+  } else if (NamespaceDecl *ND =
+                 dyn_cast_or_null<NamespaceDecl>(NamespaceDeclChain.back())) {
+    IdentifierInfo *Name = ND->getIdentifier();
+    if (std::find(CurContextIdentifiers.begin(), CurContextIdentifiers.end(),
+                  Name) != CurContextIdentifiers.end() ||
+        std::find(CurNameSpecifierIdentifiers.begin(),
+                  CurNameSpecifierIdentifiers.end(),
+                  Name) != CurNameSpecifierIdentifiers.end()) {
+      NamespaceDeclChain = FullNamespaceDeclChain;
+      NNS = NestedNameSpecifier::GlobalSpecifier(Context);
+    }
+  }
+
+  // Build the NestedNameSpecifier from what is left of the NamespaceDeclChain
+  for (DeclContextList::reverse_iterator C = NamespaceDeclChain.rbegin(),
+                                      CEnd = NamespaceDeclChain.rend();
+       C != CEnd; ++C) {
+    if (NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(*C)) {
+      NNS = NestedNameSpecifier::Create(Context, NNS, ND);
+      ++NumSpecifiers;
+    } else if (RecordDecl *RD = dyn_cast_or_null<RecordDecl>(*C)) {
+      NNS = NestedNameSpecifier::Create(Context, NNS, RD->isTemplateDecl(),
+                                        RD->getTypeForDecl());
+      ++NumSpecifiers;
+    }
+  }
+
+  // If the built NestedNameSpecifier would be replacing an existing
+  // NestedNameSpecifier, use the number of component identifiers that
+  // would need to be changed as the edit distance instead of the number
+  // of components in the built NestedNameSpecifier.
+  if (NNS && !CurNameSpecifierIdentifiers.empty()) {
+    SmallVector<const IdentifierInfo*, 4> NewNameSpecifierIdentifiers;
+    getNestedNameSpecifierIdentifiers(NNS, NewNameSpecifierIdentifiers);
+    NumSpecifiers = llvm::ComputeEditDistance(
+        ArrayRef<const IdentifierInfo *>(CurNameSpecifierIdentifiers),
+        ArrayRef<const IdentifierInfo *>(NewNameSpecifierIdentifiers));
+  }
+
+  isSorted = false;
+  Distances.insert(NumSpecifiers);
+  DistanceMap[NumSpecifiers].push_back(SpecifierInfo(Ctx, NNS, NumSpecifiers));
+}
+
 /// \brief Perform name lookup for a possible result for typo correction.
 static void LookupPotentialTypoResult(Sema &SemaRef,
                                       LookupResult &Res,
@@ -4153,12 +4223,22 @@ TypoCorrection Sema::CorrectTypo(const D
       for (unsigned I = 0, N = ExternalKnownNamespaces.size(); I != N; ++I)
         KnownNamespaces[ExternalKnownNamespaces[I]] = true;
     }
-    
-    for (llvm::MapVector<NamespaceDecl*, bool>::iterator 
+
+    for (llvm::MapVector<NamespaceDecl*, bool>::iterator
            KNI = KnownNamespaces.begin(),
            KNIEnd = KnownNamespaces.end();
          KNI != KNIEnd; ++KNI)
       Namespaces.AddNamespace(KNI->first);
+
+    for (ASTContext::type_iterator TI = Context.types_begin(),
+                                   TIEnd = Context.types_end();
+         TI != TIEnd; ++TI) {
+      if (CXXRecordDecl *CD = (*TI)->getAsCXXRecordDecl()) {
+        if (!CD->isDependentType() && !CD->isAnonymousStructOrUnion() &&
+            !CD->isUnion())
+          Namespaces.AddRecord(CD);
+      }
+    }
   }
 
   // Weed out any names that could not be found by name lookup or, if a
@@ -4295,6 +4375,17 @@ retry_lookup:
                                           NIEnd = Namespaces.end();
              NI != NIEnd; ++NI) {
           DeclContext *Ctx = NI->DeclCtx;
+          const Type *NSType = NI->NameSpecifier->getAsType();
+
+          // If the current NestedNameSpecifier refers to a class and the
+          // current correction candidate is the name of that class, then skip
+          // it as it is unlikely a qualified version of the class' constructor
+          // is an appropriate correction.
+          if (CXXRecordDecl *NSDecl =
+                  NSType ? NSType->getAsCXXRecordDecl() : 0) {
+            if (NSDecl->getIdentifier() == QRI->getCorrectionAsIdentifierInfo())
+              continue;
+          }
 
           // FIXME: Stop searching once the namespaces are too far away to create
           // acceptable corrections for this identifier (since the namespaces
@@ -4310,14 +4401,20 @@ retry_lookup:
           case LookupResult::Found:
           case LookupResult::FoundOverloaded: {
             TypoCorrection TC(*QRI);
+            TC.ClearCorrectionDecls();
             TC.setCorrectionSpecifier(NI->NameSpecifier);
             TC.setQualifierDistance(NI->EditDistance);
             TC.setCallbackDistance(0); // Reset the callback distance
             for (LookupResult::iterator TRD = TmpRes.begin(),
                                      TRDEnd = TmpRes.end();
-                 TRD != TRDEnd; ++TRD)
-              TC.addCorrectionDecl(*TRD);
-            Consumer.addCorrection(TC);
+                 TRD != TRDEnd; ++TRD) {
+              if (CheckMemberAccess(TC.getCorrectionRange().getBegin(),
+                                    NSType ? NSType->getAsCXXRecordDecl() : 0,
+                                    *TRD) == AR_accessible)
+                TC.addCorrectionDecl(*TRD);
+            }
+            if (TC.isResolved())
+              Consumer.addCorrection(TC);
             break;
           }
           case LookupResult::NotFound:

Modified: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp (original)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp Thu Sep 26 14:10:29 2013
@@ -5,7 +5,7 @@ namespace N {
   
   X operator+(X, X);
 
-  void f(X);
+  void f(X); // expected-note 2 {{'N::f' declared here}}
   void g(X); // expected-note{{candidate function}}
 
   void test_multiadd(X x) {
@@ -17,7 +17,7 @@ namespace M {
   struct Y : N::X { };
 }
 
-void f(); // expected-note 2 {{'f' declared here}}
+void f();
 
 void test_operator_adl(N::X x, M::Y y) {
   (void)(x + x);
@@ -27,8 +27,8 @@ void test_operator_adl(N::X x, M::Y y) {
 void test_func_adl(N::X x, M::Y y) {
   f(x);
   f(y);
-  (f)(x); // expected-error{{too many arguments to function call}}
-  ::f(x); // expected-error{{too many arguments to function call}}
+  (f)(x); // expected-error{{too many arguments to function call, expected 0, have 1; did you mean 'N::f'?}}
+  ::f(x); // expected-error{{too many arguments to function call, expected 0, have 1; did you mean 'N::f'?}}
 }
 
 namespace N {

Modified: cfe/trunk/test/CXX/class/class.nested.type/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.nested.type/p1.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/CXX/class/class.nested.type/p1.cpp (original)
+++ cfe/trunk/test/CXX/class/class.nested.type/p1.cpp Thu Sep 26 14:10:29 2013
@@ -2,12 +2,12 @@
 
 class X {
 public:
-  typedef int I;
-  class Y { };
+  typedef int I; // expected-note{{'X::I' declared here}}
+  class Y { }; // expected-note{{'X::Y' declared here}}
   I a;
 };
 
-I b; // expected-error{{unknown type name 'I'}}
-Y c; // expected-error{{unknown type name 'Y'}}
+I b; // expected-error{{unknown type name 'I'; did you mean 'X::I'?}}
+Y c; // expected-error{{unknown type name 'Y'; did you mean 'X::Y'?}}
 X::Y d;
 X::I e;

Modified: cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp (original)
+++ cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp Thu Sep 26 14:10:29 2013
@@ -1,16 +1,16 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 
 struct A { 
-  virtual void f(int a = 7);
+  virtual void f(int a = 7); // expected-note{{'A::f' declared here}}
 }; 
 
 struct B : public A {
-  void f(int a); // expected-note{{'f' declared here}}
+  void f(int a);
 }; 
 
 void m() {
   B* pb = new B; 
   A* pa = pb; 
   pa->f(); // OK, calls pa->B::f(7) 
-  pb->f(); // expected-error{{too few arguments}}
+  pb->f(); // expected-error{{too few arguments to function call, expected 1, have 0; did you mean 'A::f'?}}
 }

Modified: cfe/trunk/test/FixIt/typo-using.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/typo-using.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/FixIt/typo-using.cpp (original)
+++ cfe/trunk/test/FixIt/typo-using.cpp Thu Sep 26 14:10:29 2013
@@ -23,21 +23,21 @@ using N::FFG; // expected-error {{no mem
 }
 
 namespace using_suggestion_ty_dropped_specifier {
-class AAA {}; // expected-note {{'::using_suggestion_ty_dropped_specifier::AAA' declared here}}
+class ABC {}; // expected-note {{'::using_suggestion_ty_dropped_specifier::ABC' declared here}}
 namespace N { }
-using N::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_ty_dropped_specifier::N'; did you mean '::using_suggestion_ty_dropped_specifier::AAA'?}}
+using N::ABC; // expected-error {{no member named 'ABC' in namespace 'using_suggestion_ty_dropped_specifier::N'; did you mean '::using_suggestion_ty_dropped_specifier::ABC'?}}
 }
 
 namespace using_suggestion_tyname_ty_dropped_specifier {
-class AAA {}; // expected-note {{'::using_suggestion_tyname_ty_dropped_specifier::AAA' declared here}}
+class BCD {}; // expected-note {{'::using_suggestion_tyname_ty_dropped_specifier::BCD' declared here}}
 namespace N { }
-using typename N::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_tyname_ty_dropped_specifier::N'; did you mean '::using_suggestion_tyname_ty_dropped_specifier::AAA'?}}
+using typename N::BCD; // expected-error {{no member named 'BCD' in namespace 'using_suggestion_tyname_ty_dropped_specifier::N'; did you mean '::using_suggestion_tyname_ty_dropped_specifier::BCD'?}}
 }
 
 namespace using_suggestion_val_dropped_specifier {
-void FFF() {} // expected-note {{'::using_suggestion_val_dropped_specifier::FFF' declared here}}
+void EFG() {} // expected-note {{'::using_suggestion_val_dropped_specifier::EFG' declared here}}
 namespace N { }
-using N::FFF; // expected-error {{no member named 'FFF' in namespace 'using_suggestion_val_dropped_specifier::N'; did you mean '::using_suggestion_val_dropped_specifier::FFF'?}}
+using N::EFG; // expected-error {{no member named 'EFG' in namespace 'using_suggestion_val_dropped_specifier::N'; did you mean '::using_suggestion_val_dropped_specifier::EFG'?}}
 }
 
 namespace using_suggestion_member_ty {

Modified: cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp (original)
+++ cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp Thu Sep 26 14:10:29 2013
@@ -19,7 +19,7 @@ namespace fizbin {
                                                           // expected-note{{'fizbin::nested::lessFoobar' declared here}}
   class dummy { // expected-note 2 {{'fizbin::dummy' declared here}}
    public:
-    static bool moreFoobar() { return false; } // expected-note{{'moreFoobar' declared here}}
+    static bool morebar() { return false; } // expected-note{{'morebar' declared here}}
   };
 }
 void Check() { // expected-note{{'Check' declared here}}
@@ -29,9 +29,9 @@ void Check() { // expected-note{{'Check'
   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 'moreFoobar'?}}
+  if (dummy::morebar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}}
+  if (dummy::mrebar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}} \
+                                     // expected-error{{no member named 'mrebar' in 'fizbin::dummy'; did you mean 'morebar'?}}
   if (moFoobin()) Double(7); // expected-error{{use of undeclared identifier 'moFoobin'}}
 }
 

Modified: cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp (original)
+++ cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp Thu Sep 26 14:10:29 2013
@@ -33,3 +33,64 @@ void test(Foo F, int num) {
   F.B(num);  // expected-error {{too many arguments to function call, expected 0, have 1; did you mean '::TemplateFunction::B'?}}
 }
 }
+namespace using_suggestion_val_dropped_specifier {
+void FFF() {} // expected-note {{'::using_suggestion_val_dropped_specifier::FFF' declared here}}
+namespace N { }
+using N::FFF; // expected-error {{no member named 'FFF' in namespace 'using_suggestion_val_dropped_specifier::N'; did you mean '::using_suggestion_val_dropped_specifier::FFF'?}}
+}
+
+namespace class_member_typo_corrections {
+class Outer {
+public:
+  class Inner {};  // expected-note {{'Outer::Inner' declared here}}
+  Inner MyMethod(Inner arg);
+};
+
+Inner Outer::MyMethod(Inner arg) {  // expected-error {{unknown type name 'Inner'; did you mean 'Outer::Inner'?}}
+  // TODO: Recovery needs to be fixed/added for the typo-correction of the
+  // return type so the below error isn't still generated.
+  return Inner();  // expected-error {{no viable conversion from 'class_member_typo_corrections::Outer::Inner' to 'int'}}
+}
+
+class Result {
+public:
+  enum ResultType {
+    ENTITY,  // expected-note {{'Result::ENTITY' declared here}}
+    PREDICATE,  // expected-note {{'Result::PREDICATE' declared here}}
+    LITERAL  // expected-note {{'Result::LITERAL' declared here}}
+  };
+
+  ResultType type();
+};
+
+void test() {
+  Result result_cell;
+  switch (result_cell.type()) {
+  case ENTITY:  // expected-error {{use of undeclared identifier 'ENTITY'; did you mean 'Result::ENTITY'?}}
+  case LITERAL:  // expected-error {{use of undeclared identifier 'LITERAL'; did you mean 'Result::LITERAL'?}}
+  case PREDICAT:  // expected-error {{use of undeclared identifier 'PREDICAT'; did you mean 'Result::PREDICATE'?}}
+    break;
+  }
+}
+
+class Figure {
+  enum ResultType {
+    SQUARE,
+    TRIANGLE,
+    CIRCLE
+  };
+
+public:
+  ResultType type();
+};
+
+void testAccess() {
+  Figure obj;
+  switch (obj.type()) {  // expected-warning {{enumeration values 'SQUARE', 'TRIANGLE', and 'CIRCLE' not handled in switch}}
+  case SQUARE:  // expected-error-re {{use of undeclared identifier 'SQUARE'$}}
+  case TRIANGLE:  // expected-error-re {{use of undeclared identifier 'TRIANGLE'$}}
+  case CIRCE:  // expected-error-re {{use of undeclared identifier 'CIRCE'$}}
+    break;
+  }
+}
+}





More information about the cfe-commits mailing list