[clang] 4f33e7c - [analyzer] Report violations of the "returns_nonnull" attribute (#106048)

via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 27 05:41:56 PDT 2024


Author: Arseniy Zaostrovnykh
Date: 2024-08-27T14:41:52+02:00
New Revision: 4f33e7c683104ea72e013d4ddd104b711a25d620

URL: https://github.com/llvm/llvm-project/commit/4f33e7c683104ea72e013d4ddd104b711a25d620
DIFF: https://github.com/llvm/llvm-project/commit/4f33e7c683104ea72e013d4ddd104b711a25d620.diff

LOG: [analyzer] Report violations of the "returns_nonnull" attribute (#106048)

Make sure code respects the GNU-extension `__attribute__((returns_nonnull))`.

Extend the NullabilityChecker to check that a function returns_nonnull
does not return a nullptr.

This commit also reverts an old hack introduced by
49bd58f1ebe28d97e4949e9c757bc5dfd8b2d72f
because it is no longer needed

CPP-4741

Added: 
    

Modified: 
    clang/docs/analyzer/checkers.rst
    clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
    clang/test/Analysis/nullability.c
    clang/test/Analysis/nullability.mm

Removed: 
    


################################################################################
diff  --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 0bfbc995579d41..89a1018e14c0e6 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -571,7 +571,7 @@ if ((y = make_int())) {
 nullability
 ^^^^^^^^^^^
 
-Objective C checkers that warn for null pointer passing and dereferencing errors.
+Checkers (mostly Objective C) that warn for null pointer passing and dereferencing errors.
 
 .. _nullability-NullPassedToNonnull:
 
@@ -588,8 +588,8 @@ Warns when a null pointer is passed to a pointer which has a _Nonnull type.
 
 .. _nullability-NullReturnedFromNonnull:
 
-nullability.NullReturnedFromNonnull (ObjC)
-""""""""""""""""""""""""""""""""""""""""""
+nullability.NullReturnedFromNonnull (C, C++, ObjC)
+""""""""""""""""""""""""""""""""""""""""""""""""""
 Warns when a null pointer is returned from a function that has _Nonnull return type.
 
 .. code-block:: objc
@@ -604,6 +604,22 @@ Warns when a null pointer is returned from a function that has _Nonnull return t
    return result;
  }
 
+Warns when a null pointer is returned from a function annotated with ``__attribute__((returns_nonnull))``
+
+.. code-block:: cpp
+
+ int global;
+ __attribute__((returns_nonnull)) void* getPtr(void* p);
+
+ void* getPtr(void* p) {
+   if (p) { // forgot to negate the condition
+     return &global;
+   }
+   // Warning: nullptr returned from a function that is expected
+   // to return a non-null value
+   return p;
+ }
+
 .. _nullability-NullableDereferenced:
 
 nullability.NullableDereferenced (ObjC)

diff  --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
index 60934e51febe84..04472bb3895a78 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -692,6 +692,14 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
   NullConstraint Nullness = getNullConstraint(*RetSVal, State);
 
   Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType);
+  if (const auto *FunDecl = C.getLocationContext()->getDecl();
+      FunDecl && FunDecl->getAttr<ReturnsNonNullAttr>() &&
+      (RequiredNullability == Nullability::Unspecified ||
+       RequiredNullability == Nullability::Nullable)) {
+    // If a function is marked with the returns_nonnull attribute,
+    // the return value must be non-null.
+    RequiredNullability = Nullability::Nonnull;
+  }
 
   // If the returned value is null but the type of the expression
   // generating it is nonnull then we will suppress the diagnostic.
@@ -705,7 +713,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
                                   Nullness == NullConstraint::IsNull);
   if (ChecksEnabled[CK_NullReturnedFromNonnull] && NullReturnedFromNonNull &&
       RetExprTypeLevelNullability != Nullability::Nonnull &&
-      !InSuppressedMethodFamily && C.getLocationContext()->inTopFrame()) {
+      !InSuppressedMethodFamily) {
     static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull");
     ExplodedNode *N = C.generateErrorNode(State, &Tag);
     if (!N)

diff  --git a/clang/test/Analysis/nullability.c b/clang/test/Analysis/nullability.c
index fbc03c864ad83f..57ce9ac8aee3d0 100644
--- a/clang/test/Analysis/nullability.c
+++ b/clang/test/Analysis/nullability.c
@@ -1,12 +1,65 @@
-// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability -Wno-deprecated-non-prototype -verify %s
+// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached(void);
 
 void it_takes_two(int a, int b);
-void function_pointer_arity_mismatch() {
+void function_pointer_arity_mismatch(void) {
   void(*fptr)() = it_takes_two;
   fptr(1); // no-crash expected-warning {{Function taking 2 arguments is called with fewer (1)}}
+  // expected-warning at -1 {{passing arguments to a function without a prototype is deprecated in all versions of C and is not supported in C23}}
 }
 
-void block_arity_mismatch() {
+void block_arity_mismatch(void) {
   void(^b)() = ^(int a, int b) { };
   b(1);  // no-crash expected-warning {{Block taking 2 arguments is called with fewer (1)}}
+  // expected-warning at -1 {{passing arguments to a function without a prototype is deprecated in all versions of C and is not supported in C23}}
+}
+
+int *nonnull_return_annotation_indirect(void) __attribute__((returns_nonnull));
+int *nonnull_return_annotation_indirect(void) {
+  int *x = 0;
+  return x; // expected-warning {{Null returned from a function that is expected to return a non-null value}}
+}
+
+int *nonnull_return_annotation_direct(void) __attribute__((returns_nonnull));
+int *nonnull_return_annotation_direct(void) {
+  return 0; // expected-warning {{Null returned from a function that is expected to return a non-null value}}
+} // expected-warning at -1 {{null returned from function that requires a non-null return value}}
+
+int *nonnull_return_annotation_assumed(int* ptr) __attribute__((returns_nonnull));
+int *nonnull_return_annotation_assumed(int* ptr) {
+  if (ptr) {
+    return ptr;
+  }
+  return ptr; // expected-warning {{Null returned from a function that is expected to return a non-null value}}
+}
+
+int *produce_nonnull_ptr(void) __attribute__((returns_nonnull));
+
+__attribute__((returns_nonnull))
+int *cannot_return_null(void) {
+  int *x = produce_nonnull_ptr();
+  if (!x) {
+    clang_analyzer_warnIfReached();
+    // expected-warning at -1 {{REACHABLE}}
+    // TODO: This warning is a false positive, according to the contract of
+    // produce_nonnull_ptr, x cannot be null.
+  }
+  // Regardless of the potential state split above, x cannot be nullptr
+  // according to the produce_nonnull_ptr annotation.
+  return x;
+  // False positive: expected-warning at -1 {{Null returned from a function that is expected to return a non-null value}}
+}
+
+__attribute__((returns_nonnull)) int *passthrough(int *p) {
+  return p; // no-warning: we have no evidence that `p` is null, i.e., violating the contract
+}
+
+__attribute__((returns_nonnull)) int *passthrough2(int *p);
+int *passthrough2(int *p) {
+  return p; // expected-warning{{Null returned from a function that is expected to return a non-null value}}
+}
+
+void call_with_null(void) {
+  passthrough2(0);
 }

diff  --git a/clang/test/Analysis/nullability.mm b/clang/test/Analysis/nullability.mm
index d69116d03df746..64222d939bdd06 100644
--- a/clang/test/Analysis/nullability.mm
+++ b/clang/test/Analysis/nullability.mm
@@ -438,7 +438,7 @@ -(Dummy *)callerWithParam:(Dummy * _Nonnull) p1 {
 
 int * _Nonnull InlinedReturnNullOverSuppressionCallee(int * _Nonnull p2) {
   int *result = 0;
-  return result; // no-warning; but this is an over suppression
+  return result; // expected-warning{{Null returned from a function that is expected to return a non-null value}}
 }
 
 int *InlinedReturnNullOverSuppressionCaller(int * _Nonnull p1) {


        


More information about the cfe-commits mailing list