[cfe-commits] r124161 - in /cfe/trunk: include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h include/clang/StaticAnalyzer/PathSensitive/SVals.h lib/StaticAnalyzer/BasicStore.cpp lib/StaticAnalyzer/CFRefCount.cpp lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp lib/StaticAnalyzer/Checkers/ExprEngine.cpp lib/StaticAnalyzer/RegionStore.cpp lib/StaticAnalyzer/SVals.cpp test/Analysis/properties.m

Argyrios Kyrtzidis akyrtzi at gmail.com
Mon Jan 24 16:04:03 PST 2011


Author: akirtzidis
Date: Mon Jan 24 18:04:03 2011
New Revision: 124161

URL: http://llvm.org/viewvc/llvm-project?rev=124161&view=rev
Log:
[analyzer] Handle the dot syntax for properties in the ExprEngine.

We translate property accesses to obj-c messages by simulating "loads" or "stores" to properties
using a pseudo-location SVal kind (ObjCPropRef).

Checkers can now reason about obj-c messages for both explicit message expressions and implicit
messages due to property accesses.

Added:
    cfe/trunk/test/Analysis/properties.m
Modified:
    cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h
    cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/SVals.h
    cfe/trunk/lib/StaticAnalyzer/BasicStore.cpp
    cfe/trunk/lib/StaticAnalyzer/CFRefCount.cpp
    cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
    cfe/trunk/lib/StaticAnalyzer/Checkers/ExprEngine.cpp
    cfe/trunk/lib/StaticAnalyzer/RegionStore.cpp
    cfe/trunk/lib/StaticAnalyzer/SVals.cpp

Modified: cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h?rev=124161&r1=124160&r2=124161&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h Mon Jan 24 18:04:03 2011
@@ -375,6 +375,9 @@
   void VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
                                    ExplodedNode *Pred, ExplodedNodeSet &Dst);
 
+  void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *E,
+                                ExplodedNode *Pred, ExplodedNodeSet &Dst);
+
   /// Transfer function logic for computing the lvalue of an Objective-C ivar.
   void VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr* DR, ExplodedNode* Pred,
                                 ExplodedNodeSet& Dst);

Modified: cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/SVals.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/SVals.h?rev=124161&r1=124160&r2=124161&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/SVals.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/PathSensitive/SVals.h Mon Jan 24 18:04:03 2011
@@ -427,7 +427,7 @@
 
 namespace loc {
 
-enum Kind { GotoLabelKind, MemRegionKind, ConcreteIntKind };
+enum Kind { GotoLabelKind, MemRegionKind, ConcreteIntKind, ObjCPropRefKind };
 
 class GotoLabel : public Loc {
 public:
@@ -505,6 +505,28 @@
   }
 };
 
+/// \brief Pseudo-location SVal used by the ExprEngine to simulate a "load" or
+/// "store" of an ObjC property for the dot syntax.
+class ObjCPropRef : public Loc {
+public:
+  explicit ObjCPropRef(const ObjCPropertyRefExpr *E)
+    : Loc(ObjCPropRefKind, E) {}
+
+  const ObjCPropertyRefExpr *getPropRefExpr() const {
+    return static_cast<const ObjCPropertyRefExpr *>(Data);
+  }
+
+  // Implement isa<T> support.
+  static inline bool classof(const SVal* V) {
+    return V->getBaseKind() == LocKind &&
+           V->getSubKind() == ObjCPropRefKind;
+  }
+
+  static inline bool classof(const Loc* V) {
+    return V->getSubKind() == ObjCPropRefKind;
+  }
+};
+
 } // end ento::loc namespace
 } // end GR namespace
 

Modified: cfe/trunk/lib/StaticAnalyzer/BasicStore.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/BasicStore.cpp?rev=124161&r1=124160&r2=124161&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/BasicStore.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/BasicStore.cpp Mon Jan 24 18:04:03 2011
@@ -196,6 +196,7 @@
       return V.isUnknownOrUndef() ? V : CastRetrievedVal(V, TR, T);
     }
 
