]> granicus.if.org Git - clang/commitdiff
Migrate the path-sensitive checking of 'nonnull' arguments over to the new
authorTed Kremenek <kremenek@apple.com>
Wed, 22 Jul 2009 21:46:56 +0000 (21:46 +0000)
committerTed Kremenek <kremenek@apple.com>
Wed, 22 Jul 2009 21:46:56 +0000 (21:46 +0000)
'Checker' interface. An updated test case illustrates that after calling a
function with the 'nonnull' attribute we now register the fact that the passed
pointer must be non-null. This retention of information was not possible with
the previously used GRSimpleAPICheck interface.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@76797 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Analysis/GRExprEngineInternalChecks.cpp
test/Analysis/null-deref-ps.c

index aff58df42d646a5874a765b54bbb1a19dc1453c7..c84b6c001b1ea0d1eed2dc5c3eae3c4c4b1a68a9 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "clang/Analysis/PathSensitive/BugReporter.h"
 #include "clang/Analysis/PathSensitive/GRExprEngine.h"
+#include "clang/Analysis/PathSensitive/CheckerVisitor.h"
 #include "clang/Analysis/PathDiagnostic.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/Support/Compiler.h"
@@ -536,54 +537,85 @@ public:
 //===----------------------------------------------------------------------===//
 // __attribute__(nonnull) checking
 
-class VISIBILITY_HIDDEN CheckAttrNonNull : public GRSimpleAPICheck {
+class VISIBILITY_HIDDEN CheckAttrNonNull :
+    public CheckerVisitor<CheckAttrNonNull> {
+
   BugType *BT;
-  BugReporter &BR;
   
 public:
-  CheckAttrNonNull(BugReporter &br) : BT(0), BR(br) {}
+  CheckAttrNonNull() : BT(0) {}
+
+  const void *getTag() {
+    static int x = 0;
+    return &x;
+  }
 
-  virtual bool Audit(ExplodedNode<GRState>* N, GRStateManager& VMgr) {
-    CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
-    const GRState* state = N->getState();
+  void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) {
+    const GRState *state = C.getState();
+    const GRState *originalState = state;
     
+    // Check if the callee has a 'nonnull' attribute.
     SVal X = state->getSVal(CE->getCallee());
-
+    
     const FunctionDecl* FD = X.getAsFunctionDecl();
     if (!FD)
-      return false;
+      return;
 
-    const NonNullAttr* Att = FD->getAttr<NonNullAttr>();
-    
+    const NonNullAttr* Att = FD->getAttr<NonNullAttr>();    
     if (!Att)
-      return false;
+      return;
     
     // Iterate through the arguments of CE and check them for null.
     unsigned idx = 0;
-    bool hasError = false;
     
-    for (CallExpr::arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E;
+    for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E;
          ++I, ++idx) {
-      
-      if (!VMgr.isEqual(state, *I, 0) || !Att->isNonNull(idx))
+    
+      if (!Att->isNonNull(idx))
         continue;
-
-      // Lazily allocate the BugType object if it hasn't already been created.
-      // Ownership is transferred to the BugReporter object once the BugReport
-      // is passed to 'EmitWarning'.
-      if (!BT) BT =
-        new BugType("Argument with 'nonnull' attribute passed null", "API");
       
-      RangedBugReport *R = new RangedBugReport(*BT,
-                                   "Null pointer passed as an argument to a "
-                                   "'nonnull' parameter", N);
-
-      R->addRange((*I)->getSourceRange());
-      BR.EmitReport(R);
-      hasError = true;
+      ConstraintManager &CM = C.getConstraintManager();
+      const GRState *stateNotNull, *stateNull;
+      llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state,
+                                                         state->getSVal(*I));
+      
+      if (stateNull && !stateNotNull) {
+        // Generate an error node.  Check for a null node in case
+        // we cache out.
+        if (ExplodedNode<GRState> *errorNode = C.generateNode(CE, stateNull)) {
+                  
+          // Lazily allocate the BugType object if it hasn't already been
+          // created. Ownership is transferred to the BugReporter object once
+          // the BugReport is passed to 'EmitWarning'.
+          if (!BT)
+            BT = new BugType("Argument with 'nonnull' attribute passed null",
+                             "API");
+          
+          RangedBugReport *R =
+            new RangedBugReport(*BT, "Null pointer passed as an argument to a "
+                                "'nonnull' parameter", errorNode);
+          
+          // Highlight the range of the argument that was null.
+          R->addRange((*I)->getSourceRange());
+          
+          // Emit the bug report.
+          C.EmitReport(R);
+        }
+        
+        // Always return.  Either we cached out or we just emitted an error.
+        return;
+      }
+      
+      // If a pointer value passed the check we should assume that it is
+      // indeed not null from this point forward.
+      assert(stateNotNull);
+      state = stateNotNull;
     }
-    
-    return hasError;
+   
+    // If we reach here all of the arguments passed the nonnull check.
+    // If 'state' has been updated generated a new node.
+    if (state != originalState)
+      C.generateNode(CE, state);
   }
 };
 } // end anonymous namespace
@@ -619,5 +651,5 @@ void GRExprEngine::RegisterInternalChecks() {
   // their associated BugType will get registered with the BugReporter
   // automatically.  Note that the check itself is owned by the GRExprEngine
   // object.
-  AddCheck(new CheckAttrNonNull(BR), Stmt::CallExprClass);
+  registerCheck(new CheckAttrNonNull());
 }
index a31bcecf971021cd1db93d55324e7009b32d029c..f2994f55da6a7a9c76d60ab9f87df828f9252001 100644 (file)
@@ -105,6 +105,15 @@ int f6c(int *p, int *q) {
              : bar3(p, 2, q); // no-warning
 }
 
+int f6d(int *p) {
+  bar(p, 0);
+  // At this point, 'p' cannot be null.
+  if (!p) {
+    int *q = 0;
+    *q = 0xDEADBEEF; // no-warning    
+  }  
+}
+
 int* qux();
 
 int f7(int x) {