[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