}
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;
+
+ // 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;
+ }
+ }
- // Ignore weak variables, which have special behavior.
- if (Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_weak)
- 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());
+ // 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;
+ // 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;
selfVar->getType()),
IVar);
- if (!Prop->getType()->isReferenceType())
+ if (!MD->getReturnType()->isReferenceType())
loadedIVar = M.makeLvalueToRvalue(loadedIVar, IVar->getType());
return M.makeReturn(loadedIVar);
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
return nullptr;
}
- Val = createObjCPropertyGetter(C, Prop);
+ Val = createObjCPropertyGetter(C, D);
return Val.getValue();
}
}
const ObjCMethodDecl *MD = Val.getValue();
+ if (MD && !MD->hasBody())
+ MD = MD->getCanonicalDecl();
if (CanBeSubClassed)
return RuntimeDefinition(MD, Receiver);
else
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
-// 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"
@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}}
}
void clang_analyzer_eval(int);
+#define nil ((id)0)
+
typedef const void * CFTypeRef;
extern CFTypeRef CFRetain(CFTypeRef cf);
void CFRelease(CFTypeRef cf);
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