diff options
-rw-r--r-- | include/clang/AST/Type.h | 3 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 3 | ||||
-rw-r--r-- | lib/AST/Type.cpp | 20 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 51 | ||||
-rw-r--r-- | test/SemaCXX/nested-name-spec.cpp | 4 | ||||
-rw-r--r-- | test/SemaCXX/out-of-line-def-mismatch.cpp | 24 |
6 files changed, 95 insertions, 10 deletions
diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 2b72610226..d0d69651e2 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -503,6 +503,9 @@ public: const Type *getTypePtrOrNull() const; + /// Retrieves a pointer to the name of the base type. + const IdentifierInfo *getBaseTypeIdentifier() const; + /// Divides a QualType into its unqualified type and a set of local /// qualifiers. SplitQualType split() const; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7a79c5b27c..236368ce3a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2971,6 +2971,9 @@ def warn_member_extra_qualification : Warning< def err_member_qualification : Error< "non-friend class member %0 cannot have a qualified name">; def note_member_def_close_match : Note<"member declaration nearly matches">; +def note_member_def_close_param_match : Note< + "type of %ordinal0 parameter of member declaration does not match " + "definition (%1 vs %2)">; def err_typecheck_ivar_variable_size : Error< "instance variables must have a constant size">; def err_ivar_reference_type : Error< diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index b32908bc8f..81627f2bf9 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -42,6 +42,26 @@ bool Qualifiers::isStrictSupersetOf(Qualifiers Other) const { (hasObjCLifetime() && !Other.hasObjCLifetime())); } +const IdentifierInfo* QualType::getBaseTypeIdentifier() const { + const Type* ty = getTypePtr(); + NamedDecl *ND = NULL; + if (ty->isPointerType() || ty->isReferenceType()) + return ty->getPointeeType().getBaseTypeIdentifier(); + else if (ty->isRecordType()) + ND = ty->getAs<RecordType>()->getDecl(); + else if (ty->isEnumeralType()) + ND = ty->getAs<EnumType>()->getDecl(); + else if (ty->getTypeClass() == Type::Typedef) + ND = ty->getAs<TypedefType>()->getDecl(); + else if (ty->isArrayType()) + return ty->castAsArrayTypeUnsafe()-> + getElementType().getBaseTypeIdentifier(); + + if (ND) + return ND->getIdentifier(); + return NULL; +} + bool QualType::isConstant(QualType T, ASTContext &Ctx) { if (T.isConstQualified()) return true; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 2b6cf312a1..542614ed9e 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2884,22 +2884,47 @@ Sema::GetNameFromUnqualifiedId(const UnqualifiedId &Name) { return DeclarationNameInfo(); } +static QualType getCoreType(QualType Ty) { + do { + if (Ty->isPointerType() || Ty->isReferenceType()) + Ty = Ty->getPointeeType(); + else if (Ty->isArrayType()) + Ty = Ty->castAsArrayTypeUnsafe()->getElementType(); + else + return Ty.withoutLocalFastQualifiers(); + } while (true); +} + /// isNearlyMatchingFunction - Determine whether the C++ functions /// Declaration and Definition are "nearly" matching. This heuristic /// is used to improve diagnostics in the case where an out-of-line -/// function definition doesn't match any declaration within -/// the class or namespace. +/// function definition doesn't match any declaration within the class +/// or namespace. Also sets Params to the list of indices to the +/// parameters that differ between the declaration and the definition. static bool isNearlyMatchingFunction(ASTContext &Context, FunctionDecl *Declaration, - FunctionDecl *Definition) { + FunctionDecl *Definition, + llvm::SmallVectorImpl<unsigned> &Params) { + Params.clear(); if (Declaration->param_size() != Definition->param_size()) return false; for (unsigned Idx = 0; Idx < Declaration->param_size(); ++Idx) { QualType DeclParamTy = Declaration->getParamDecl(Idx)->getType(); QualType DefParamTy = Definition->getParamDecl(Idx)->getType(); - if (!Context.hasSameUnqualifiedType(DeclParamTy.getNonReferenceType(), - DefParamTy.getNonReferenceType())) + // The parameter types are identical + if (DefParamTy == DeclParamTy) + continue; + + QualType DeclParamBaseTy = getCoreType(DeclParamTy); + QualType DefParamBaseTy = getCoreType(DefParamTy); + const IdentifierInfo *DeclTyName = DeclParamBaseTy.getBaseTypeIdentifier(); + const IdentifierInfo *DefTyName = DefParamBaseTy.getBaseTypeIdentifier(); + + if (Context.hasSameUnqualifiedType(DeclParamBaseTy, DefParamBaseTy) || + (DeclTyName && DeclTyName == DefTyName)) + Params.push_back(Idx); + else // The two parameters aren't even close return false; } @@ -4155,14 +4180,24 @@ bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) { static void DiagnoseInvalidRedeclaration(Sema &S, FunctionDecl *NewFD) { LookupResult Prev(S, NewFD->getDeclName(), NewFD->getLocation(), Sema::LookupOrdinaryName, Sema::ForRedeclaration); + llvm::SmallVector<unsigned, 1> MismatchedParams; S.LookupQualifiedName(Prev, NewFD->getDeclContext()); assert(!Prev.isAmbiguous() && "Cannot have an ambiguity in previous-declaration lookup"); for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end(); Func != FuncEnd; ++Func) { - if (isa<FunctionDecl>(*Func) && - isNearlyMatchingFunction(S.Context, cast<FunctionDecl>(*Func), NewFD)) - S.Diag((*Func)->getLocation(), diag::note_member_def_close_match); + FunctionDecl *FD = dyn_cast<FunctionDecl>(*Func); + if (FD && isNearlyMatchingFunction(S.Context, FD, NewFD, + MismatchedParams)) { + if (MismatchedParams.size() > 0) { + unsigned Idx = MismatchedParams.front(); + ParmVarDecl *FDParam = FD->getParamDecl(Idx); + S.Diag(FDParam->getTypeSpecStartLoc(), + diag::note_member_def_close_param_match) + << Idx+1 << FDParam->getType() << NewFD->getParamDecl(Idx)->getType(); + } else + S.Diag(FD->getLocation(), diag::note_member_def_close_match); + } } } diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp index ee6ca88573..8e73c3e2ed 100644 --- a/test/SemaCXX/nested-name-spec.cpp +++ b/test/SemaCXX/nested-name-spec.cpp @@ -29,7 +29,7 @@ static int A::C::cx2 = 17; // expected-error{{'static' can}} class C2 { void m(); // expected-note{{member declaration nearly matches}} - void f(const int& parm); // expected-note{{member declaration nearly matches}} + void f(const int& parm); // expected-note{{type of 1st parameter of member declaration does not match definition ('const int &' vs 'int')}} void f(int) const; // expected-note{{member declaration nearly matches}} void f(float); @@ -140,7 +140,7 @@ Operators::operator bool() { } namespace A { - void g(int&); // expected-note{{member declaration nearly matches}} + void g(int&); // expected-note{{type of 1st parameter of member declaration does not match definition ('int &' vs 'const int &')}} } void A::f() {} // expected-error{{out-of-line definition of 'f' does not match any declaration in namespace 'A'}} diff --git a/test/SemaCXX/out-of-line-def-mismatch.cpp b/test/SemaCXX/out-of-line-def-mismatch.cpp new file mode 100644 index 0000000000..6ade5b802b --- /dev/null +++ b/test/SemaCXX/out-of-line-def-mismatch.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++98 -verify %s + +namespace N2 { + struct S1; + + namespace N1 { + class C1 {}; + + struct S2 { + void func(S1*); // expected-note {{type of 1st parameter of member declaration does not match definition ('N2::S1 *' vs 'N2::N1::S1 *')}} + void func(C1&, unsigned, const S1*); // expected-note {{type of 3rd parameter of member declaration does not match definition ('const N2::S1 *' vs 'const N2::N1::S1 *')}} + void func(const S1*, unsigned); //expected-note {{type of 1st parameter of member declaration does not match definition ('const N2::S1 *' vs 'N2::N1::S1')}} + void func(unsigned, const S1*); // expected-note {{type of 1st parameter of member declaration does not match definition ('unsigned int' vs 'unsigned int *')}} + }; + + struct S1 {}; + } +} + +void N2::N1::S2::func(S1*) {} // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}} +void N2::N1::S2::func(C1&, unsigned, const S1*) {} // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}} +void N2::N1::S2::func(S1*, double) {} // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}} +void N2::N1::S2::func(S1, unsigned) {} // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}} +void N2::N1::S2::func(unsigned*, S1*) {} // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}} |