Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2727,7 +2727,7 @@ "'true'">, InGroup; 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; def warn_this_bool_conversion : Warning< @@ -2743,8 +2743,9 @@ "equal to a null pointer is always %select{true|false}2">, InGroup; 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">, + "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; def warn_this_null_compare : Warning< "'this' pointer cannot be null in well-defined C++ code; comparison may be " Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -1180,8 +1180,7 @@ /// 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)) { @@ -7666,6 +7665,22 @@ } } + auto ComplainAboutNonnullParamOrCall = [&](bool IsParam) { + 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) << int(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(E->IgnoreParenImpCasts())) + if (auto *Callee = Call->getDirectCallee()) + if (Callee->hasAttr()) + return ComplainAboutNonnullParamOrCall(false); + // Expect to find a single Decl. Skip anything more complicated. ValueDecl *D = nullptr; if (DeclRefExpr *R = dyn_cast(E)) { @@ -7679,9 +7694,9 @@ return; // Check for parameter decl with nonnull attribute - if (const ParmVarDecl* PV = dyn_cast(D)) { + if (const auto *PV = dyn_cast(D)) { if (getCurFunction() && !getCurFunction()->ModifiedNonNullParams.count(PV)) - if (const FunctionDecl* FD = dyn_cast(PV->getDeclContext())) { + if (const auto *FD = dyn_cast(PV->getDeclContext())) { unsigned NumArgs = FD->getNumParams(); llvm::SmallBitVector AttrNonNull(NumArgs); for (const auto *NonNull : FD->specific_attrs()) { @@ -7695,21 +7710,12 @@ AttrNonNull.set(Val); } } - if (!AttrNonNull.empty()) - for (unsigned i = 0; i < NumArgs; ++i) - if (FD->getParamDecl(i) == PV && - (AttrNonNull[i] || PV->hasAttr())) { - 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; - return; - } + for (unsigned i = 0; i < NumArgs; ++i) + if (FD->getParamDecl(i) == PV && + (AttrNonNull[i] || PV->hasAttr())) + return ComplainAboutNonnullParamOrCall(true); } - } + } QualType T = D->getType(); const bool IsArray = T->isArrayType(); Index: test/Sema/nonnull.c =================================================================== --- test/Sema/nonnull.c +++ test/Sema/nonnull.c @@ -153,3 +153,17 @@ if (p) // No warning ; } + +__attribute__((returns_nonnull)) void *returns_nonnull_whee(); + +void returns_nonnull_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}} +}