[analyzer] Fix Objective-C accessor body farms after 2073dd2d.

Fix a canonicalization problem for the newly added property accessor stubs that
was causing a wrong decl to be used for 'self' in the accessor's body farm.

Fix a crash when constructing a body farm for accessors of a property
that is declared and @synthesize'd in different (but related) interfaces.

Differential Revision: https://reviews.llvm.org/D70158
This commit is contained in:
Artem Dergachev 2019-11-21 18:13:48 -08:00
parent bbc8662db1
commit 0b58b80edb
5 changed files with 182 additions and 67 deletions

View File

@ -737,50 +737,65 @@ static const ObjCIvarDecl *findBackingIvar(const ObjCPropertyDecl *Prop) {
}
static Stmt *createObjCPropertyGetter(ASTContext &Ctx,
const ObjCPropertyDecl *Prop) {
// First, find the backing ivar.
const ObjCIvarDecl *IVar = findBackingIvar(Prop);
if (!IVar)
return nullptr;
const ObjCMethodDecl *MD) {
// First, find the backing ivar.
const ObjCIvarDecl *IVar = nullptr;
// Ignore weak variables, which have special behavior.
if (Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_weak)
return nullptr;
// Look to see if Sema has synthesized a body for us. This happens in
// Objective-C++ because the return value may be a C++ class type with a
// non-trivial copy constructor. We can only do this if we can find the
// @synthesize for this property, though (or if we know it's been auto-
// synthesized).
const ObjCImplementationDecl *ImplDecl =
IVar->getContainingInterface()->getImplementation();
if (ImplDecl) {
for (const auto *I : ImplDecl->property_impls()) {
if (I->getPropertyDecl() != Prop)
continue;
if (I->getGetterCXXConstructor()) {
ASTMaker M(Ctx);
return M.makeReturn(I->getGetterCXXConstructor());
}
// Property accessor stubs sometimes do not correspond to any property.
if (MD->isSynthesizedAccessorStub()) {
const ObjCInterfaceDecl *IntD = MD->getClassInterface();
const ObjCImplementationDecl *ImpD = IntD->getImplementation();
for (const auto *V: ImpD->ivars()) {
if (V->getName() == MD->getSelector().getNameForSlot(0))
IVar = V;
}
}
// Sanity check that the property is the same type as the ivar, or a
// reference to it, and that it is either an object pointer or trivially
// copyable.
if (!Ctx.hasSameUnqualifiedType(IVar->getType(),
Prop->getType().getNonReferenceType()))
return nullptr;
if (!IVar->getType()->isObjCLifetimeType() &&
!IVar->getType().isTriviallyCopyableType(Ctx))
return nullptr;
if (!IVar) {
const ObjCPropertyDecl *Prop = MD->findPropertyDecl();
IVar = findBackingIvar(Prop);
if (!IVar)
return nullptr;
// Ignore weak variables, which have special behavior.
if (Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_weak)
return nullptr;
// Look to see if Sema has synthesized a body for us. This happens in
// Objective-C++ because the return value may be a C++ class type with a
// non-trivial copy constructor. We can only do this if we can find the
// @synthesize for this property, though (or if we know it's been auto-
// synthesized).
const ObjCImplementationDecl *ImplDecl =
IVar->getContainingInterface()->getImplementation();
if (ImplDecl) {
for (const auto *I : ImplDecl->property_impls()) {
if (I->getPropertyDecl() != Prop)
continue;
if (I->getGetterCXXConstructor()) {
ASTMaker M(Ctx);
return M.makeReturn(I->getGetterCXXConstructor());
}
}
}
// Sanity check that the property is the same type as the ivar, or a
// reference to it, and that it is either an object pointer or trivially
// copyable.
if (!Ctx.hasSameUnqualifiedType(IVar->getType(),
Prop->getType().getNonReferenceType()))
return nullptr;
if (!IVar->getType()->isObjCLifetimeType() &&
!IVar->getType().isTriviallyCopyableType(Ctx))
return nullptr;
}
// Generate our body:
// return self->_ivar;
ASTMaker M(Ctx);
const VarDecl *selfVar = Prop->getGetterMethodDecl()->getSelfDecl();
const VarDecl *selfVar = MD->getSelfDecl();
if (!selfVar)
return nullptr;
@ -791,7 +806,7 @@ static Stmt *createObjCPropertyGetter(ASTContext &Ctx,
selfVar->getType()),
IVar);
if (!Prop->getType()->isReferenceType())
if (!MD->getReturnType()->isReferenceType())
loadedIVar = M.makeLvalueToRvalue(loadedIVar, IVar->getType());
return M.makeReturn(loadedIVar);
@ -814,10 +829,6 @@ Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) {
return Val.getValue();
Val = nullptr;
const ObjCPropertyDecl *Prop = D->findPropertyDecl();
if (!Prop)
return nullptr;
// For now, we only synthesize getters.
// Synthesizing setters would cause false negatives in the
// RetainCountChecker because the method body would bind the parameter
@ -840,7 +851,7 @@ Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) {
return nullptr;
}
Val = createObjCPropertyGetter(C, Prop);
Val = createObjCPropertyGetter(C, D);
return Val.getValue();
}