+    case loc::ObjCPropRefKind:
     case loc::ConcreteIntKind:
       // Support direct accesses to memory.  It's up to individual checkers
       // to flag an error.

Modified: cfe/trunk/lib/StaticAnalyzer/CFRefCount.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/CFRefCount.cpp?rev=124161&r1=124160&r2=124161&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/CFRefCount.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/CFRefCount.cpp Mon Jan 24 18:04:03 2011
@@ -2033,9 +2033,10 @@
       else
         os << "function call";
     }
-    else {
-      assert (isa<ObjCMessageExpr>(S));
+    else if (isa<ObjCMessageExpr>(S)) {
       os << "Method";
+    } else {
+      os << "Property";
     }
 
     if (CurrV.getObjKind() == RetEffect::CF) {

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp?rev=124161&r1=124160&r2=124161&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp Mon Jan 24 18:04:03 2011
@@ -246,10 +246,11 @@
       return;
     }
 
+  const char *bugDesc = msg.isPropertySetter() ?
+                     "Argument for property setter is an uninitialized value"
+                   : "Argument in message expression is an uninitialized value";
   // Check for any arguments that are uninitialized/undefined.
-  PreVisitProcessArgs(C, CallOrObjCMessage(msg, state),
-                      "Argument in message expression "
-                      "is an uninitialized value", BT_msg_arg);
+  PreVisitProcessArgs(C, CallOrObjCMessage(msg, state), bugDesc, BT_msg_arg);
 }
 
 bool CallAndMessageChecker::evalNilReceiver(CheckerContext &C,

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/ExprEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/ExprEngine.cpp?rev=124161&r1=124160&r2=124161&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/ExprEngine.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/ExprEngine.cpp Mon Jan 24 18:04:03 2011
@@ -865,6 +865,10 @@
       VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S), Pred, Dst);
       break;
 
+    case Stmt::ObjCPropertyRefExprClass:
+      VisitObjCPropertyRefExpr(cast<ObjCPropertyRefExpr>(S), Pred, Dst);
+      break;
+
     // Cases not handled yet; but will handle some day.
     case Stmt::DesignatedInitExprClass:
     case Stmt::ExtVectorElementExprClass:
@@ -875,7 +879,6 @@
     case Stmt::ObjCAtTryStmtClass:
     case Stmt::ObjCEncodeExprClass:
     case Stmt::ObjCIsaExprClass:
-    case Stmt::ObjCPropertyRefExprClass:
     case Stmt::ObjCProtocolExprClass:
     case Stmt::ObjCSelectorExprClass:
     case Stmt::ObjCStringLiteralClass:
@@ -1799,6 +1802,17 @@
 
   assert(Builder && "StmtNodeBuilder must be defined.");
 
+  // Proceed with the store.  We use AssignE as the anchor for the PostStore
+  // ProgramPoint if it is non-NULL, and LocationE otherwise.
+  const Expr *StoreE = AssignE ? AssignE : LocationE;
+
+  if (isa<loc::ObjCPropRef>(location)) {
+    loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location);
+    ExplodedNodeSet src = Pred;
+    return VisitObjCMessage(ObjCPropertySetter(prop.getPropRefExpr(),
+                                               StoreE, Val), src, Dst);
+  }
+
   // Evaluate the location (checks for bad dereferences).
   ExplodedNodeSet Tmp;
   evalLocation(Tmp, LocationE, Pred, state, location, tag, false);
@@ -1811,10 +1825,6 @@
   SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind,
                                                    ProgramPoint::PostStoreKind);
 
-  // Proceed with the store.  We use AssignE as the anchor for the PostStore
-  // ProgramPoint if it is non-NULL, and LocationE otherwise.
-  const Expr *StoreE = AssignE ? AssignE : LocationE;
-
   for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI)
     evalBind(Dst, StoreE, *NI, GetState(*NI), location, Val);
 }
