From f356419bf57bd7cedb6b925d2eae619132f2e052 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 26 Apr 2010 17:32:49 +0000 Subject: [PATCH] Refactor Objective-C @catch parameter checking by detangling it from function-parameter checking and splitting it into the normal ActOn*/Build* pair in Sema. We now use VarDecl to represent the @catch parameter rather than the ill-fitting ParmVarDecl. llvm-svn: 102347 --- .../clang/Basic/DiagnosticSemaKinds.td | 9 ++ clang/include/clang/Parse/DeclSpec.h | 3 +- clang/lib/Sema/Sema.h | 8 +- clang/lib/Sema/SemaDeclObjC.cpp | 94 +++++++++++++++++-- clang/lib/Sema/SemaStmt.cpp | 22 +---- clang/test/SemaObjC/stmts.m | 4 + 6 files changed, 115 insertions(+), 25 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 534b451d97d6..7f2c4b5d84d5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2083,6 +2083,15 @@ def err_catch_param_not_objc_type : Error< "@catch parameter is not a pointer to an interface type">; def err_illegal_qualifiers_on_catch_parm : Error< "illegal qualifiers on @catch parameter">; +def err_storage_spec_on_catch_parm : Error< + "@catch parameter cannot have storage specifier %select{|'typedef'|'extern'|" + "'static'|'auto'|'register'|'__private_extern__'|'mutable'}0">; +def warn_register_objc_catch_parm : Warning< + "'register' storage specifier on @catch parameter will be ignored">; +def err_qualified_objc_catch_parm : Error< + "@catch parameter declarator cannot be qualified">; + + def warn_setter_getter_impl_required : Warning< "property %0 requires method %1 to be defined - " "use @synthesize, @dynamic or provide a method implementation">; diff --git a/clang/include/clang/Parse/DeclSpec.h b/clang/include/clang/Parse/DeclSpec.h index 6642e1b9f4fd..9c19a674732a 100644 --- a/clang/include/clang/Parse/DeclSpec.h +++ b/clang/include/clang/Parse/DeclSpec.h @@ -82,8 +82,9 @@ public: class DeclSpec { public: // storage-class-specifier + // Note: The order of these enumerators is important for diagnostics. enum SCS { - SCS_unspecified, + SCS_unspecified = 0, SCS_typedef, SCS_extern, SCS_static, diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 8f3d737341c7..5eb95714dcdb 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -812,7 +812,6 @@ public: SourceLocation NameLoc, VarDecl::StorageClass StorageClass, VarDecl::StorageClass StorageClassAsWritten); - virtual DeclPtrTy ActOnObjCExceptionDecl(Scope *S, Declarator &D); virtual void ActOnParamDefaultArgument(DeclPtrTy param, SourceLocation EqualLoc, ExprArg defarg); @@ -1675,6 +1674,13 @@ public: SourceLocation RParenLoc, bool MSAsm = false); + + VarDecl *BuildObjCExceptionDecl(TypeSourceInfo *TInfo, QualType ExceptionType, + IdentifierInfo *Name, SourceLocation NameLoc, + bool Invalid = false); + + virtual DeclPtrTy ActOnObjCExceptionDecl(Scope *S, Declarator &D); + virtual OwningStmtResult ActOnObjCAtCatchStmt(SourceLocation AtLoc, SourceLocation RParen, DeclPtrTy Parm, StmtArg Body); diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 8ee5d3292ee5..b7ef8df16878 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -1707,10 +1707,92 @@ void Sema::ActOnDefs(Scope *S, DeclPtrTy TagD, SourceLocation DeclStart, } } -Sema::DeclPtrTy Sema::ActOnObjCExceptionDecl(Scope *S, Declarator &D) { - // FIXME: Perform checking on the declaration here. - DeclPtrTy Dcl = ActOnParamDeclarator(S, D); - if (Dcl.get()) - cast(Dcl.getAs())->setDeclContext(CurContext); - return Dcl; +/// \brief Build a type-check a new Objective-C exception variable declaration. +VarDecl *Sema::BuildObjCExceptionDecl(TypeSourceInfo *TInfo, + QualType T, + IdentifierInfo *Name, + SourceLocation NameLoc, + bool Invalid) { + // ISO/IEC TR 18037 S6.7.3: "The type of an object with automatic storage + // duration shall not be qualified by an address-space qualifier." + // Since all parameters have automatic store duration, they can not have + // an address space. + if (T.getAddressSpace() != 0) { + Diag(NameLoc, diag::err_arg_with_address_space); + Invalid = true; + } + + // An @catch parameter must be an unqualified object pointer type; + // FIXME: Recover from "NSObject foo" by inserting the * in "NSObject *foo"? + if (Invalid) { + // Don't do any further checking. + } else if (!T->isObjCObjectPointerType()) { + Invalid = true; + Diag(NameLoc ,diag::err_catch_param_not_objc_type); + } else if (T->isObjCQualifiedIdType()) { + Invalid = true; + Diag(NameLoc, diag::err_illegal_qualifiers_on_catch_parm); + } + + VarDecl *New = VarDecl::Create(Context, CurContext, NameLoc, Name, T, TInfo, + VarDecl::None, VarDecl::None); + if (Invalid) + New->setInvalidDecl(); + return New; +} + +Sema::DeclPtrTy Sema::ActOnObjCExceptionDecl(Scope *S, Declarator &D) { + const DeclSpec &DS = D.getDeclSpec(); + + // We allow the "register" storage class on exception variables because + // GCC did, but we drop it completely. Any other storage class is an error. + if (DS.getStorageClassSpec() == DeclSpec::SCS_register) { + Diag(DS.getStorageClassSpecLoc(), diag::warn_register_objc_catch_parm) + << FixItHint::CreateRemoval(SourceRange(DS.getStorageClassSpecLoc())); + } else if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified) { + Diag(DS.getStorageClassSpecLoc(), diag::err_storage_spec_on_catch_parm) + << DS.getStorageClassSpec(); + } + if (D.getDeclSpec().isThreadSpecified()) + Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + D.getMutableDeclSpec().ClearStorageClassSpecs(); + + DiagnoseFunctionSpecifiers(D); + + // Check that there are no default arguments inside the type of this + // exception object (C++ only). + if (getLangOptions().CPlusPlus) + CheckExtraCXXDefaultArguments(D); + + TypeSourceInfo *TInfo = 0; + TagDecl *OwnedDecl = 0; + QualType ExceptionType = GetTypeForDeclarator(D, S, &TInfo, &OwnedDecl); + + if (getLangOptions().CPlusPlus && OwnedDecl && OwnedDecl->isDefinition()) { + // Objective-C++: Types shall not be defined in exception types. + Diag(OwnedDecl->getLocation(), diag::err_type_defined_in_param_type) + << Context.getTypeDeclType(OwnedDecl); + } + + VarDecl *New = BuildObjCExceptionDecl(TInfo, ExceptionType, D.getIdentifier(), + D.getIdentifierLoc(), + D.isInvalidType()); + + // Parameter declarators cannot be qualified (C++ [dcl.meaning]p1). + if (D.getCXXScopeSpec().isSet()) { + Diag(D.getIdentifierLoc(), diag::err_qualified_objc_catch_parm) + << D.getCXXScopeSpec().getRange(); + New->setInvalidDecl(); + } + + // Add the parameter declaration into this scope. + S->AddDecl(DeclPtrTy::make(New)); + if (D.getIdentifier()) + IdResolver.AddDecl(New); + + ProcessDeclAttributes(S, New, D); + + if (New->hasAttr()) + Diag(New->getLocation(), diag::err_block_on_nonlocal); + return DeclPtrTy::make(New); } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index a5292efea172..9d6132d050c6 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1527,23 +1527,11 @@ Action::OwningStmtResult Sema::ActOnObjCAtCatchStmt(SourceLocation AtLoc, SourceLocation RParen, DeclPtrTy Parm, StmtArg Body) { - ParmVarDecl *PVD = cast_or_null(Parm.getAs()); - - // PVD == 0 implies @catch(...). - if (PVD) { - // If we already know the decl is invalid, reject it. - if (PVD->isInvalidDecl()) - return StmtError(); - - if (!PVD->getType()->isObjCObjectPointerType()) - return StmtError(Diag(PVD->getLocation(), - diag::err_catch_param_not_objc_type)); - if (PVD->getType()->isObjCQualifiedIdType()) - return StmtError(Diag(PVD->getLocation(), - diag::err_illegal_qualifiers_on_catch_parm)); - } - - return Owned(new (Context) ObjCAtCatchStmt(AtLoc, RParen, PVD, + VarDecl *Var = cast_or_null(Parm.getAs()); + if (Var && Var->isInvalidDecl()) + return StmtError(); + + return Owned(new (Context) ObjCAtCatchStmt(AtLoc, RParen, Var, Body.takeAs())); } diff --git a/clang/test/SemaObjC/stmts.m b/clang/test/SemaObjC/stmts.m index 20a9f55b2095..d1e2ad3612e0 100644 --- a/clang/test/SemaObjC/stmts.m +++ b/clang/test/SemaObjC/stmts.m @@ -2,12 +2,16 @@ struct some_struct; +@interface NSObject +@end + // Note: NSException is not declared. void f0(id x) { @try { } @catch (NSException *x) { // expected-error {{unknown type name 'NSException'}} } @catch (struct some_struct x) { // expected-error {{@catch parameter is not a pointer to an interface type}} } @catch (int x) { // expected-error {{@catch parameter is not a pointer to an interface type}} + } @catch (static NSObject *y) { // expected-error {{@catch parameter cannot have storage specifier 'static'}} } @catch (...) { } }