Allow CorrectTypo to replace CXXScopeSpecifiers that refer to classes.
authorKaelyn Uhrain <rikka@google.com>
Sat, 19 Oct 2013 00:05:00 +0000 (00:05 +0000)
committerKaelyn Uhrain <rikka@google.com>
Sat, 19 Oct 2013 00:05:00 +0000 (00:05 +0000)
Now that CorrectTypo knows how to correctly search classes for typo
correction candidates, there is no good reason to only replace an
existing CXXScopeSpecifier if it refers to a namespace. While the actual
enablement was a matter of changing a single comparison, the fallout
from enabling the functionality required a lot more code changes
(including my two previous commits).

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

12 files changed:
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExprMember.cpp
lib/Sema/SemaLookup.cpp
test/CXX/class.access/class.friend/p1.cpp
test/CXX/class.access/p6.cpp
test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp
test/Parser/cxx-using-directive.cpp
test/Parser/switch-recovery.cpp
test/SemaCXX/missing-members.cpp
test/SemaCXX/typo-correction-pt2.cpp
test/SemaCXX/typo-correction.cpp
test/SemaTemplate/temp_arg_nontype.cpp

index ed5724b88fb36af4e86467c230c65c666a8f4c74..f5bb3511f22561908d72445ac08b98a9e8ed3230 100644 (file)
@@ -7301,9 +7301,10 @@ void Sema::HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow) {
 namespace {
 class UsingValidatorCCC : public CorrectionCandidateCallback {
 public:
-  UsingValidatorCCC(bool HasTypenameKeyword, bool IsInstantiation)
+  UsingValidatorCCC(bool HasTypenameKeyword, bool IsInstantiation,
+                    bool RequireMember)
       : HasTypenameKeyword(HasTypenameKeyword),
-        IsInstantiation(IsInstantiation) {}
+        IsInstantiation(IsInstantiation), RequireMember(RequireMember) {}
 
   bool ValidateCandidate(const TypoCorrection &Candidate) LLVM_OVERRIDE {
     NamedDecl *ND = Candidate.getCorrectionDecl();
@@ -7312,6 +7313,10 @@ public:
     if (!ND || isa<NamespaceDecl>(ND))
       return false;
 
+    if (RequireMember && !isa<FieldDecl>(ND) && !isa<CXXMethodDecl>(ND) &&
+        !isa<TypeDecl>(ND))
+      return false;
+
     // Completely unqualified names are invalid for a 'using' declaration.
     if (Candidate.WillReplaceSpecifier() && !Candidate.getCorrectionSpecifier())
       return false;
@@ -7325,6 +7330,7 @@ public:
 private:
   bool HasTypenameKeyword;
   bool IsInstantiation;
+  bool RequireMember;
 };
 } // end anonymous namespace
 
@@ -7440,7 +7446,8 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
 
   // Try to correct typos if possible.
   if (R.empty()) {
-    UsingValidatorCCC CCC(HasTypenameKeyword, IsInstantiation);
+    UsingValidatorCCC CCC(HasTypenameKeyword, IsInstantiation,
+                          CurContext->isRecord());
     if (TypoCorrection Corrected = CorrectTypo(R.getLookupNameInfo(),
                                                R.getLookupKind(), S, &SS, CCC)){
       // We reject any correction for which ND would be NULL.
index 5970120d01e03d6573be26069d647ff82a86d560..e9cd537cc8469dfa14f86ebf3d4f920130f4d4e4 100644 (file)
@@ -538,13 +538,42 @@ bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr,
 namespace {
 
 // Callback to only accept typo corrections that are either a ValueDecl or a
-// FunctionTemplateDecl.
+// FunctionTemplateDecl and are declared in the current record or, for a C++
+// classes, one of its base classes.
 class RecordMemberExprValidatorCCC : public CorrectionCandidateCallback {
  public:
+  explicit RecordMemberExprValidatorCCC(const RecordType *RTy)
+      : Record(RTy->getDecl()) {}
+
   virtual bool ValidateCandidate(const TypoCorrection &candidate) {
     NamedDecl *ND = candidate.getCorrectionDecl();
-    return ND && (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND));
+    // Don't accept candidates that cannot be member functions, constants,
+    // variables, or templates.
+    if (!ND || !(isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)))
+      return false;
+
+    // Accept candidates that occur in the current record.
+    if (Record->containsDecl(ND))
+      return true;
+
+    if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Record)) {
+      // Accept candidates that occur in any of the current class' base classes.
+      for (CXXRecordDecl::base_class_const_iterator BS = RD->bases_begin(),
+                                                    BSEnd = RD->bases_end();
+           BS != BSEnd; ++BS) {
+        if (const RecordType *BSTy = dyn_cast_or_null<RecordType>(
+                BS->getType().getTypePtrOrNull())) {
+          if (BSTy->getDecl()->containsDecl(ND))
+            return true;
+        }
+      }
+    }
+
+    return false;
   }
+
+ private:
+  const RecordDecl *const Record;
 };
 
 }
