diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ed5724b88fb3..f5bb3511f225 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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(ND)) return false; + if (RequireMember && !isa(ND) && !isa(ND) && + !isa(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. diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 5970120d01e0..e9cd537cc846 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -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(ND) || isa(ND)); + // Don't accept candidates that cannot be member functions, constants, + // variables, or templates. + if (!ND || !(isa(ND) || isa(ND))) + return false; + + // Accept candidates that occur in the current record. + if (Record->containsDecl(ND)) + return true; + + if (const CXXRecordDecl *RD = dyn_cast(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( + 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); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index bb1150fc72a4..cca0f22f14a1 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -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) { diff --git a/clang/test/CXX/class.access/class.friend/p1.cpp b/clang/test/CXX/class.access/class.friend/p1.cpp index 1a519dcc3e54..4a681629eeae 100644 --- a/clang/test/CXX/class.access/class.friend/p1.cpp +++ b/clang/test/CXX/class.access/class.friend/p1.cpp @@ -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'}} } } diff --git a/clang/test/CXX/class.access/p6.cpp b/clang/test/CXX/class.access/p6.cpp index fbdc87b24e26..6a93658fc78d 100644 --- a/clang/test/CXX/class.access/p6.cpp +++ b/clang/test/CXX/class.access/p6.cpp @@ -92,7 +92,7 @@ namespace test3 { template class Outer::A { public: - static void foo(); + static void foo(); // expected-note {{'Outer::A::foo' declared here}} }; class B { @@ -102,7 +102,7 @@ namespace test3 { void test() { Outer::A::foo(); - Outer::A::foo(); // expected-error {{no member named 'foo'}} + Outer::A::foo(); // expected-error {{no member named 'foo' in 'test3::Outer::A'; did you mean 'Outer::A::foo'?}} } } diff --git a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp index f8cc00947480..2884be146c7c 100644 --- a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp @@ -12,7 +12,7 @@ A a; A::E a0 = A().v; int n = A::E::e1; // expected-error {{implicit instantiation of undefined member}} -template enum A::E : T { e1, e2 }; +template enum A::E : T { e1, e2 }; // expected-note 2 {{declared here}} // FIXME: Now that A::E is defined, we are supposed to inject its enumerators // into the already-instantiated class A. This seems like a really bad idea, @@ -20,7 +20,7 @@ template enum A::E : T { e1, e2 }; // // Either do as the standard says, or only include enumerators lexically defined // within the class in its scope. -A::E a1 = A::e1; // expected-error {{no member named 'e1' in 'A'}} +A::E a1 = A::e1; // expected-error {{no member named 'e1' in 'A'; did you mean simply 'e1'?}} A::E a2 = A::e2; @@ -94,7 +94,7 @@ D::E d1 = D::E::e1; // expected-error {{incomplete type 'D::E'}} template<> enum class D::E { e2 }; D::E d2 = D::E::e2; D::E d3 = D::E::e1; // expected-note {{first required here}} -D::E d4 = D::E::e2; // expected-error {{no member named 'e2'}} +D::E d4 = D::E::e2; // expected-error {{no member named 'e2' in 'D::E'; did you mean simply 'e2'?}} template<> enum class D::E { e3 }; // expected-error {{explicit specialization of 'E' after instantiation}} template<> enum class D::E; diff --git a/clang/test/Parser/cxx-using-directive.cpp b/clang/test/Parser/cxx-using-directive.cpp index 76dc22f15301..5efd991c8e7a 100644 --- a/clang/test/Parser/cxx-using-directive.cpp +++ b/clang/test/Parser/cxx-using-directive.cpp @@ -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 ; diff --git a/clang/test/Parser/switch-recovery.cpp b/clang/test/Parser/switch-recovery.cpp index 84ac0c899e55..63b580202af8 100644 --- a/clang/test/Parser/switch-recovery.cpp +++ b/clang/test/Parser/switch-recovery.cpp @@ -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 2 {{expression result unused}} 9:: :y; // expected-error {{expected ';' after expression}} \ expected-error {{expected unqualified-id}} \ expected-warning {{expression result unused}} diff --git a/clang/test/SemaCXX/missing-members.cpp b/clang/test/SemaCXX/missing-members.cpp index 529ba1023dcd..619bc61f2501 100644 --- a/clang/test/SemaCXX/missing-members.cpp +++ b/clang/test/SemaCXX/missing-members.cpp @@ -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'}} diff --git a/clang/test/SemaCXX/typo-correction-pt2.cpp b/clang/test/SemaCXX/typo-correction-pt2.cpp index d22a8e92e0dd..2da52b31f573 100644 --- a/clang/test/SemaCXX/typo-correction-pt2.cpp +++ b/clang/test/SemaCXX/typo-correction-pt2.cpp @@ -168,3 +168,16 @@ namespace PR17019 { evil 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'?}} +} +} diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp index d779e2a4480a..4047e6a18cee 100644 --- a/clang/test/SemaCXX/typo-correction.cpp +++ b/clang/test/SemaCXX/typo-correction.cpp @@ -217,10 +217,14 @@ namespace PR13051 { operator bool() const; }; - void f() { - f(&S::tempalte f); // expected-error{{did you mean 'template'?}} - f(&S::opeartor bool); // expected-error{{did you mean 'operator'?}} - f(&S::foo); // expected-error-re{{no member named 'foo' in 'PR13051::S'$}} + void foo(); // expected-note{{'foo' declared here}} + void g(void(*)()); + void g(bool(S::*)() const); + + void test() { + g(&S::tempalte f); // expected-error{{did you mean 'template'?}} + g(&S::opeartor bool); // expected-error{{did you mean 'operator'?}} + g(&S::foo); // expected-error{{no member named 'foo' in 'PR13051::S'; did you mean simply 'foo'?}} } } diff --git a/clang/test/SemaTemplate/temp_arg_nontype.cpp b/clang/test/SemaTemplate/temp_arg_nontype.cpp index 24509524b294..4a75b11c42b5 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype.cpp @@ -252,16 +252,16 @@ namespace PR8372 { namespace PR9227 { template struct enable_if_bool { }; - template <> struct enable_if_bool { typedef int type; }; - void test_bool() { enable_if_bool::type i; } // expected-error{{enable_if_bool}} + template <> struct enable_if_bool { typedef int type; }; // expected-note{{'enable_if_bool::type' declared here}} + void test_bool() { enable_if_bool::type i; } // expected-error{{enable_if_bool'; did you mean 'enable_if_bool::type'?}} template 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 {