"'true'">,
InGroup<PointerBoolConversion>;
def warn_cast_nonnull_to_bool : Warning<
- "nonnull parameter '%0' will evaluate to "
+ "nonnull %select{function call|parameter}0 '%1' will evaluate to "
"'true' on first encounter">,
InGroup<PointerBoolConversion>;
def warn_this_bool_conversion : Warning<
"comparison of %select{address of|function|array}0 '%1' %select{not |}2"
"equal to a null pointer is always %select{true|false}2">,
InGroup<TautologicalPointerCompare>;
-def warn_nonnull_parameter_compare : Warning<
- "comparison of nonnull parameter '%0' %select{not |}1"
- "equal to a null pointer is %select{true|false}1 on first encounter">,
+def warn_nonnull_expr_compare : Warning<
+ "comparison of nonnull %select{function call|parameter}0 '%1' "
+ "%select{not |}2equal to a null pointer is '%select{true|false}2' on first "
+ "encounter">,
InGroup<TautologicalPointerCompare>;
def warn_this_null_compare : Warning<
"'this' pointer cannot be null in well-defined C++ code; comparison may be "
/// Checks if a the given expression evaluates to null.
///
/// \brief Returns true if the value evaluates to null.
-static bool CheckNonNullExpr(Sema &S,
- const Expr *Expr) {
+static bool CheckNonNullExpr(Sema &S, const Expr *Expr) {
// If the expression has non-null type, it doesn't evaluate to null.
if (auto nullability
= Expr->IgnoreImplicit()->getType()->getNullability(S.Context)) {
}
}
+ auto ComplainAboutNonnullParamOrCall = [&](bool IsParam) {
+ std::string Str;
+ llvm::raw_string_ostream S(Str);
+ E->printPretty(S, nullptr, getPrintingPolicy());
+ unsigned DiagID = IsCompare ? diag::warn_nonnull_expr_compare
+ : diag::warn_cast_nonnull_to_bool;
+ Diag(E->getExprLoc(), DiagID) << IsParam << S.str()
+ << E->getSourceRange() << Range << IsEqual;
+ };
+
+ // If we have a CallExpr that is tagged with returns_nonnull, we can complain.
+ if (auto *Call = dyn_cast<CallExpr>(E->IgnoreParenImpCasts())) {
+ if (auto *Callee = Call->getDirectCallee()) {
+ if (Callee->hasAttr<ReturnsNonNullAttr>()) {
+ ComplainAboutNonnullParamOrCall(false);
+ return;
+ }
+ }
+ }
+
// Expect to find a single Decl. Skip anything more complicated.
ValueDecl *D = nullptr;
if (DeclRefExpr *R = dyn_cast<DeclRefExpr>(E)) {
// Weak Decls can be null.
if (!D || D->isWeak())
return;
-
+
// Check for parameter decl with nonnull attribute
- if (const ParmVarDecl* PV = dyn_cast<ParmVarDecl>(D)) {
- if (getCurFunction() && !getCurFunction()->ModifiedNonNullParams.count(PV))
- if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(PV->getDeclContext())) {
- unsigned NumArgs = FD->getNumParams();
- llvm::SmallBitVector AttrNonNull(NumArgs);
+ if (const auto* PV = dyn_cast<ParmVarDecl>(D)) {
+ if (getCurFunction() &&
+ !getCurFunction()->ModifiedNonNullParams.count(PV)) {
+ if (PV->hasAttr<NonNullAttr>()) {
+ ComplainAboutNonnullParamOrCall(true);
+ return;
+ }
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(PV->getDeclContext())) {
+ auto ParamIter = std::find(FD->param_begin(), FD->param_end(), PV);
+ assert(ParamIter != FD->param_end());
+ unsigned ParamNo = std::distance(FD->param_begin(), ParamIter);
+
for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
if (!NonNull->args_size()) {
- AttrNonNull.set(0, NumArgs);
- break;
- }
- for (unsigned Val : NonNull->args()) {
- if (Val >= NumArgs)
- continue;
- AttrNonNull.set(Val);
+ ComplainAboutNonnullParamOrCall(true);
+ return;
}
- }
- if (!AttrNonNull.empty())
- for (unsigned i = 0; i < NumArgs; ++i)
- if (FD->getParamDecl(i) == PV &&
- (AttrNonNull[i] || PV->hasAttr<NonNullAttr>())) {
- std::string Str;
- llvm::raw_string_ostream S(Str);
- E->printPretty(S, nullptr, getPrintingPolicy());
- unsigned DiagID = IsCompare ? diag::warn_nonnull_parameter_compare
- : diag::warn_cast_nonnull_to_bool;
- Diag(E->getExprLoc(), DiagID) << S.str() << E->getSourceRange()
- << Range << IsEqual;
+
+ for (unsigned ArgNo : NonNull->args()) {
+ if (ArgNo == ParamNo) {
+ ComplainAboutNonnullParamOrCall(true);
return;
}
+ }
+ }
}
}
-
+ }
+
QualType T = D->getType();
const bool IsArray = T->isArrayType();
const bool IsFunction = T->isFunctionType();
__attribute__((__nonnull__))
int evil_nonnull_func(int* pointer, void * pv)
{
- if (pointer == NULL) { // expected-warning {{comparison of nonnull parameter 'pointer' equal to a null pointer is false on first encounter}}
+ if (pointer == NULL) { // expected-warning {{comparison of nonnull parameter 'pointer' equal to a null pointer is 'false' on first encounter}}
return 0;
} else {
return *pointer;
else
return *pointer;
- if (pv == NULL) {} // expected-warning {{comparison of nonnull parameter 'pv' equal to a null pointer is false on first encounter}}
+ if (pv == NULL) {} // expected-warning {{comparison of nonnull parameter 'pv' equal to a null pointer is 'false' on first encounter}}
}
void set_param_to_null(int**);
int another_evil_nonnull_func(int* pointer, char ch, void * pv) __attribute__((nonnull(1, 3)));
int another_evil_nonnull_func(int* pointer, char ch, void * pv) {
- if (pointer == NULL) { // expected-warning {{comparison of nonnull parameter 'pointer' equal to a null pointer is false on first encounter}}
+ if (pointer == NULL) { // expected-warning {{comparison of nonnull parameter 'pointer' equal to a null pointer is 'false' on first encounter}}
return 0;
} else {
return *pointer;
else
return *pointer;
- if (pv == NULL) {} // expected-warning {{comparison of nonnull parameter 'pv' equal to a null pointer is false on first encounter}}
+ if (pv == NULL) {} // expected-warning {{comparison of nonnull parameter 'pv' equal to a null pointer is 'false' on first encounter}}
}
extern void *returns_null(void**);
if (p) // No warning
;
}
+
+__attribute__((returns_nonnull)) void *returns_nonnull_whee();
+
+void returns_nonnull_warning_tests() {
+ if (returns_nonnull_whee() == NULL) {} // expected-warning {{comparison of nonnull function call 'returns_nonnull_whee()' equal to a null pointer is 'false' on first encounter}}
+
+ if (returns_nonnull_whee() != NULL) {} // expected-warning {{comparison of nonnull function call 'returns_nonnull_whee()' not equal to a null pointer is 'true' on first encounter}}
+
+ if (returns_nonnull_whee()) {} // expected-warning {{nonnull function call 'returns_nonnull_whee()' will evaluate to 'true' on first encounter}}
+ if (!returns_nonnull_whee()) {} // expected-warning {{nonnull function call 'returns_nonnull_whee()' will evaluate to 'true' on first encounter}}
+
+ int and_again = !returns_nonnull_whee(); // expected-warning {{nonnull function call 'returns_nonnull_whee()' will evaluate to 'true' on first encounter}}
+ and_again = !returns_nonnull_whee(); // expected-warning {{nonnull function call 'returns_nonnull_whee()' will evaluate to 'true' on first encounter}}
+}