[analyzer] Very simple ObjC instance method inlining
- Retrieves the type of the object/receiver from the state.
- Binds self during stack setup.
- Only explores the path on which the method is inlined (no
bifurcation to explore the path on which the method is not inlined).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160991 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index 97612d6..74ac253 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -695,8 +695,6 @@
virtual void getExtraInvalidatedRegions(RegionList &Regions) const;
virtual QualType getDeclaredResultType() const;
- ObjCMethodDecl *LookupClassMethodDefinition(Selector Sel,
- ObjCInterfaceDecl *ClassDecl) const;
public:
virtual const ObjCMessageExpr *getOriginExpr() const {
@@ -752,22 +750,7 @@
// TODO: We might want to only compute this once (or change the API for
// getting the parameters). Currently, this gets called 3 times during
// inlining.
- virtual const Decl *getRuntimeDefinition() const {
- const ObjCMessageExpr *E = getOriginExpr();
- assert(E);
-
- if (E->isInstanceMessage()) {
- return 0;
- } else {
- // This is a class method.
- // If we have type info for the receiver class, we are calling via
- // class name.
- if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface())
- return LookupClassMethodDefinition(E->getSelector(), IDecl);
- }
-
- return 0;
- }
+ virtual const Decl *getRuntimeDefinition() const;
virtual param_iterator param_begin(bool UseDefinitionParams = false) const;
virtual param_iterator param_end(bool UseDefinitionParams = false) const;
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index 0d1579f..4e92873 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -56,6 +56,18 @@
}
};
+/// \class Stores the dynamic type information.
+/// Information about type of an object at runtime. This is used by dynamic
+/// dispatch implementation.
+class DynamicTypeInfo {
+ QualType T;
+
+public:
+ DynamicTypeInfo() : T(QualType()) {}
+ DynamicTypeInfo(QualType WithType) : T(WithType) {}
+ QualType getType() {return T;}
+};
+
/// \class ProgramState
/// ProgramState - This class encapsulates:
///
@@ -313,6 +325,9 @@
bool isTainted(SymbolRef Sym, TaintTagType Kind = TaintTagGeneric) const;
bool isTainted(const MemRegion *Reg, TaintTagType Kind=TaintTagGeneric) const;
+ /// Get dynamic type information for a region.
+ DynamicTypeInfo getDynamicTypeInfo(const MemRegion *Reg) const;
+
//==---------------------------------------------------------------------==//
// Accessing the Generic Data Map (GDM).
//==---------------------------------------------------------------------==//
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index ab4b383..6a5d0f0 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -590,34 +590,34 @@
return static_cast<ObjCMessageKind>(Info.getInt());
}
-// TODO: This implementation is copied from SemaExprObjC.cpp, needs to be
-// factored into the ObjCInterfaceDecl.
-ObjCMethodDecl *ObjCMethodCall::LookupClassMethodDefinition(Selector Sel,
- ObjCInterfaceDecl *ClassDecl) const {
- ObjCMethodDecl *Method = 0;
- // Lookup in class and all superclasses.
- while (ClassDecl && !Method) {
- if (ObjCImplementationDecl *ImpDecl = ClassDecl->getImplementation())
- Method = ImpDecl->getClassMethod(Sel);
+const Decl *ObjCMethodCall::getRuntimeDefinition() const {
+ const ObjCMessageExpr *E = getOriginExpr();
+ Selector Sel = E->getSelector();
+ assert(E);
- // Look through local category implementations associated with the class.
- if (!Method)
- Method = ClassDecl->getCategoryClassMethod(Sel);
-
- // Before we give up, check if the selector is an instance method.
- // But only in the root. This matches gcc's behavior and what the
- // runtime expects.
- if (!Method && !ClassDecl->getSuperClass()) {
- Method = ClassDecl->lookupInstanceMethod(Sel);
- // Look through local category implementations associated
- // with the root class.
- //if (!Method)
- // Method = LookupPrivateInstanceMethod(Sel, ClassDecl);
+ if (E->isInstanceMessage()) {
+ const MemRegion *Receiver = getReceiverSVal().getAsRegion();
+ DynamicTypeInfo TI = getState()->getDynamicTypeInfo(Receiver);
+ const ObjCObjectPointerType *T =
+ dyn_cast<ObjCObjectPointerType>(TI.getType().getTypePtr());
+ if (!T)
+ return 0;
+ if (ObjCInterfaceDecl *IDecl = T->getInterfaceDecl()) {
+ // Find the method implementation.
+ return IDecl->lookupPrivateMethod(Sel);
}
- ClassDecl = ClassDecl->getSuperClass();
+ } else {
+ // This is a class method.
+ // If we have type info for the receiver class, we are calling via
+ // class name.
+ if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) {
+ // Find/Return the method implementation.
+ return IDecl->lookupPrivateClassMethod(Sel);
+ }
}
- return Method;
+
+ return 0;
}
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index 6adc18c..20f1e22 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -731,3 +731,12 @@
return Tainted;
}
+
+DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const {
+ if (const TypedRegion *TR = dyn_cast<TypedRegion>(Reg))
+ return DynamicTypeInfo(TR->getLocationType());
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg))
+ return DynamicTypeInfo(SR->getSymbol()
+ ->getType(getStateManager().getContext()));
+ return DynamicTypeInfo();
+}
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index 98d815f..ed221c5 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -53,6 +53,13 @@
Store = Bind(Store.getStore(), ThisRegion, ThisVal);
}
+ if (const ObjCMethodCall *MCall = dyn_cast<ObjCMethodCall>(&Call)) {
+ SVal SelfVal = MCall->getReceiverSVal();
+ const VarDecl *SelfDecl = LCtx->getAnalysisDeclContext()->getSelfDecl();
+ Store = Bind(Store.getStore(),
+ svalBuilder.makeLoc(MRMgr.getVarRegion(SelfDecl, LCtx)),
+ SelfVal);
+ }
return Store;
}
diff --git a/test/Analysis/inlining/InlineObjCInstanceMethod.h b/test/Analysis/inlining/InlineObjCInstanceMethod.h
new file mode 100644
index 0000000..18131c8
--- /dev/null
+++ b/test/Analysis/inlining/InlineObjCInstanceMethod.h
@@ -0,0 +1,19 @@
+
+// Define a public header for the ObjC methods that are "visible" externally
+// and, thus, could be sub-classed. We should explore the path on which these
+// are sub-classed with unknown class by not inlining them.
+
+typedef signed char BOOL;
+typedef struct objc_class *Class;
+typedef struct objc_object {
+ Class isa;
+} *id;
+@protocol NSObject - (BOOL)isEqual:(id)object; @end
+@interface NSObject <NSObject> {}
++(id)alloc;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+- (Class)class;
+-(id)retain;
+@end
diff --git a/test/Analysis/inlining/InlineObjCInstanceMethod.m b/test/Analysis/inlining/InlineObjCInstanceMethod.m
new file mode 100644
index 0000000..682d02a
--- /dev/null
+++ b/test/Analysis/inlining/InlineObjCInstanceMethod.m
@@ -0,0 +1,80 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=dynamic -verify %s
+
+#include "InlineObjCInstanceMethod.h"
+
+// Method is defined in the parent; called through self.
+@interface MyParent : NSObject
+- (int)getInt;
+@end
+@implementation MyParent
+- (int)getInt {
+ return 0;
+}
+@end
+
+@interface MyClass : MyParent
+@end
+@implementation MyClass
+- (int)testDynDispatchSelf {
+ int y = [self getInt];
+ return 5/y; // expected-warning {{Division by zero}}
+}
+
+// Method is called on inited object.
++ (int)testAllocInit {
+ MyClass *a = [[self alloc] init];
+ return 5/[a getInt]; // todo
+}
+
+// Method is called on inited object.
++ (int)testAllocInit2 {
+ MyClass *a = [[MyClass alloc] init];
+ return 5/[a getInt]; // todo
+}
+
+// Method is called on a parameter.
++ (int)testParam: (MyClass*) a {
+ return 5/[a getInt]; // expected-warning {{Division by zero}}
+}
+
+// Method is called on a parameter of unnown type.
++ (int)testParamUnknownType: (id) a {
+ return 5/[a getInt]; // no warning
+}
+
+@end
+
+// TODO: When method is inlined, the attribute reset should be visible.
+@interface TestSettingAnAttributeInCallee : NSObject {
+ int _attribute;
+}
+ - (void) method2;
+@end
+
+@implementation TestSettingAnAttributeInCallee
+- (int) method1 {
+ [self method2];
+ return 5/_attribute; // expected-warning {{Division by zero}}
+}
+
+- (void) method2 {
+ _attribute = 0;
+}
+@end
+
+@interface TestSettingAnAttributeInCaller : NSObject {
+ int _attribute;
+}
+ - (int) method2;
+@end
+
+@implementation TestSettingAnAttributeInCaller
+- (void) method1 {
+ _attribute = 0;
+ [self method2];
+}
+
+- (int) method2 {
+ return 5/_attribute; // expected-warning {{Division by zero}}
+}
+@end
\ No newline at end of file