diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index cb739fb99f3f..586969268c93 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -693,6 +693,12 @@ def NonNull : InheritableAttr { } }]; } +def ReturnsNonNull : InheritableAttr { + let Spellings = [GNU<"returns_nonnull">]; + let Subjects = SubjectList<[ObjCMethod, FunctionLike, HasFunctionProto], + WarnDiag, "ExpectedFunctionOrMethod">; +} + def NoReturn : InheritableAttr { let Spellings = [GNU<"noreturn">, CXX11<"gnu", "noreturn">, Declspec<"noreturn">]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 59ade9e3dc4b..ac5b406b801b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1847,6 +1847,9 @@ def warn_attribute_pointers_only : Warning< "%0 attribute only applies to pointer arguments">, InGroup; def err_attribute_pointers_only : Error; +def warn_attribute_return_pointers_only : Warning< + "%0 attribute only applies to return values that are pointers">, + InGroup; def err_attribute_no_member_pointers : Error< "%0 attribute cannot be used with pointers to members">; def err_attribute_invalid_implicit_this_argument : Error< diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index eaad404666e9..ed8610cec39e 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1157,12 +1157,14 @@ static void possibleTransparentUnionPointerType(QualType &T) { } static bool attrNonNullArgCheck(Sema &S, QualType T, const AttributeList &Attr, - SourceRange R) { + SourceRange R, bool isReturnValue = false) { T = T.getNonReferenceType(); possibleTransparentUnionPointerType(T); if (!T->isAnyPointerType() && !T->isBlockPointerType()) { - S.Diag(Attr.getLoc(), diag::warn_attribute_pointers_only) + S.Diag(Attr.getLoc(), + isReturnValue ? diag::warn_attribute_return_pointers_only + : diag::warn_attribute_pointers_only) << Attr.getName() << R; return false; } @@ -1231,6 +1233,23 @@ static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &Attr) { Attr.getAttributeSpellingListIndex())); } +static void handleReturnsNonNullAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + QualType ResultType; + if (const FunctionType *Ty = D->getFunctionType()) + ResultType = Ty->getResultType(); + else if (const ObjCMethodDecl *MD = dyn_cast(D)) + ResultType = MD->getResultType(); + + if (!attrNonNullArgCheck(S, ResultType, Attr, Attr.getRange(), + /* isReturnValue */ true)) + return; + + D->addAttr(::new (S.Context) + ReturnsNonNullAttr(Attr.getRange(), S.Context, + Attr.getAttributeSpellingListIndex())); +} + static const char *ownershipKindToDiagName(OwnershipAttr::OwnershipKind K) { switch (K) { case OwnershipAttr::Holds: return "'ownership_holds'"; @@ -3978,6 +3997,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, else handleNonNullAttr(S, D, Attr); break; + case AttributeList::AT_ReturnsNonNull: + handleReturnsNonNullAttr(S, D, Attr); + break; case AttributeList::AT_Overloadable: handleSimpleAttribute(S, D, Attr); break; case AttributeList::AT_Ownership: handleOwnershipAttr (S, D, Attr); break; diff --git a/clang/test/Sema/nonnull.c b/clang/test/Sema/nonnull.c index 3c560752749a..f1ba482453f2 100644 --- a/clang/test/Sema/nonnull.c +++ b/clang/test/Sema/nonnull.c @@ -32,4 +32,10 @@ void test_baz() { baz3(0); // no-warning } +void test_void_returns_nonnull() __attribute__((returns_nonnull)); // expected-warning {{'returns_nonnull' attribute only applies to return values that are pointers}} +int test_int_returns_nonnull() __attribute__((returns_nonnull)); // expected-warning {{'returns_nonnull' attribute only applies to return values that are pointers}} +void *test_ptr_returns_nonnull() __attribute__((returns_nonnull)); // no-warning + int i __attribute__((nonnull)); // expected-warning {{'nonnull' attribute only applies to functions, methods, and parameters}} +int j __attribute__((returns_nonnull)); // expected-warning {{'returns_nonnull' attribute only applies to functions and methods}} + diff --git a/clang/test/SemaObjC/nonnull.m b/clang/test/SemaObjC/nonnull.m index d9ecbdc37eeb..f6e4f57d2a83 100644 --- a/clang/test/SemaObjC/nonnull.m +++ b/clang/test/SemaObjC/nonnull.m @@ -74,6 +74,9 @@ void func6(dispatch_object_t _head) { @interface NSObject - (void)doSomethingWithNonNullPointer:(void *)ptr :(int)iarg : (void*)ptr1 __attribute__((nonnull(1, 3))); + (void)doSomethingClassyWithNonNullPointer:(void *)ptr __attribute__((nonnull(1))); +- (void*)returnsCNonNull __attribute__((returns_nonnull)); // no-warning +- (id)returnsObjCNonNull __attribute__((returns_nonnull)); // no-warning +- (int)returnsIntNonNull __attribute__((returns_nonnull)); // expected-warning {{'returns_nonnull' attribute only applies to return values that are pointers}} @end extern void DoSomethingNotNull(void *db) __attribute__((nonnull(1)));