Skip to content

Commit 2618fc7

Browse files
committed
[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
1 parent 6932f47 commit 2618fc7

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,14 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
692692
NullConstraint Nullness = getNullConstraint(*RetSVal, State);
693693

694694
Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType);
695+
if (const auto *FunDecl = C.getLocationContext()->getDecl();
696+
FunDecl && FunDecl->getAttr<ReturnsNonNullAttr>() &&
697+
(RequiredNullability == Nullability::Unspecified ||
698+
RequiredNullability == Nullability::Nullable)) {
699+
// If a function is marked with the returns_nonnull attribute,
700+
// the return value must be non-null.
701+
RequiredNullability = Nullability::Nonnull;
702+
}
695703

696704
// If the returned value is null but the type of the expression
697705
// generating it is nonnull then we will suppress the diagnostic.

clang/test/Analysis/nullability.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability -Wno-deprecated-non-prototype -verify %s
1+
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability,debug.ExprInspection -Wno-deprecated-non-prototype -verify %s
2+
3+
void clang_analyzer_warnIfReached();
24

35
void it_takes_two(int a, int b);
46
void function_pointer_arity_mismatch() {
@@ -10,3 +12,42 @@ void block_arity_mismatch() {
1012
void(^b)() = ^(int a, int b) { };
1113
b(1); // no-crash expected-warning {{Block taking 2 arguments is called with fewer (1)}}
1214
}
15+
16+
int *nonnull_return_annotation_indirect() __attribute__((returns_nonnull));
17+
int *nonnull_return_annotation_indirect() {
18+
int *x = 0;
19+
return x; // expected-warning {{Null returned from a function that is expected to return a non-null value}}
20+
}
21+
22+
int *nonnull_return_annotation_direct() __attribute__((returns_nonnull));
23+
int *nonnull_return_annotation_direct() {
24+
return 0; // expected-warning {{Null returned from a function that is expected to return a non-null value}}
25+
} // expected-warning@-1 {{null returned from function that requires a non-null return value}}
26+
27+
int *nonnull_return_annotation_assumed() __attribute__((returns_nonnull));
28+
int *nonnull_return_annotation_assumed(int* ptr) {
29+
if (ptr) {
30+
return ptr;
31+
}
32+
return ptr; // expected-warning {{Null returned from a function that is expected to return a non-null value}}
33+
}
34+
35+
int *produce_nonnull_ptr() __attribute__((returns_nonnull));
36+
37+
__attribute__((returns_nonnull))
38+
int *cannot_return_null() {
39+
int *x = produce_nonnull_ptr();
40+
if (!x) {
41+
clang_analyzer_warnIfReached();
42+
// Incorrect: expected-warning@-1 {{REACHABLE}}
43+
// According to produce_nonnull_ptr contract, x cannot be null.
44+
}
45+
// Regardless of the potential state split above, x cannot be nullptr
46+
// according to the produce_nonnull_ptr annotation.
47+
return x;
48+
// False positive: expected-warning@-1 {{Null returned from a function that is expected to return a non-null value}}
49+
}
50+
51+
__attribute__((returns_nonnull)) int *passthrough(int *p) {
52+
return p; // no-warning: we have no evidence that `p` is null, i.e., violating the contract
53+
}

0 commit comments

Comments
 (0)