diff options
Diffstat (limited to 'clang')
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticKinds.def | 15 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.h | 6 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 160 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaType.cpp | 6 | ||||
| -rw-r--r-- | clang/test/SemaCXX/nested-name-spec.cpp | 21 |
5 files changed, 183 insertions, 25 deletions
diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index fba04101cf3..8b0bd277672 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -1169,7 +1169,20 @@ DIAG(err_typecheck_incomplete_tag, ERROR, "incomplete definition of type %0") DIAG(err_typecheck_no_member, ERROR, "no member named %0") -DIAG(err_member_redeclared, ERROR, "class member cannot be redeclared") +DIAG(err_member_redeclared, ERROR, + "class member cannot be redeclared") +DIAG(err_member_def_does_not_match, ERROR, + "out-of-line definition does not match any declaration in %0") +DIAG(err_nonstatic_member_out_of_line, ERROR, + "non-static data member defined out-of-line") +DIAG(err_qualified_typedef_declarator, ERROR, + "typedef declarator cannot be qualified") +DIAG(err_qualified_param_declarator, ERROR, + "parameter declarator cannot be qualified") +DIAG(err_out_of_line_declaration, ERROR, + "out-of-line declaration of a member must be a definition") +DIAG(note_member_def_close_match, NOTE, + "member declaration nearly matches") DIAG(err_typecheck_ivar_variable_size, ERROR, "instance variables must have a constant size") // FIXME: Improve with %select diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 528611b3a98..bebd0cfe740 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -267,7 +267,11 @@ public: // virtual TypeTy *isTypeName(IdentifierInfo &II, Scope *S, const CXXScopeSpec *SS); - virtual DeclTy *ActOnDeclarator(Scope *S, Declarator &D, DeclTy *LastInGroup); + virtual DeclTy *ActOnDeclarator(Scope *S, Declarator &D, DeclTy *LastInGroup) { + return ActOnDeclarator(S, D, LastInGroup, false); + } + DeclTy *ActOnDeclarator(Scope *S, Declarator &D, DeclTy *LastInGroup, + bool IsFunctionDefinition); virtual DeclTy *ActOnParamDeclarator(Scope *S, Declarator &D); virtual void ActOnParamDefaultArgument(DeclTy *param, SourceLocation EqualLoc, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 1578a253e91..1be30715e5c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -910,8 +910,32 @@ DeclarationName Sema::GetNameForDeclarator(Declarator &D) { return DeclarationName(); } +/// isNearlyMatchingMemberFunction - Determine whether the C++ member +/// functions Declaration and Definition are "nearly" matching. This +/// heuristic is used to improve diagnostics in the case where an +/// out-of-line member function definition doesn't match any +/// declaration within the class. +static bool isNearlyMatchingMemberFunction(ASTContext &Context, + FunctionDecl *Declaration, + FunctionDecl *Definition) { + 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(); + + DeclParamTy = Context.getCanonicalType(DeclParamTy.getNonReferenceType()); + DefParamTy = Context.getCanonicalType(DefParamTy.getNonReferenceType()); + if (DeclParamTy.getUnqualifiedType() != DefParamTy.getUnqualifiedType()) + return false; + } + + return true; +} + Sema::DeclTy * -Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { +Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, + bool IsFunctionDefinition) { ScopedDecl *LastDeclarator = dyn_cast_or_null<ScopedDecl>((Decl *)lastDecl); DeclarationName Name = GetNameForDeclarator(D); @@ -952,21 +976,21 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { // after the point of declaration in a namespace that encloses the // declarations namespace. // - // FIXME: We need to perform this check later, once we know that - // we've actually found a redeclaration. Otherwise, just the fact - // that there is some entity with the same name will suppress this - // diagnostic, e.g., we fail to diagnose: + // Note that we only check the context at this point. We don't yet + // have enough information to make sure that PrevDecl is actually + // the declaration we want to match. For example, given: + // // class X { // void f(); + // void f(float); // }; // - // void X::f(int) { } // ill-formed, but we don't complain. - if (PrevDecl == 0) { - // No previous declaration in the qualifying scope. - Diag(D.getIdentifierLoc(), diag::err_typecheck_no_member) - << Name << D.getCXXScopeSpec().getRange(); - InvalidDecl = true; - } else if (!CurContext->Encloses(DC)) { + // void X::f(int) { } // ill-formed + // + // In this case, PrevDecl will point to the overload set + // containing the two f's declared in X, but neither of them + // matches. + if (!CurContext->Encloses(DC)) { // The qualifying scope doesn't enclose the original declaration. // Emit diagnostic based on current scope. SourceLocation L = D.getIdentifierLoc(); @@ -999,6 +1023,15 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { assert(!R.isNull() && "GetTypeForDeclarator() returned null type"); if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef) { + // Typedef declarators cannot be qualified (C++ [dcl.meaning]p1). + if (D.getCXXScopeSpec().isSet()) { + Diag(D.getIdentifierLoc(), diag::err_qualified_typedef_declarator) + << D.getCXXScopeSpec().getRange(); + InvalidDecl = true; + // Pretend we didn't see the scope specifier. + DC = 0; + } + // Check that there are no default arguments (C++ only). if (getLangOptions().CPlusPlus) CheckExtraCXXDefaultArguments(D); @@ -1116,6 +1149,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { // FIXME: Move to DeclGroup... D.getDeclSpec().getSourceRange().getBegin()); } + // Handle attributes. ProcessDeclAttributes(NewFD, D); @@ -1288,17 +1322,83 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { // Check default arguments now that we have merged decls. CheckCXXDefaultArguments(NewFD); + + // An out-of-line member function declaration must also be a + // definition (C++ [dcl.meaning]p1). + if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet() && + !InvalidDecl) { + Diag(NewFD->getLocation(), diag::err_out_of_line_declaration) + << D.getCXXScopeSpec().getRange(); + NewFD->setInvalidDecl(); + } } return NewFD; } } + + if (!Redeclaration && D.getCXXScopeSpec().isSet()) { + // The user tried to provide an out-of-line definition for a + // member function, but there was no such member function + // declared (C++ [class.mfct]p2). For example: + // + // class X { + // void f() const; + // }; + // + // void X::f() { } // ill-formed + // + // Complain about this problem, and attempt to suggest close + // matches (e.g., those that differ only in cv-qualifiers and + // whether the parameter types are references). + Diag(D.getIdentifierLoc(), diag::err_member_def_does_not_match) + << cast<CXXRecordDecl>(DC)->getDeclName() + << D.getCXXScopeSpec().getRange(); + InvalidDecl = true; + + PrevDecl = LookupDecl(Name, Decl::IDNS_Ordinary, S, DC); + if (!PrevDecl) { + // Nothing to suggest. + } else if (OverloadedFunctionDecl *Ovl + = dyn_cast<OverloadedFunctionDecl>(PrevDecl)) { + for (OverloadedFunctionDecl::function_iterator + Func = Ovl->function_begin(), + FuncEnd = Ovl->function_end(); + Func != FuncEnd; ++Func) { + if (isNearlyMatchingMemberFunction(Context, *Func, NewFD)) + Diag((*Func)->getLocation(), diag::note_member_def_close_match); + + } + } else if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(PrevDecl)) { + // Suggest this no matter how mismatched it is; it's the only + // thing we have. + unsigned diag; + if (isNearlyMatchingMemberFunction(Context, Method, NewFD)) + diag = diag::note_member_def_close_match; + else if (Method->getBody()) + diag = diag::note_previous_definition; + else + diag = diag::note_previous_declaration; + Diag(Method->getLocation(), diag); + } + + PrevDecl = 0; + } } New = NewFD; - // In C++, check default arguments now that we have merged decls. - if (getLangOptions().CPlusPlus) + if (getLangOptions().CPlusPlus) { + // In C++, check default arguments now that we have merged decls. CheckCXXDefaultArguments(NewFD); + + // An out-of-line member function declaration must also be a + // definition (C++ [dcl.meaning]p1). + if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet()) { + Diag(NewFD->getLocation(), diag::err_out_of_line_declaration) + << D.getCXXScopeSpec().getRange(); + InvalidDecl = true; + } + } } else { // Check that there are no default arguments (C++ only). if (getLangOptions().CPlusPlus) @@ -1337,7 +1437,6 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { } if (DC->isCXXRecord()) { - assert(SC == VarDecl::Static && "Invalid storage class for member!"); // This is a static data member for a C++ class. NewVD = CXXClassVarDecl::Create(Context, cast<CXXRecordDecl>(DC), D.getIdentifierLoc(), II, @@ -1380,8 +1479,24 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { // Merge the decl with the existing one if appropriate. If the decl is // in an outer scope, it isn't the same thing. if (PrevDecl && isDeclInScope(PrevDecl, DC, S)) { + if (isa<FieldDecl>(PrevDecl) && D.getCXXScopeSpec().isSet()) { + // The user tried to define a non-static data member + // out-of-line (C++ [dcl.meaning]p1). + Diag(NewVD->getLocation(), diag::err_nonstatic_member_out_of_line) + << D.getCXXScopeSpec().getRange(); + NewVD->Destroy(Context); + return 0; + } + NewVD = MergeVarDecl(NewVD, PrevDecl); if (NewVD == 0) return 0; + + if (D.getCXXScopeSpec().isSet()) { + // No previous declaration in the qualifying scope. + Diag(D.getIdentifierLoc(), diag::err_typecheck_no_member) + << Name << D.getCXXScopeSpec().getRange(); + InvalidDecl = true; + } } New = NewVD; } @@ -2151,9 +2266,8 @@ Sema::DeclTy *Sema::FinalizeDeclaratorGroup(Scope *S, DeclTy *group) { /// to introduce parameters into function prototype scope. Sema::DeclTy * Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { - // FIXME: disallow CXXScopeSpec for param declarators. const DeclSpec &DS = D.getDeclSpec(); - + // Verify C99 6.7.5.3p2: The only SCS allowed is 'register'. VarDecl::StorageClass StorageClass = VarDecl::None; if (DS.getStorageClassSpec() == DeclSpec::SCS_register) { @@ -2231,6 +2345,13 @@ Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { if (D.getInvalidType()) New->setInvalidDecl(); + // Parameter declarators cannot be qualified (C++ [dcl.meaning]p1). + if (D.getCXXScopeSpec().isSet()) { + Diag(D.getIdentifierLoc(), diag::err_qualified_param_declarator) + << D.getCXXScopeSpec().getRange(); + New->setInvalidDecl(); + } + // Add the parameter declaration into this scope. S->AddDecl(New); if (II) @@ -2269,10 +2390,11 @@ Sema::DeclTy *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D) { // FIXME: Diagnose arguments without names in C. } - Scope *GlobalScope = FnBodyScope->getParent(); + Scope *ParentScope = FnBodyScope->getParent(); return ActOnStartOfFunctionDef(FnBodyScope, - ActOnDeclarator(GlobalScope, D, 0)); + ActOnDeclarator(ParentScope, D, 0, + /*IsFunctionDefinition=*/true)); } Sema::DeclTy *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, DeclTy *D) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 2423e1271b8..3f911f5b0cd 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -533,9 +533,11 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { // declaration. if (FnTy->getTypeQuals() != 0 && D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef && - (D.getContext() != Declarator::MemberContext || + ((D.getContext() != Declarator::MemberContext && + (!D.getCXXScopeSpec().isSet() || + !static_cast<DeclContext*>(D.getCXXScopeSpec().getScopeRep()) + ->isCXXRecord())) || D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) { - if (D.isFunctionDeclarator()) Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type); else diff --git a/clang/test/SemaCXX/nested-name-spec.cpp b/clang/test/SemaCXX/nested-name-spec.cpp index b6032e64946..1d91c24b969 100644 --- a/clang/test/SemaCXX/nested-name-spec.cpp +++ b/clang/test/SemaCXX/nested-name-spec.cpp @@ -12,10 +12,19 @@ A:: ; // expected-error {{expected unqualified-id}} A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} expected-error {{expected '=', ',', ';', 'asm', or '__attribute__' after declarator}} class C2 { - void m(); + void m(); // expected-note{{member declaration nearly matches}} + + void f(const int& parm); // expected-note{{member declaration nearly matches}} + void f(int) const; // expected-note{{member declaration nearly matches}} + void f(float); + int x; }; +void C2::m() const { } // expected-error{{out-of-line definition does not match any declaration in 'C2'}} + +void C2::f(int) { } // expected-error{{out-of-line definition does not match any declaration in 'C2'}} + void C2::m() { x = 0; } @@ -25,7 +34,7 @@ namespace B { } void f1() { - void A::Af(); // expected-error {{definition or redeclaration of 'Af' not allowed inside a function}} + void A::Af(); // expected-error {{definition or redeclaration of 'Af' not allowed inside a function}} } void f2() { @@ -73,3 +82,11 @@ void f3() { // make sure the following doesn't hit any asserts void f4(undef::C); // expected-error {{use of undeclared identifier 'undef'}} // expected-error {{expected ')'}} expected-note {{to match this '('}} // expected-error {{variable has incomplete type 'void'}} + +typedef void C2::f5(int); // expected-error{{typedef declarator cannot be qualified}} + +void f6(int A2::RC::x); // expected-error{{parameter declarator cannot be qualified}} + +int A2::RC::x; // expected-error{{non-static data member defined out-of-line}} + +void A2::CC::NC::m(); // expected-error{{out-of-line declaration of a member must be a definition}} |

