diff options
author | Kaelyn Uhrain <rikka@google.com> | 2013-10-19 00:05:00 +0000 |
---|---|---|
committer | Kaelyn Uhrain <rikka@google.com> | 2013-10-19 00:05:00 +0000 |
commit | 8aa8da85ca60f2c7d88c47a67a4252bd032d1eb9 (patch) | |
tree | 481dd4d4b8137bf847d34fcc4a42c9043aa01bce | |
parent | f7b63e3e18601459d95849e6fbc776b718d12515 (diff) | |
download | bcm5719-llvm-8aa8da85ca60f2c7d88c47a67a4252bd032d1eb9.tar.gz bcm5719-llvm-8aa8da85ca60f2c7d88c47a67a4252bd032d1eb9.zip |
Allow CorrectTypo to replace CXXScopeSpecifiers that refer to classes.
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).
llvm-svn: 193020
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 13 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprMember.cpp | 35 | ||||
-rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 14 | ||||
-rw-r--r-- | clang/test/CXX/class.access/class.friend/p1.cpp | 24 | ||||
-rw-r--r-- | clang/test/CXX/class.access/p6.cpp | 4 | ||||
-rw-r--r-- | clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp | 6 | ||||
-rw-r--r-- | clang/test/Parser/cxx-using-directive.cpp | 4 | ||||
-rw-r--r-- | clang/test/Parser/switch-recovery.cpp | 6 | ||||
-rw-r--r-- | clang/test/SemaCXX/missing-members.cpp | 10 | ||||
-rw-r--r-- | clang/test/SemaCXX/typo-correction-pt2.cpp | 13 | ||||
-rw-r--r-- | clang/test/SemaCXX/typo-correction.cpp | 12 | ||||
-rw-r--r-- | clang/test/SemaTemplate/temp_arg_nontype.cpp | 16 |
12 files changed, 113 insertions, 44 deletions
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ed5724b88fb..f5bb3511f22 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<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. diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 5970120d01e..e9cd537cc84 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<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); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index bb1150fc72a..cca0f22f14a 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 1a519dcc3e5..4a681629eea 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 fbdc87b24e2..6a93658fc78 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 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'?}} } } 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 f8cc0094748..2884be146c7 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<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; diff --git a/clang/test/Parser/cxx-using-directive.cpp b/clang/test/Parser/cxx-using-directive.cpp index 76dc22f1530..5efd991c8e7 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 84ac0c899e5..63b580202af 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 529ba1023dc..619bc61f250 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 d22a8e92e0d..2da52b31f57 100644 --- a/clang/test/SemaCXX/typo-correction-pt2.cpp +++ b/clang/test/SemaCXX/typo-correction-pt2.cpp @@ -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'?}} +} +} diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp index d779e2a4480..4047e6a18ce 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<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'?}} } } diff --git a/clang/test/SemaTemplate/temp_arg_nontype.cpp b/clang/test/SemaTemplate/temp_arg_nontype.cpp index 24509524b29..4a75b11c42b 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 <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 { |