Skip to content

Commit cce7a4b

Browse files
AnnaZaksdevincoughlin
authored andcommitted
[analyzer] Support inlining of '[self classMethod]' and '[[self class] classMethod]'
Differential Revision: https://reviews.llvm.org/D28495 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@291867 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 7bb9c80d0673c9176d4e58b3989b3d2af4206c9b) rdar://problem/26963009 apple-llvm-split-commit: e5fa6efc21f79a00b4e54ba944375d3b3735bb0f apple-llvm-split-dir: clang/
1 parent 50005b0 commit cce7a4b

File tree

2 files changed

+136
-3
lines changed

2 files changed

+136
-3
lines changed

clang/lib/StaticAnalyzer/Core/CallEvent.cpp

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,38 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
896896
llvm_unreachable("The while loop should always terminate.");
897897
}
898898

899+
static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) {
900+
if (!MD)
901+
return MD;
902+
903+
// Find the redeclaration that defines the method.
904+
if (!MD->hasBody()) {
905+
for (auto I : MD->redecls())
906+
if (I->hasBody())
907+
MD = cast<ObjCMethodDecl>(I);
908+
}
909+
return MD;
910+
}
911+
912+
static bool isCallToSelfClass(const ObjCMessageExpr *ME) {
913+
const Expr* InstRec = ME->getInstanceReceiver();
914+
if (!InstRec)
915+
return false;
916+
const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts());
917+
918+
// Check that receiver is called 'self'.
919+
if (!InstRecIg || !InstRecIg->getFoundDecl() ||
920+
!InstRecIg->getFoundDecl()->getName().equals("self"))
921+
return false;
922+
923+
// Check that the method name is 'class'.
924+
if (ME->getSelector().getNumArgs() != 0 ||
925+
!ME->getSelector().getNameForSlot(0).equals("class"))
926+
return false;
927+
928+
return true;
929+
}
930+
899931
RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
900932
const ObjCMessageExpr *E = getOriginExpr();
901933
assert(E);
@@ -910,6 +942,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
910942
const MemRegion *Receiver = nullptr;
911943

912944
if (!SupersType.isNull()) {
945+
// The receiver is guaranteed to be 'super' in this case.
913946
// Super always means the type of immediate predecessor to the method
914947
// where the call occurs.
915948
ReceiverT = cast<ObjCObjectPointerType>(SupersType);
@@ -921,15 +954,40 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
921954
DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver);
922955
QualType DynType = DTI.getType();
923956
CanBeSubClassed = DTI.canBeASubClass();
924-
ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType);
957+
ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
925958

926959
if (ReceiverT && CanBeSubClassed)
927960
if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
928961
if (!canBeOverridenInSubclass(IDecl, Sel))
929962
CanBeSubClassed = false;
930963
}
931964

932-
// Lookup the method implementation.
965+
// Handle special cases of '[self classMethod]' and
966+
// '[[self class] classMethod]', which are treated by the compiler as
967+
// instance (not class) messages. We will statically dispatch to those.
968+
if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) {
969+
// For [self classMethod], return the compiler visible declaration.
970+
if (PT->getObjectType()->isObjCClass() &&
971+
Receiver == getSelfSVal().getAsRegion())
972+
return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
973+
974+
// Similarly, handle [[self class] classMethod].
975+
// TODO: We are currently doing a syntactic match for this pattern with is
976+
// limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m
977+
// shows. A better way would be to associate the meta type with the symbol
978+
// using the dynamic type info tracking and use it here. We can add a new
979+
// SVal for ObjC 'Class' values that know what interface declaration they
980+
// come from. Then 'self' in a class method would be filled in with
981+
// something meaningful in ObjCMethodCall::getReceiverSVal() and we could
982+
// do proper dynamic dispatch for class methods just like we do for
983+
// instance methods now.
984+
if (E->getInstanceReceiver())
985+
if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver()))
986+
if (isCallToSelfClass(M))
987+
return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
988+
}
989+
990+
// Lookup the instance method implementation.
933991
if (ReceiverT)
934992
if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
935993
// Repeatedly calling lookupPrivateMethod() is expensive, especially

clang/test/Analysis/inlining/InlineObjCClassMethod.m

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config ipa=dynamic-bifurcate -verify %s
22

33
void clang_analyzer_checkInlined(int);
4+
void clang_analyzer_eval(int);
45

56
// Test inlining of ObjC class methods.
67

@@ -194,7 +195,9 @@ + (int)foo;
194195
@implementation SelfUsedInParent
195196
+ (int)getNum {return 5;}
196197
+ (int)foo {
197-
return [self getNum];
198+
int r = [self getNum];
199+
clang_analyzer_eval(r == 5); // expected-warning{{TRUE}}
200+
return r;
198201
}
199202
@end
200203
@interface SelfUsedInParentChild : SelfUsedInParent
@@ -229,8 +232,80 @@ + (void)forwardDeclaredMethod {
229232
+ (void)forwardDeclaredVariadicMethod:(int)x, ... {
230233
clang_analyzer_checkInlined(0); // no-warning
231234
}
235+
@end
232236

237+
@interface SelfClassTestParent : NSObject
238+
-(unsigned)returns10;
239+
+(unsigned)returns20;
240+
+(unsigned)returns30;
233241
@end
234242

243+
@implementation SelfClassTestParent
244+
-(unsigned)returns10 { return 100; }
245+
+(unsigned)returns20 { return 100; }
246+
+(unsigned)returns30 { return 100; }
247+
@end
235248

249+
@interface SelfClassTest : SelfClassTestParent
250+
-(unsigned)returns10;
251+
+(unsigned)returns20;
252+
+(unsigned)returns30;
253+
@end
254+
255+
@implementation SelfClassTest
256+
-(unsigned)returns10 { return 10; }
257+
+(unsigned)returns20 { return 20; }
258+
+(unsigned)returns30 { return 30; }
259+
+(void)classMethod {
260+
unsigned result1 = [self returns20];
261+
clang_analyzer_eval(result1 == 20); // expected-warning{{TRUE}}
262+
unsigned result2 = [[self class] returns30];
263+
clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}}
264+
unsigned result3 = [[super class] returns30];
265+
clang_analyzer_eval(result3 == 100); // expected-warning{{UNKNOWN}}
266+
}
267+
-(void)instanceMethod {
268+
unsigned result0 = [self returns10];
269+
clang_analyzer_eval(result0 == 10); // expected-warning{{TRUE}}
270+
unsigned result2 = [[self class] returns30];
271+
clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}}
272+
unsigned result3 = [[super class] returns30];
273+
clang_analyzer_eval(result3 == 100); // expected-warning{{UNKNOWN}}
274+
}
275+
@end
236276

277+
@interface Parent : NSObject
278+
+ (int)a;
279+
+ (int)b;
280+
@end
281+
@interface Child : Parent
282+
@end
283+
@interface Other : NSObject
284+
+(void)run;
285+
@end
286+
int main(int argc, const char * argv[]) {
287+
@autoreleasepool {
288+
[Other run];
289+
}
290+
return 0;
291+
}
292+
@implementation Other
293+
+(void)run {
294+
int result = [Child a];
295+
// TODO: This should return 100.
296+
clang_analyzer_eval(result == 12); // expected-warning{{TRUE}}
297+
}
298+
@end
299+
@implementation Parent
300+
+ (int)a; {
301+
return [self b];
302+
}
303+
+ (int)b; {
304+
return 12;
305+
}
306+
@end
307+
@implementation Child
308+
+ (int)b; {
309+
return 100;
310+
}
311+
@end

0 commit comments

Comments
 (0)