summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp13
-rw-r--r--clang/lib/Sema/SemaExprMember.cpp35
-rw-r--r--clang/lib/Sema/SemaLookup.cpp14
-rw-r--r--clang/test/CXX/class.access/class.friend/p1.cpp24
-rw-r--r--clang/test/CXX/class.access/p6.cpp4
-rw-r--r--clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp6
-rw-r--r--clang/test/Parser/cxx-using-directive.cpp4
-rw-r--r--clang/test/Parser/switch-recovery.cpp6
-rw-r--r--clang/test/SemaCXX/missing-members.cpp10
-rw-r--r--clang/test/SemaCXX/typo-correction-pt2.cpp13
-rw-r--r--clang/test/SemaCXX/typo-correction.cpp12
-rw-r--r--clang/test/SemaTemplate/temp_arg_nontype.cpp16
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 {
OpenPOWER on IntegriCloud