[clang] [analyzer] Report violations of the "returns_nonnull" attribute (PR #106048)
Arseniy Zaostrovnykh via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 26 07:00:08 PDT 2024
https://github.com/necto updated https://github.com/llvm/llvm-project/pull/106048
>From 7d5ae515f7727de98e7e8ce2f259e579a1f24463 Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto.ne at gmail.com>
Date: Tue, 20 Aug 2024 17:31:11 +0200
Subject: [PATCH 1/2] [analyzer] Report violations of the "returns_nonnull"
attribute
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.
CPP-4741
---
.../Checkers/NullabilityChecker.cpp | 8 ++++
clang/test/Analysis/nullability.c | 43 ++++++++++++++++++-
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
index 60934e51febe84..2035d50eea4c2d 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.
diff --git a/clang/test/Analysis/nullability.c b/clang/test/Analysis/nullability.c
index fbc03c864ad83f..9ddb9c8d2dc34f 100644
--- a/clang/test/Analysis/nullability.c
+++ b/clang/test/Analysis/nullability.c
@@ -1,4 +1,6 @@
-// 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 -Wno-deprecated-non-prototype -verify %s
+
+void clang_analyzer_warnIfReached();
void it_takes_two(int a, int b);
void function_pointer_arity_mismatch() {
@@ -10,3 +12,42 @@ void block_arity_mismatch() {
void(^b)() = ^(int a, int b) { };
b(1); // no-crash expected-warning {{Block taking 2 arguments is called with fewer (1)}}
}
+
+int *nonnull_return_annotation_indirect() __attribute__((returns_nonnull));
+int *nonnull_return_annotation_indirect() {
+ 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() __attribute__((returns_nonnull));
+int *nonnull_return_annotation_direct() {
+ 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() __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() __attribute__((returns_nonnull));
+
+__attribute__((returns_nonnull))
+int *cannot_return_null() {
+ int *x = produce_nonnull_ptr();
+ if (!x) {
+ clang_analyzer_warnIfReached();
+ // Incorrect: expected-warning at -1 {{REACHABLE}}
+ // According to produce_nonnull_ptr contract, 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
+}
>From d3b30f3f31cc3a4cc71ae967ea6fc554a4764e37 Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <arseniy.zaostrovnykh at sonarsource.com>
Date: Mon, 26 Aug 2024 15:59:59 +0200
Subject: [PATCH 2/2] Update clang/test/Analysis/nullability.c
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: DonĂ¡t Nagy <donat.nagy at ericsson.com>
---
clang/test/Analysis/nullability.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/test/Analysis/nullability.c b/clang/test/Analysis/nullability.c
index 9ddb9c8d2dc34f..341d29c6e99d1e 100644
--- a/clang/test/Analysis/nullability.c
+++ b/clang/test/Analysis/nullability.c
@@ -39,8 +39,9 @@ int *cannot_return_null() {
int *x = produce_nonnull_ptr();
if (!x) {
clang_analyzer_warnIfReached();
- // Incorrect: expected-warning at -1 {{REACHABLE}}
- // According to produce_nonnull_ptr contract, x cannot be null.
+ // 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.
More information about the cfe-commits
mailing list