@@ -600,7 +629,7 @@ LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
   // We didn't find anything with the given name, so try to correct
   // for typos.
   DeclarationName Name = R.getLookupName();
-  RecordMemberExprValidatorCCC Validator;
+  RecordMemberExprValidatorCCC Validator(RTy);
   TypoCorrection Corrected = SemaRef.CorrectTypo(R.getLookupNameInfo(),
                                                  R.getLookupKind(), NULL,
                                                  &SS, Validator, DC);
index bb1150fc72a4659b1a440f80ecde1cbac24435a9..cca0f22f14a1efc8c7c8ee099a266b886f48e611 100644 (file)
@@ -4157,7 +4157,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
   // corrections.
   bool SearchNamespaces
     = getLangOpts().CPlusPlus &&
-      (IsUnqualifiedLookup || (QualifiedDC && QualifiedDC->isNamespace()));
+      (IsUnqualifiedLookup || (SS && SS->isSet()));
   // In a few cases we *only* want to search for corrections based on just
   // adding or changing the nested name specifier.
   unsigned TypoLen = Typo->getName().size();
@@ -4400,6 +4400,18 @@ retry_lookup:
           switch (TmpRes.getResultKind()) {
           case LookupResult::Found:
           case LookupResult::FoundOverloaded: {
+            if (SS && SS->isValid()) {
+              std::string NewQualified = TC.getAsString(getLangOpts());
+              std::string OldQualified;
+              llvm::raw_string_ostream OldOStream(OldQualified);
+              SS->getScopeRep()->print(OldOStream, getPrintingPolicy());
+              OldOStream << TypoName;
+              // If correction candidate would be an identical written qualified
+              // identifer, then the existing CXXScopeSpec probably included a
+              // typedef that didn't get accounted for properly.
+              if (OldOStream.str() == NewQualified)
+                break;
+            }
             for (LookupResult::iterator TRD = TmpRes.begin(),
                                      TRDEnd = TmpRes.end();
                  TRD != TRDEnd; ++TRD) {
index 1a519dcc3e54f47471453300924d76928f0ccd07..4a681629eeae46d5af89f3f3248290d8539e9a49 100644 (file)
@@ -7,8 +7,8 @@
 //   special access rights to the friends, but they do not make the nominated
 //   friends members of the befriending class.
 
-struct S { static void f(); };
-S* g() { return 0; }
+struct S { static void f(); }; // expected-note 2 {{'S' declared here}}
+S* g() { return 0; } // expected-note 2 {{'g' declared here}}
 
 struct X {
   friend struct S;
@@ -19,8 +19,8 @@ void test1() {
   S s;
   g()->f();
   S::f();
-  X::g(); // expected-error{{no member named 'g' in 'X'}}
-  X::S x_s; // expected-error{{no type named 'S' in 'X'}}
+  X::g(); // expected-error{{no member named 'g' in 'X'; did you mean simply 'g'?}}
+  X::S x_s; // expected-error{{no type named 'S' in 'X'; did you mean simply 'S'?}}
   X x;
   x.g(); // expected-error{{no member named 'g' in 'X'}}
 }
@@ -36,24 +36,24 @@ namespace N {
     friend struct S2* g2();
   };
 
-  struct S2 { static void f2(); };
-  S2* g2() { return 0; }
+  struct S2 { static void f2(); }; // expected-note 2 {{'S2' declared here}}
+  S2* g2() { return 0; } // expected-note 2 {{'g2' declared here}}
 
   void test() {
     g()->f();
     S s;
     S::f();
-    X::g(); // expected-error{{no member named 'g' in 'N::X'}}
-    X::S x_s; // expected-error{{no type named 'S' in 'N::X'}}
+    X::g(); // expected-error{{no member named 'g' in 'N::X'; did you mean simply 'g'?}}
+    X::S x_s; // expected-error{{no type named 'S' in 'N::X'; did you mean simply 'S'?}}
     X x;
     x.g(); // expected-error{{no member named 'g' in 'N::X'}}
 
     g2();
     S2 s2;
-    ::g2(); // expected-error{{no member named 'g2' in the global namespace}}
-    ::S2 g_s2; // expected-error{{no type named 'S2' in the global namespace}}
-    X::g2(); // expected-error{{no member named 'g2' in 'N::X'}}
-    X::S2 x_s2; // expected-error{{no type named 'S2' in 'N::X'}}
+    ::g2(); // expected-error{{no member named 'g2' in the global namespace; did you mean simply 'g2'?}}
+    ::S2 g_s2; // expected-error{{no type named 'S2' in the global namespace; did you mean simply 'S2'?}}
+    X::g2(); // expected-error{{no member named 'g2' in 'N::X'; did you mean simply 'g2'?}}
+    X::S2 x_s2; // expected-error{{no type named 'S2' in 'N::X'; did you mean simply 'S2'?}}
     x.g2(); // expected-error{{no member named 'g2' in 'N::X'}}
   }
 }
index fbdc87b24e26ab82971a7a3cfa0f8b1d3ccf240d..6a93658fc78d0cd6099416fe6efe1d0011c2132d 100644 (file)
@@ -92,7 +92,7 @@ namespace test3 {
 
   template <class T> class Outer::A<T, typename T::nature> {
   public:
-    static void foo();
+    static void foo(); // expected-note {{'Outer::A<B, Green>::foo' declared here}}
   };
 
   class B {
@@ -102,7 +102,7 @@ namespace test3 {
 
   void test() {
     Outer::A<B, Green>::foo();
-    Outer::A<B, Blue>::foo(); // expected-error {{no member named 'foo'}}
+    Outer::A<B, Blue>::foo(); // expected-error {{no member named 'foo' in 'test3::Outer::A<test3::B, test3::Blue>'; did you mean 'Outer::A<B, Green>::foo'?}}
   }
 }
 
index f8cc00947480b59af9101030ae054f220a4fbdc5..2884be146c7c346ffeece73c6b697de7f131b817 100644 (file)
@@ -12,7 +12,7 @@ A<int> a;
 A<int>::E a0 = A<int>().v;
 int n = A<int>::E::e1; // expected-error {{implicit instantiation of undefined member}}
 
-template<typename T> enum A<T>::E : T { e1, e2 };
+template<typename T> enum A<T>::E : T { e1, e2 }; // expected-note 2 {{declared here}}
 
 // FIXME: Now that A<T>::E is defined, we are supposed to inject its enumerators
 // into the already-instantiated class A<T>. This seems like a really bad idea,
@@ -20,7 +20,7 @@ template<typename T> enum A<T>::E : T { e1, e2 };
 //
 // Either do as the standard says, or only include enumerators lexically defined
 // within the class in its scope.
-A<int>::E a1 = A<int>::e1; // expected-error {{no member named 'e1' in 'A<int>'}}
+A<int>::E a1 = A<int>::e1; // expected-error {{no member named 'e1' in 'A<int>'; did you mean simply 'e1'?}}
 
 A<char>::E a2 = A<char>::e2;
 
@@ -94,7 +94,7 @@ D<int>::E d1 = D<int>::E::e1; // expected-error {{incomplete type 'D<int>::E'}}
 template<> enum class D<int>::E { e2 };
 D<int>::E d2 = D<int>::E::e2;
 D<char>::E d3 = D<char>::E::e1; // expected-note {{first required here}}
-D<char>::E d4 = D<char>::E::e2; // expected-error {{no member named 'e2'}}
+D<char>::E d4 = D<char>::E::e2; // expected-error {{no member named 'e2' in 'D<char>::E'; did you mean simply 'e2'?}}
 template<> enum class D<char>::E { e3 }; // expected-error {{explicit specialization of 'E' after instantiation}}
 
 template<> enum class D<short>::E;
index 76dc22f15301a6ed4e2e505fc1a65e0f3d4008a1..5efd991c8e7acd61cdcdfb5cafb45b1b9bd960a5 100644 (file)
@@ -4,7 +4,7 @@ class A {};
 
 namespace B {
   namespace A {} // expected-note{{namespace '::B::A' defined here}} \
-                 // expected-note{{namespace 'B::A' defined here}}
+                 // expected-note 2{{namespace 'B::A' defined here}}
   using namespace A ;
 }
 
@@ -28,7 +28,7 @@ namespace D {
 
 using namespace ! ; // expected-error{{expected namespace name}}
 using namespace A ; // expected-error{{no namespace named 'A'; did you mean 'B::A'?}}
-using namespace ::A // expected-error{{expected namespace name}} \
+using namespace ::A // expected-error{{no namespace named 'A' in the global namespace; did you mean 'B::A'?}} \
                     // expected-error{{expected ';' after namespace name}}
                     B ; 
 
index 84ac0c899e55ae6cb6e0d89e8e08b29ac8025b7c..63b580202af8144fdfc82f3ac9a39ac5b443ab5f 100644 (file)
@@ -95,7 +95,7 @@ int test8( foo x ) {
 }
 
 // Stress test to make sure Clang doesn't crash.
-void test9(int x) {
+void test9(int x) { // expected-note {{'x' declared here}}
   switch(x) {
     case 1: return;
     2: case; // expected-error {{expected 'case' keyword before expression}} \
@@ -104,8 +104,8 @@ void test9(int x) {
     7: :x; // expected-error {{expected 'case' keyword before expression}} \
               expected-error {{expected expression}}
     8:: x; // expected-error {{expected ';' after expression}} \
-              expected-error {{no member named 'x' in the global namespace}} \
-              expected-warning {{expression result unused}}
+              expected-error {{no member named 'x' in the global namespace; did you mean simply 'x'?}} \
+              expected-warning {{expression result unused}}
     9:: :y; // expected-error {{expected ';' after expression}} \
                expected-error {{expected unqualified-id}} \
                expected-warning {{expression result unused}}
index 529ba1023dcde494cb98b0edaeaa78421151c50f..619bc61f250111094738118b26fae3615ab39a4e 100644 (file)
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 namespace A {
   namespace B {
-    class C { };
+    class C { }; // expected-note 2 {{'A::B::C' declared here}}
     struct S { };
     union U { };
   }
@@ -19,8 +19,12 @@ namespace B {
 
 void g() {
   A::B::D::E; // expected-error {{no member named 'D' in namespace 'A::B'}}
-  B::B::C::D; // expected-error {{no member named 'C' in 'B::B'}}
-  ::C::D; // expected-error {{no member named 'C' in the global namespace}}
+  // FIXME: The typo corrections below should be suppressed since A::B::C
+  // doesn't have a member named D.
+  B::B::C::D; // expected-error {{no member named 'C' in 'B::B'; did you mean 'A::B::C'?}} \
+              // expected-error {{no member named 'D' in 'A::B::C'}}
+  ::C::D; // expected-error {{no member named 'C' in the global namespace; did you mean 'A::B::C'?}}\
+          // expected-error {{no member named 'D' in 'A::B::C'}}
 }
 
 int A::B::i = 10; // expected-error {{no member named 'i' in namespace 'A::B'}}
index d22a8e92e0ddd956cec93dc739dab1998704484d..2da52b31f57327745f8d2ce37f9b827c637c1a4f 100644 (file)
@@ -168,3 +168,16 @@ namespace PR17019 {
     evil<int> Q(0); // expected-note {{in instantiation of member function}}
   }
 }
+
+namespace fix_class_name_qualifier {
+class MessageHeaders {};
+class MessageUtils {
+ public:
+  static void ParseMessageHeaders(int, int); // expected-note {{'MessageUtils::ParseMessageHeaders' declared here}}
+};
+
+void test() {
+  // No, we didn't mean to call MessageHeaders::MessageHeaders.
+  MessageHeaders::ParseMessageHeaders(5, 4); // expected-error {{no member named 'ParseMessageHeaders' in 'fix_class_name_qualifier::MessageHeaders'; did you mean 'MessageUtils::ParseMessageHeaders'?}}
+}
+}
index d779e2a4480a6a5be533e77e7bf1e5a16f14eb86..4047e6a18cee26d168cc43c2ebf08d09307d444d 100644 (file)
@@ -217,10 +217,14 @@ namespace PR13051 {
     operator bool() const;
   };
 
-  void f() {
-    f(&S<int>::tempalte f<int>); // expected-error{{did you mean 'template'?}}
-    f(&S<int>::opeartor bool); // expected-error{{did you mean 'operator'?}}
-    f(&S<int>::foo); // expected-error-re{{no member named 'foo' in 'PR13051::S<int>'$}}
+  void foo(); // expected-note{{'foo' declared here}}
+  void g(void(*)());
+  void g(bool(S<int>::*)() const);
+
+  void test() {
+    g(&S<int>::tempalte f<int>); // expected-error{{did you mean 'template'?}}
+    g(&S<int>::opeartor bool); // expected-error{{did you mean 'operator'?}}
+    g(&S<int>::foo); // expected-error{{no member named 'foo' in 'PR13051::S<int>'; did you mean simply 'foo'?}}
   }
 }
 
index 24509524b294c0b1de6967f16492839882d892f4..4a75b11c42b57c577e567da6aa3a3580dec55c84 100644 (file)
@@ -252,16 +252,16 @@ namespace PR8372 {
 
 namespace PR9227 {
   template <bool B> struct enable_if_bool { };
-  template <> struct enable_if_bool<true> { typedef int type; };
-  void test_bool() { enable_if_bool<false>::type i; } // expected-error{{enable_if_bool<false>}}
+  template <> struct enable_if_bool<true> { typedef int type; }; // expected-note{{'enable_if_bool<true>::type' declared here}}
+  void test_bool() { enable_if_bool<false>::type i; } // expected-error{{enable_if_bool<false>'; did you mean 'enable_if_bool<true>::type'?}}
 
   template <char C> struct enable_if_char { };
-  template <> struct enable_if_char<'a'> { typedef int type; };
-  void test_char_0() { enable_if_char<0>::type i; } // expected-error{{enable_if_char<'\x00'>}}
-  void test_char_b() { enable_if_char<'b'>::type i; } // expected-error{{enable_if_char<'b'>}}
-  void test_char_possibly_negative() { enable_if_char<'\x02'>::type i; } // expected-error{{enable_if_char<'\x02'>}}
-  void test_char_single_quote() { enable_if_char<'\''>::type i; } // expected-error{{enable_if_char<'\''>}}
-  void test_char_backslash() { enable_if_char<'\\'>::type i; } // expected-error{{enable_if_char<'\\'>}}
+  template <> struct enable_if_char<'a'> { typedef int type; }; // expected-note 5{{'enable_if_char<'a'>::type' declared here}}
+  void test_char_0() { enable_if_char<0>::type i; } // expected-error{{enable_if_char<'\x00'>'; did you mean 'enable_if_char<'a'>::type'?}}
+  void test_char_b() { enable_if_char<'b'>::type i; } // expected-error{{enable_if_char<'b'>'; did you mean 'enable_if_char<'a'>::type'?}}
+  void test_char_possibly_negative() { enable_if_char<'\x02'>::type i; } // expected-error{{enable_if_char<'\x02'>'; did you mean 'enable_if_char<'a'>::type'?}}
+  void test_char_single_quote() { enable_if_char<'\''>::type i; } // expected-error{{enable_if_char<'\''>'; did you mean 'enable_if_char<'a'>::type'?}}
+  void test_char_backslash() { enable_if_char<'\\'>::type i; } // expected-error{{enable_if_char<'\\'>'; did you mean 'enable_if_char<'a'>::type'?}}
 }
 
 namespace PR10579 {