View File

@ -1309,6 +1309,8 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
}
const ObjCMethodDecl *MD = Val.getValue();
if (MD && !MD->hasBody())
MD = MD->getCanonicalDecl();
if (CanBeSubClassed)
return RuntimeDefinition(MD, Receiver);
else

View File

@ -16,12 +16,46 @@
<key>start</key>
<array>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>31</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>31</integer>
<key>col</key><integer>33</integer>
<key>file</key><integer>0</integer>
</dict>
</array>
<key>end</key>
<array>
<dict>
<key>line</key><integer>33</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>33</integer>
<key>col</key><integer>10</integer>
<key>file</key><integer>0</integer>
</dict>
</array>
</dict>
</array>
</dict>
<dict>
<key>kind</key><string>control</string>
<key>edges</key>
<array>
<dict>
<key>start</key>
<array>
<dict>
<key>line</key><integer>33</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>33</integer>
<key>col</key><integer>10</integer>
<key>file</key><integer>0</integer>
</dict>
@ -29,12 +63,12 @@
<key>end</key>
<array>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>22</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>22</integer>
<key>file</key><integer>0</integer>
</dict>
@ -46,7 +80,7 @@
<key>kind</key><string>event</string>
<key>location</key>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>22</integer>
<key>file</key><integer>0</integer>
</dict>
@ -54,12 +88,12 @@
<array>
<array>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>22</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>22</integer>
<key>file</key><integer>0</integer>
</dict>
@ -79,12 +113,12 @@
<key>start</key>
<array>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>22</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>22</integer>
<key>file</key><integer>0</integer>
</dict>
@ -92,12 +126,12 @@
<key>end</key>
<array>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>10</integer>
<key>file</key><integer>0</integer>
</dict>
@ -113,12 +147,12 @@
<key>start</key>
<array>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>16</integer>
<key>line</key><integer>33</integer>
<key>col</key><integer>10</integer>
<key>file</key><integer>0</integer>
</dict>
@ -126,12 +160,12 @@
<key>end</key>
<array>
<dict>
<key>line</key><integer>17</integer>
<key>line</key><integer>36</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>17</integer>
<key>line</key><integer>36</integer>
<key>col</key><integer>14</integer>
<key>file</key><integer>0</integer>
</dict>
@ -143,7 +177,7 @@
<key>kind</key><string>event</string>
<key>location</key>
<dict>
<key>line</key><integer>17</integer>
<key>line</key><integer>36</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
@ -151,12 +185,12 @@
<array>
<array>
<dict>
<key>line</key><integer>17</integer>
<key>line</key><integer>36</integer>
<key>col</key><integer>16</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>17</integer>
<key>line</key><integer>36</integer>
<key>col</key><integer>16</integer>
<key>file</key><integer>0</integer>
</dict>
@ -177,10 +211,10 @@
<key>issue_hash_content_of_line_in_context</key><string>ff735bea0eb12d4d172b139143c32365</string>
<key>issue_context_kind</key><string>Objective-C method</string>
<key>issue_context</key><string>method</string>
<key>issue_hash_function_offset</key><string>3</string>
<key>issue_hash_function_offset</key><string>6</string>
<key>location</key>
<dict>
<key>line</key><integer>17</integer>
<key>line</key><integer>36</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
@ -188,9 +222,11 @@
<dict>
<key>0</key>
<array>
<integer>14</integer>
<integer>16</integer>
<integer>17</integer>
<integer>26</integer>
<integer>30</integer>
<integer>31</integer>
<integer>33</integer>
<integer>36</integer>
</array>
</dict>
</dict>

