george.burgess.iv created this revision.
george.burgess.iv added a reviewer: rtrieu.
george.burgess.iv added a subscriber: cfe-commits.

Addresses a problem brought up by Xavier here: 
http://permalink.gmane.org/gmane.comp.compilers.clang.devel/46163

tl;dr: We get a warning on the following code

```
void foo(void *p) __attribute__((nonnull(1))) {
        if (p == NULL) {} // warning: always equal to false
}
```

…But not this code:

```
void foo() __attribute__((returns_nonnull));
int main() {
        if (foo() == NULL) {} // no warning
}
```

This patch makes us give a warning in the second case.

http://reviews.llvm.org/D15324

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/Sema/SemaChecking.cpp
  test/Sema/nonnull.c

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}}
+}
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<CallExpr>(E->IgnoreParenImpCasts()))
+    if (auto *Callee = Call->getDirectCallee())
+      if (Callee->hasAttr<ReturnsNonNullAttr>())
+        return ComplainAboutNonnullParamOrCall(false);
+
   // Expect to find a single Decl.  Skip anything more complicated.
   ValueDecl *D = nullptr;
   if (DeclRefExpr *R = dyn_cast<DeclRefExpr>(E)) {
@@ -7679,9 +7694,9 @@
     return;
   
   // Check for parameter decl with nonnull attribute
-  if (const ParmVarDecl* PV = dyn_cast<ParmVarDecl>(D)) {
+  if (const auto *PV = dyn_cast<ParmVarDecl>(D)) {
     if (getCurFunction() && !getCurFunction()->ModifiedNonNullParams.count(PV))
-      if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(PV->getDeclContext())) {
+      if (const auto *FD = dyn_cast<FunctionDecl>(PV->getDeclContext())) {
         unsigned NumArgs = FD->getNumParams();
         llvm::SmallBitVector AttrNonNull(NumArgs);
         for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
@@ -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<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;
-              return;
-            }
+        for (unsigned i = 0; i < NumArgs; ++i)
+          if (FD->getParamDecl(i) == PV &&
+              (AttrNonNull[i] || PV->hasAttr<NonNullAttr>()))
+            return ComplainAboutNonnullParamOrCall(true);
       }
-    }
+  }
   
   QualType T = D->getType();
   const bool IsArray = T->isArrayType();
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2727,7 +2727,7 @@
     "'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<
@@ -2743,8 +2743,9 @@
     "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">,
+    "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 "
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to