]> granicus.if.org Git - clang/commitdiff
Teach typo correction to look inside of classes like it does namespaces.
authorKaelyn Uhrain <rikka@google.com>
Thu, 26 Sep 2013 19:10:29 +0000 (19:10 +0000)
committerKaelyn Uhrain <rikka@google.com>
Thu, 26 Sep 2013 19:10:29 +0000 (19:10 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191449 91177308-0d34-0410-b5e6-96231b3b80d8

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

index 0deb74201c49f593ec1d7960c37290ff2dcc4874..f0b772649847aed4c1c8d82108dcb7dae6ed2c34 100644 (file)
@@ -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();
index 4ecbf0ba7768305920896f3be623adbfe843fcc5..6dbfad4e18db51faf2c9be3fef0850e229ea90cb 100644 (file)
@@ -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?");
index a645986ac3142fb504e114d291fd3fb4b3257881..75b0f984f8c49f310d31c1d9d60a8fdd388ba73d 100644 (file)
@@ -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(NamespaceDecl *ND) {
   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 DeclarationNameInfo &TypoName,
       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:
index df9a2cd14cd4187170fc2c259c6fbd74a0c35d35..e61df3e586a7d2a29aa6ea6fb927d0c525bec186 100644 (file)
@@ -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 {
index 4a04a448595c5628dd5b99758a493f2653510f10..9295654412302d7118d10a3e476dcca1a9904ff1 100644 (file)
@@ -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;
index 385e45dadfae42f502d4a76f274e9abf3f590219..27ebb8e036f0727090ad4846bf5efbb232bde039 100644 (file)
@@ -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'?}}
 }
index 3644dea3b23c84e29f80fe98aba4f66c4994a31a..e676b1074f9f93f5c7629cf215626c5a5ec62b40 100644 (file)
@@ -23,21 +23,21 @@ using N::FFG; // expected-error {{no member named 'FFG' in namespace 'using_sugg
 }
 
 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 {
index 40bcf45bca32f954bc1383416bc843686e153672..6195e0395515463b94c234ba727efc19bd0e3a4b 100644 (file)
@@ -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' declared here}}
   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'}}
 }
 
index 06f69d7186bdfb0cf5964d081f3217885faf9350..f475b41a9f25cb312d237734277ec27ef7684470 100644 (file)
@@ -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;
+  }
+}
+}