[cfe-commits] r61058 - in /cfe/trunk: include/clang/Basic/DiagnosticKinds.def lib/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaType.cpp test/SemaCXX/nested-name-spec.cpp
Douglas Gregor
dgregor at apple.com
Mon Dec 15 15:53:10 PST 2008
Author: dgregor
Date: Mon Dec 15 17:53:10 2008
New Revision: 61058
URL: http://llvm.org/viewvc/llvm-project?rev=61058&view=rev
Log:
Diagnose erroneous uses of out-of-line member definitions and scope
specifiers. Specifically:
* Determine when an out-of-line function definition does not match
any declaration within the class or namespace (including coping
with overloaded functions).
* Complain about typedefs and parameters that have scope specifiers.
* Complain about out-of-line declarations that aren't also
definitions.
* Complain about non-static data members being declared out-of-line.
* Allow cv-qualifiers on out-of-line member function definitions.
Modified:
cfe/trunk/include/clang/Basic/DiagnosticKinds.def
cfe/trunk/lib/Sema/Sema.h
cfe/trunk/lib/Sema/SemaDecl.cpp
cfe/trunk/lib/Sema/SemaType.cpp
cfe/trunk/test/SemaCXX/nested-name-spec.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticKinds.def?rev=61058&r1=61057&r2=61058&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticKinds.def Mon Dec 15 17:53:10 2008
@@ -1169,7 +1169,20 @@
"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
Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=61058&r1=61057&r2=61058&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Mon Dec 15 17:53:10 2008
@@ -267,7 +267,11 @@
//
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,
Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=61058&r1=61057&r2=61058&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon Dec 15 17:53:10 2008
@@ -910,8 +910,32 @@
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 @@
// 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 @@
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 @@
// FIXME: Move to DeclGroup...
D.getDeclSpec().getSourceRange().getBegin());
}
+
// Handle attributes.
ProcessDeclAttributes(NewFD, D);
@@ -1288,17 +1322,83 @@
// 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 @@
}
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 @@
// 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 @@
/// 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 @@
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 @@
// 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) {
Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=61058&r1=61057&r2=61058&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Mon Dec 15 17:53:10 2008
@@ -533,9 +533,11 @@
// 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
Modified: cfe/trunk/test/SemaCXX/nested-name-spec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nested-name-spec.cpp?rev=61058&r1=61057&r2=61058&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/nested-name-spec.cpp (original)
+++ cfe/trunk/test/SemaCXX/nested-name-spec.cpp Mon Dec 15 17:53:10 2008
@@ -12,10 +12,19 @@
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 @@
}
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 @@
// 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}}
More information about the cfe-commits
mailing list