From: John McCall Date: Tue, 25 Jan 2011 03:31:58 +0000 (+0000) Subject: Add the ns_consumes_self, ns_consumed, cf_consumed, and ns_returns_autoreleased X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c7ad38168d329d778e884a8b6400bcbed8dc85ee;p=clang Add the ns_consumes_self, ns_consumed, cf_consumed, and ns_returns_autoreleased attributes for the benefit of the static analyzer. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124174 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 49d3ab6239..9b0982a29b 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -146,10 +146,17 @@ def CDecl : InheritableAttr { def CFReturnsRetained : InheritableAttr { let Spellings = ["cf_returns_retained"]; + let Subjects = [ObjCMethod, Function]; } def CFReturnsNotRetained : InheritableAttr { let Spellings = ["cf_returns_not_retained"]; + let Subjects = [ObjCMethod, Function]; +} + +def CFConsumed : InheritableAttr { + let Spellings = ["cf_consumed"]; + let Subjects = [ParmVar]; } def Cleanup : InheritableAttr { @@ -326,10 +333,27 @@ def NoThrow : InheritableAttr { def NSReturnsRetained : InheritableAttr { let Spellings = ["ns_returns_retained"]; + let Subjects = [ObjCMethod, Function]; } def NSReturnsNotRetained : InheritableAttr { let Spellings = ["ns_returns_not_retained"]; + let Subjects = [ObjCMethod, Function]; +} + +def NSReturnsAutoreleased : InheritableAttr { + let Spellings = ["ns_returns_autoreleased"]; + let Subjects = [ObjCMethod, Function]; +} + +def NSConsumesSelf : InheritableAttr { + let Spellings = ["ns_consumes_self"]; + let Subjects = [ObjCMethod]; +} + +def NSConsumed : InheritableAttr { + let Spellings = ["ns_consumed"]; + let Subjects = [ParmVar]; } def ObjCException : InheritableAttr { @@ -337,7 +361,7 @@ def ObjCException : InheritableAttr { } def ObjCNSObject : InheritableAttr { - let Spellings = ["NSOjbect"]; + let Spellings = ["NSObject"]; } def Overloadable : Attr { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 0d26ac1816..e29d098d82 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1040,15 +1040,15 @@ def err_alias_not_supported_on_darwin : Error < def warn_attribute_wrong_decl_type : Warning< "%0 attribute only applies to %select{function|union|" "variable and function|function or method|parameter|" - "parameter or Objective-C method |function, method or block|" + "parameter or Objective-C method|function, method or block|" "virtual method or class|function, method, or parameter|class|virtual method" - "|member|variable}1 types">; + "|member|variable|method}1 types">; def err_attribute_wrong_decl_type : Error< "%0 attribute only applies to %select{function|union|" "variable and function|function or method|parameter|" - "parameter or Objective-C method |function, method or block|" + "parameter or Objective-C method|function, method or block|" "virtual method or class|function, method, or parameter|class|virtual method" - "|member|variable}1 types">; + "|member|variable|method}1 types">; def warn_function_attribute_wrong_type : Warning< "%0 only applies to function types; type here is %1">; def warn_gnu_inline_attribute_requires_inline : Warning< @@ -1179,8 +1179,11 @@ def note_attribute_overloadable_prev_overload : Note< def err_attribute_overloadable_no_prototype : Error< "'overloadable' function %0 must have a prototype">; def warn_ns_attribute_wrong_return_type : Warning< - "%0 attribute only applies to functions or methods that " - "return a pointer or Objective-C object">; + "%0 attribute only applies to %select{functions|methods}1 that " + "return %select{an Objective-C object|a pointer}2">; +def warn_ns_attribute_wrong_parameter_type : Warning< + "%0 attribute only applies to %select{Objective-C object|pointer}1 " + "parameters">; // Function Parameter Semantic Analysis. def err_param_with_void_type : Error<"argument may not have 'void' type">; diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 432611858b..91389a4d98 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -129,6 +129,10 @@ public: AT_cf_returns_retained, // Clang-specific. AT_ns_returns_not_retained, // Clang-specific. AT_ns_returns_retained, // Clang-specific. + AT_ns_returns_autoreleased, // Clang-specific. + AT_cf_consumed, // Clang-specific. + AT_ns_consumed, // Clang-specific. + AT_ns_consumes_self, // Clang-specific. AT_objc_gc, AT_overloadable, // Clang-specific. AT_ownership_holds, // Clang-specific. diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index f5149a3ce5..77d962542b 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -105,8 +105,12 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { .Case("analyzer_noreturn", AT_analyzer_noreturn) .Case("warn_unused_result", AT_warn_unused_result) .Case("carries_dependency", AT_carries_dependency) + .Case("ns_consumed", AT_ns_consumed) + .Case("ns_consumes_self", AT_ns_consumes_self) + .Case("ns_returns_autoreleased", AT_ns_returns_autoreleased) .Case("ns_returns_not_retained", AT_ns_returns_not_retained) .Case("ns_returns_retained", AT_ns_returns_retained) + .Case("cf_consumed", AT_cf_consumed) .Case("cf_returns_not_retained", AT_cf_returns_not_retained) .Case("cf_returns_retained", AT_cf_returns_retained) .Case("ownership_returns", AT_ownership_returns) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index dce229b628..474c7cb82f 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -2441,48 +2441,116 @@ static void HandleLaunchBoundsAttr(Decl *d, const AttributeList &Attr, Sema &S){ // Checker-specific attribute handlers. //===----------------------------------------------------------------------===// -static void HandleNSReturnsRetainedAttr(Decl *d, const AttributeList &Attr, +static bool isValidSubjectOfNSAttribute(Sema &S, QualType type) { + return type->isObjCObjectPointerType() || S.Context.isObjCNSObjectType(type); +} +static bool isValidSubjectOfCFAttribute(Sema &S, QualType type) { + return type->isPointerType() || isValidSubjectOfNSAttribute(S, type); +} + +static void HandleNSConsumedAttr(Decl *d, const AttributeList &attr, Sema &S) { + ParmVarDecl *param = dyn_cast(d); + if (!param) { + S.Diag(d->getLocStart(), diag::warn_attribute_wrong_decl_type) + << SourceRange(attr.getLoc()) << attr.getName() << 4 /*parameter*/; + return; + } + + bool typeOK, cf; + if (attr.getKind() == AttributeList::AT_ns_consumed) { + typeOK = isValidSubjectOfNSAttribute(S, param->getType()); + cf = false; + } else { + typeOK = isValidSubjectOfCFAttribute(S, param->getType()); + cf = true; + } + + if (!typeOK) { + S.Diag(d->getLocStart(), diag::warn_ns_attribute_wrong_parameter_type) + << SourceRange(attr.getLoc()) << attr.getName() << cf; + return; + } + + if (cf) + param->addAttr(::new (S.Context) CFConsumedAttr(attr.getLoc(), S.Context)); + else + param->addAttr(::new (S.Context) NSConsumedAttr(attr.getLoc(), S.Context)); +} + +static void HandleNSConsumesSelfAttr(Decl *d, const AttributeList &attr, + Sema &S) { + if (!isa(d)) { + S.Diag(d->getLocStart(), diag::warn_attribute_wrong_decl_type) + << SourceRange(attr.getLoc()) << attr.getName() << 13 /*method*/; + return; + } + + d->addAttr(::new (S.Context) NSConsumesSelfAttr(attr.getLoc(), S.Context)); +} + +static void HandleNSReturnsRetainedAttr(Decl *d, const AttributeList &attr, Sema &S) { - QualType RetTy; + QualType returnType; if (ObjCMethodDecl *MD = dyn_cast(d)) - RetTy = MD->getResultType(); + returnType = MD->getResultType(); else if (FunctionDecl *FD = dyn_cast(d)) - RetTy = FD->getResultType(); + returnType = FD->getResultType(); else { - SourceLocation L = Attr.getLoc(); S.Diag(d->getLocStart(), diag::warn_attribute_wrong_decl_type) - << SourceRange(L, L) << Attr.getName() << 3 /* function or method */; + << SourceRange(attr.getLoc()) << attr.getName() + << 3 /* function or method */; return; } - if (!(S.Context.isObjCNSObjectType(RetTy) || RetTy->getAs() - || RetTy->getAs())) { - SourceLocation L = Attr.getLoc(); + bool typeOK; + bool cf; + switch (attr.getKind()) { + default: llvm_unreachable("invalid ownership attribute"); return; + case AttributeList::AT_ns_returns_autoreleased: + case AttributeList::AT_ns_returns_retained: + case AttributeList::AT_ns_returns_not_retained: + typeOK = isValidSubjectOfNSAttribute(S, returnType); + cf = false; + break; + + case AttributeList::AT_cf_returns_retained: + case AttributeList::AT_cf_returns_not_retained: + typeOK = isValidSubjectOfCFAttribute(S, returnType); + cf = true; + break; + } + + if (!typeOK) { S.Diag(d->getLocStart(), diag::warn_ns_attribute_wrong_return_type) - << SourceRange(L, L) << Attr.getName(); + << SourceRange(attr.getLoc()) + << attr.getName() << isa(d) << cf; return; } - switch (Attr.getKind()) { + switch (attr.getKind()) { default: assert(0 && "invalid ownership attribute"); return; + case AttributeList::AT_ns_returns_autoreleased: + d->addAttr(::new (S.Context) NSReturnsAutoreleasedAttr(attr.getLoc(), + S.Context)); + return; case AttributeList::AT_cf_returns_not_retained: - d->addAttr(::new (S.Context) CFReturnsNotRetainedAttr(Attr.getLoc(), + d->addAttr(::new (S.Context) CFReturnsNotRetainedAttr(attr.getLoc(), S.Context)); return; case AttributeList::AT_ns_returns_not_retained: - d->addAttr(::new (S.Context) NSReturnsNotRetainedAttr(Attr.getLoc(), + d->addAttr(::new (S.Context) NSReturnsNotRetainedAttr(attr.getLoc(), S.Context)); return; case AttributeList::AT_cf_returns_retained: - d->addAttr(::new (S.Context) CFReturnsRetainedAttr(Attr.getLoc(), + d->addAttr(::new (S.Context) CFReturnsRetainedAttr(attr.getLoc(), S.Context)); return; case AttributeList::AT_ns_returns_retained: - d->addAttr(::new (S.Context) NSReturnsRetainedAttr(Attr.getLoc(), + d->addAttr(::new (S.Context) NSReturnsRetainedAttr(attr.getLoc(), S.Context)); return; }; @@ -2629,6 +2697,12 @@ static void ProcessInheritableDeclAttr(Scope *scope, Decl *D, case AttributeList::AT_vecreturn: HandleVecReturnAttr (D, Attr, S); break; // Checker-specific. + case AttributeList::AT_cf_consumed: + case AttributeList::AT_ns_consumed: HandleNSConsumedAttr (D, Attr, S); break; + case AttributeList::AT_ns_consumes_self: + HandleNSConsumesSelfAttr(D, Attr, S); break; + + case AttributeList::AT_ns_returns_autoreleased: case AttributeList::AT_ns_returns_not_retained: case AttributeList::AT_cf_returns_not_retained: case AttributeList::AT_ns_returns_retained: diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index 3bf350f0a5..ac136cb298 100644 --- a/test/Analysis/retain-release.m +++ b/test/Analysis/retain-release.m @@ -1210,7 +1210,7 @@ typedef NSString* MyStringTy; - (MyStringTy) returnsAnOwnedTypedString NS_RETURNS_RETAINED; // no-warning - (NSString*) newString NS_RETURNS_NOT_RETAINED; // no-warning - (NSString*) newStringNoAttr; -- (int) returnsAnOwnedInt NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to functions or methods that return a pointer or Objective-C object}} +- (int) returnsAnOwnedInt NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to methods that return an Objective-C object}} @end static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to function or method types}}