@@ -1825,6 +1835,13 @@
                             const void *tag, QualType LoadTy) {
   assert(!isa<NonLoc>(location) && "location cannot be a NonLoc.");
 
+  if (isa<loc::ObjCPropRef>(location)) {
+    loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location);
+    ExplodedNodeSet src = Pred;
+    return VisitObjCMessage(ObjCPropertyGetter(prop.getPropRefExpr(), Ex),
+                            src, Dst);
+  }
+
   // Are we loading from a region?  This actually results in two loads; one
   // to fetch the address of the referenced value and one to fetch the
   // referenced value.
@@ -2048,6 +2065,34 @@
 }
 
 //===----------------------------------------------------------------------===//
+// Transfer function: Objective-C dot-syntax to access a property.
+//===----------------------------------------------------------------------===//
+
+void ExprEngine::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Ex,
+                                          ExplodedNode *Pred,
+                                          ExplodedNodeSet &Dst) {
+
+  // Visit the base expression, which is needed for computing the lvalue
+  // of the ivar.
+  ExplodedNodeSet dstBase;
+  const Expr *baseExpr = Ex->getBase();
+  Visit(baseExpr, Pred, dstBase);
+
+  ExplodedNodeSet dstPropRef;
+
+  // Using the base, compute the lvalue of the instance variable.
+  for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end();
+       I!=E; ++I) {
+    ExplodedNode *nodeBase = *I;
+    const GRState *state = GetState(nodeBase);
+    SVal baseVal = state->getSVal(baseExpr);
+    MakeNode(dstPropRef, Ex, *I, state->BindExpr(Ex, loc::ObjCPropRef(Ex)));
+  }
+  
+  Dst.insert(dstPropRef);
+}
+
+//===----------------------------------------------------------------------===//
 // Transfer function: Objective-C ivar references.
 //===----------------------------------------------------------------------===//
 
@@ -2426,7 +2471,8 @@
   ExplodedNodeSet S2;
   CheckerVisit(CastE, S2, S1, PreVisitStmtCallback);
   
