From: Devin Coughlin Date: Tue, 26 Jan 2016 23:58:48 +0000 (+0000) Subject: [analyzer] Body farm: Look for property ivar in shadowing readwrite property. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=609444fdf26450fc530f2fcc72b2c1a743e03bfd;p=clang [analyzer] Body farm: Look for property ivar in shadowing readwrite property. After r251874, readonly properties that are shadowed by a readwrite property in a class extension no longer have an instance variable, which caused the body farm to not synthesize getters. Now, if a readonly property does not have an instance variable look for a shadowing property and try to get the instance variable from there. rdar://problem/24060091 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258886 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index 09904369ba..325c326137 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -383,10 +383,49 @@ Stmt *BodyFarm::getBody(const FunctionDecl *D) { return Val.getValue(); } +static const ObjCIvarDecl *findBackingIvar(const ObjCPropertyDecl *Prop) { + const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl(); + + if (IVar) + return IVar; + + // When a readonly property is shadowed in a class extensions with a + // a readwrite property, the instance variable belongs to the shadowing + // property rather than the shadowed property. If there is no instance + // variable on a readonly property, check to see whether the property is + // shadowed and if so try to get the instance variable from shadowing + // property. + if (!Prop->isReadOnly()) + return nullptr; + + auto *Container = cast(Prop->getDeclContext()); + const ObjCInterfaceDecl *PrimaryInterface = nullptr; + if (auto *InterfaceDecl = dyn_cast(Container)) { + PrimaryInterface = InterfaceDecl; + } else if (auto *CategoryDecl = dyn_cast(Container)) { + PrimaryInterface = CategoryDecl->getClassInterface(); + } else if (auto *ImplDecl = dyn_cast(Container)) { + PrimaryInterface = ImplDecl->getClassInterface(); + } else { + return nullptr; + } + + // FindPropertyVisibleInPrimaryClass() looks first in class extensions, so it + // is guaranteed to find the shadowing property, if it exists, rather than + // the shadowed property. + auto *ShadowingProp = PrimaryInterface->FindPropertyVisibleInPrimaryClass( + Prop->getIdentifier()); + if (ShadowingProp && ShadowingProp != Prop) { + IVar = ShadowingProp->getPropertyIvarDecl(); + } + + return IVar; +} + static Stmt *createObjCPropertyGetter(ASTContext &Ctx, const ObjCPropertyDecl *Prop) { // First, find the backing ivar. - const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl(); + const ObjCIvarDecl *IVar = findBackingIvar(Prop); if (!IVar) return nullptr; diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index bf9424c8c2..4fdbb69d87 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -211,6 +211,33 @@ void testConsistencyAssign(Person *p) { clang_analyzer_eval(p.friend == origFriend); // expected-warning{{UNKNOWN}} } +@interface ClassWithShadowedReadWriteProperty { + int _f; +} +@property (readonly) int someProp; +@end + +@interface ClassWithShadowedReadWriteProperty () +@property (readwrite) int someProp; +@end + +@implementation ClassWithShadowedReadWriteProperty +- (void)testSynthesisForShadowedReadWriteProperties; { + clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}} + + _f = 1; + + // Read of shadowed property should not invalidate receiver. + (void)self.someProp; + clang_analyzer_eval(_f == 1); // expected-warning{{TRUE}} + + _f = 2; + // Call to getter of shadowed property should not invalidate receiver. + (void)[self someProp]; + clang_analyzer_eval(_f == 2); // expected-warning{{TRUE}} +} +@end + #if !__has_feature(objc_arc) void testOverrelease(Person *p, int coin) { switch (coin) {