View File

@ -1,6 +1,22 @@
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull,nullability.NullablePassedToNonnull,nullability.NullableReturnedFromNonnull,nullability.NullableDereferenced -analyzer-output=text -verify %s
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull,nullability.NullablePassedToNonnull,nullability.NullableReturnedFromNonnull,nullability.NullableDereferenced -analyzer-output=plist -o %t.plist %s
// RUN: %normalize_plist <%t.plist | diff -ub %S/Inputs/expected-plists/nullability-notes.m.plist -
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core \
// RUN: -analyzer-checker=nullability.NullPassedToNonnull \
// RUN: -analyzer-checker=nullability.NullReturnedFromNonnull \
// RUN: -analyzer-checker=nullability.NullablePassedToNonnull \
// RUN: -analyzer-checker=nullability.NullableReturnedFromNonnull \
// RUN: -analyzer-checker=nullability.NullableDereferenced \
// RUN: -analyzer-checker=debug.ExprInspection \
// RUN: -analyzer-output=text -verify %s
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core \
// RUN: -analyzer-checker=nullability.NullPassedToNonnull \
// RUN: -analyzer-checker=nullability.NullReturnedFromNonnull \
// RUN: -analyzer-checker=nullability.NullablePassedToNonnull \
// RUN: -analyzer-checker=nullability.NullableReturnedFromNonnull \
// RUN: -analyzer-checker=nullability.NullableDereferenced \
// RUN: -analyzer-output=plist -o %t.plist %s
// RUN: %normalize_plist <%t.plist \
// RUN: | diff -ub %S/Inputs/expected-plists/nullability-notes.m.plist -
void clang_analyzer_warnOnDeadSymbol(id);
#include "Inputs/system-header-simulator-for-nullability.h"
@ -12,8 +28,11 @@ void takesNonnull(NSObject *_Nonnull y);
@end;
@implementation ClassWithProperties
-(void) method {
clang_analyzer_warnOnDeadSymbol(self);
// no-crash
NSObject *x = self.x; // expected-note{{Nullability 'nullable' is inferred}}
// expected-warning@-1{{SYMBOL DEAD}}
// expected-note@-2 {{SYMBOL DEAD}}
takesNonnull(x); // expected-warning{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
// expected-note@-1{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}}
}

View File

@ -3,6 +3,8 @@
void clang_analyzer_eval(int);
#define nil ((id)0)
typedef const void * CFTypeRef;
extern CFTypeRef CFRetain(CFTypeRef cf);
void CFRelease(CFTypeRef cf);
@ -1040,3 +1042,48 @@ void testNoCrashWhenAccessPropertyAndThereAreNoDirectBindingsAtAll() {
clang_analyzer_eval(self.still_no_custom_accessor == self.still_no_custom_accessor); // expected-warning{{TRUE}}
}
@end
@interface Shadowed
@property (assign) NSObject *o;
- (NSObject *)getShadowedIvar;
- (void)clearShadowedIvar;
- (NSObject *)getShadowedProp;
- (void)clearShadowedProp;
@end
@implementation Shadowed
- (NSObject *)getShadowedIvar {
return self->_o;
}
- (void)clearShadowedIvar {
self->_o = nil;
}
- (NSObject *)getShadowedProp {
return self.o;
}
- (void)clearShadowedProp {
self.o = nil;
}
@end
@interface Shadowing : Shadowed
@end
@implementation Shadowing
// Property 'o' is declared in the superclass but synthesized here.
// This creates a separate ivar that shadows the superclass's ivar,
// but the old ivar is still accessible from the methods of the superclass.
// The old property, however, is not accessible with the property syntax
// even from the superclass methods.
@synthesize o;
-(void)testPropertyShadowing {
NSObject *oo = self.o;
clang_analyzer_eval(self.o == oo); // expected-warning{{TRUE}}
clang_analyzer_eval([self getShadowedIvar] == oo); // expected-warning{{UNKNOWN}}
[self clearShadowedIvar];
clang_analyzer_eval(self.o == oo); // expected-warning{{TRUE}}
clang_analyzer_eval([self getShadowedIvar] == oo); // expected-warning{{UNKNOWN}}
clang_analyzer_eval([self getShadowedIvar] == nil); // expected-warning{{TRUE}}
}
@end