-  if (CastE->getCastKind() == CK_LValueToRValue) {
+  if (CastE->getCastKind() == CK_LValueToRValue ||
+      CastE->getCastKind() == CK_GetObjCProperty) {
     for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I!=E; ++I) {
       ExplodedNode *subExprNode = *I;
       const GRState *state = GetState(subExprNode);

Modified: cfe/trunk/lib/StaticAnalyzer/RegionStore.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/RegionStore.cpp?rev=124161&r1=124160&r2=124161&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/RegionStore.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/RegionStore.cpp Mon Jan 24 18:04:03 2011
@@ -985,6 +985,9 @@
   if (isa<loc::ConcreteInt>(L)) {
     return UnknownVal();
   }
+  if (!isa<loc::MemRegionVal>(L)) {
+    return UnknownVal();
+  }
 
   const MemRegion *MR = cast<loc::MemRegionVal>(L).getRegion();
 

Modified: cfe/trunk/lib/StaticAnalyzer/SVals.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/SVals.cpp?rev=124161&r1=124160&r2=124161&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/SVals.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/SVals.cpp Mon Jan 24 18:04:03 2011
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/StaticAnalyzer/PathSensitive/GRState.h"
+#include "clang/AST/ExprObjC.h"
 #include "clang/Basic/IdentifierTable.h"
 
 using namespace clang;
@@ -354,6 +355,22 @@
     case loc::MemRegionKind:
       os << '&' << cast<loc::MemRegionVal>(this)->getRegion()->getString();
       break;
+    case loc::ObjCPropRefKind: {
+      const ObjCPropertyRefExpr *E = cast<loc::ObjCPropRef>(this)->getPropRefExpr();
+      os << "objc-prop{";
+      if (E->isSuperReceiver())
+        os << "super.";
+      else if (E->getBase())
+        os << "<base>.";
+
+      if (E->isImplicitProperty())
+        os << E->getImplicitPropertyGetter()->getSelector().getAsString();
+      else
+        os << E->getExplicitProperty()->getName();
+
+      os << "}";
+      break;
+    }
     default:
       assert(false && "Pretty-printing not implemented for this Loc.");
       break;

Added: cfe/trunk/test/Analysis/properties.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/properties.m?rev=124161&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/properties.m (added)
+++ cfe/trunk/test/Analysis/properties.m Mon Jan 24 18:04:03 2011
@@ -0,0 +1,135 @@
+// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -verify %s
+
+typedef signed char BOOL;
+typedef unsigned int NSUInteger;
+typedef struct _NSZone NSZone;
+ at class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
+ at protocol NSObject  - (BOOL)isEqual:(id)object; @end
+ at protocol NSCopying  - (id)copyWithZone:(NSZone *)zone; @end
+ at protocol NSCoding  - (void)encodeWithCoder:(NSCoder *)aCoder; @end
+ at protocol NSMutableCopying  - (id)mutableCopyWithZone:(NSZone *)zone; @end
+ at interface NSObject <NSObject> {}
++(id)alloc;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+-(id)retain;
+ at end
+ at interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
+- (NSUInteger)length;
+-(id)initWithFormat:(NSString *)f,...;
+-(BOOL)isEqualToString:(NSString *)s;
++ (id)string;
+ at end
+ at interface NSNumber : NSObject {}
++(id)alloc;
+-(id)initWithInteger:(int)i;
+ at end
+
+// rdar://6946338
+
+ at interface Test1 : NSObject {
+  NSString *text;
+}
+-(id)myMethod;
+ at property (nonatomic, assign) NSString *text;
+ at end
+
+
+ at implementation Test1
+
+ at synthesize text;
+
+-(id)myMethod {
+  Test1 *cell = [[[Test1 alloc] init] autorelease];
+
+  NSString *string1 = [[NSString alloc] initWithFormat:@"test %f", 0.0]; // expected-warning {{Potential leak}}
+  cell.text = string1;
+
+  return cell;
+}
+
+ at end
+
+
+// rdar://8824416
+
+ at interface MyNumber : NSObject
+{
+  NSNumber* _myNumber;
+}
+
+- (id)initWithNumber:(NSNumber *)number;
+
+ at property (nonatomic, readonly) NSNumber* myNumber;
+ at property (nonatomic, readonly) NSNumber* newMyNumber;
+
+ at end
+
+ at implementation MyNumber
+ at synthesize myNumber=_myNumber;
+ 
+- (id)initWithNumber:(NSNumber *)number
+{
+  self = [super init];
+  
+  if ( self )
+  {
+    _myNumber = [number copy];
+  }
+  
+  return self;
+}
+
+- (NSNumber*)newMyNumber
+{
+  if ( _myNumber )
+    return [_myNumber retain];
+  
+  return [[NSNumber alloc] initWithInteger:1];
+}
+
+- (id)valueForUndefinedKey:(NSString*)key
+{
+  id value = 0;
+  
+  if ([key isEqualToString:@"MyIvarNumberAsPropertyOverReleased"])
+    value = self.myNumber; // _myNumber will be over released, since the value returned from self.myNumber is not retained.
+  else if ([key isEqualToString:@"MyIvarNumberAsPropertyOk"])
+    value = [self.myNumber retain]; // this line fixes the over release
+  else if ([key isEqualToString:@"MyIvarNumberAsNewMyNumber"])
+    value = self.newMyNumber; // this one is ok, since value is returned retained
+  else 
+    value = [[NSNumber alloc] initWithInteger:0];
+  
+  return [value autorelease]; // expected-warning {{Object sent -autorelease too many times}}
+}
+
+ at end
+
+NSNumber* numberFromMyNumberProperty(MyNumber* aMyNumber)
+{
+  NSNumber* result = aMyNumber.myNumber;
+    
+  return [result autorelease]; // expected-warning {{Object sent -autorelease too many times}}
+}
+
+
+// rdar://6611873
+
+ at interface Person : NSObject {
+  NSString *_name;
+}
+ at property (retain) NSString * name;
+ at end
+
+ at implementation Person
+ at synthesize name = _name;
+ at end
+
+void rdar6611873() {
+  Person *p = [[[Person alloc] init] autorelease];
+  
+  p.name = [[NSString string] retain]; // expected-warning {{leak}}
+  p.name = [[NSString alloc] init]; // expected-warning {{leak}}
+}





More information about the cfe-commits mailing list