llvm-project/clang/test/Analysis/objc_invalidation.m
Artem Dergachev 017675fff1
[attributes][analyzer] Generalize [[clang::suppress]] to declarations. (#80371)
The attribute is now allowed on an assortment of declarations, to
suppress warnings related to declarations themselves, or all warnings in
the lexical scope of the declaration.

I don't necessarily see a reason to have a list at all, but it does look
as if some of those more niche items aren't properly supported by the
compiler itself so let's maintain a short safe list for now.

The initial implementation raised a question whether the attribute
should apply to lexical declaration context vs. "actual" declaration
context. I'm using "lexical" here because it results in less warnings
suppressed, which is the conservative behavior: we can always expand it
later if we think this is wrong, without breaking any existing code. I
also think that this is the correct behavior that we will probably never
want to change, given that the user typically desires to keep the
suppressions as localized as possible.
2024-02-13 14:57:55 -08:00

391 lines
12 KiB
Objective-C

// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.osx.cocoa.InstanceVariableInvalidation -DRUN_IVAR_INVALIDATION -verify %s
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.osx.cocoa.MissingInvalidationMethod -DRUN_MISSING_INVALIDATION_METHOD -verify %s
extern void __assert_fail (__const char *__assertion, __const char *__file,
unsigned int __line, __const char *__function)
__attribute__ ((__noreturn__));
#define assert(expr) \
((expr) ? (void)(0) : __assert_fail (#expr, __FILE__, __LINE__, __func__))
@protocol NSObject
@end
@interface NSObject <NSObject> {}
+(id)alloc;
+(id)new;
-(id)init;
-(id)autorelease;
-(id)copy;
- (Class)class;
-(id)retain;
-(id)description;
@end
@class NSString;
extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2)));
@protocol Invalidation1 <NSObject>
- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
@end
@protocol Invalidation2 <NSObject>
- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
@end
@protocol Invalidation3 <NSObject>
- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
- (void) invalidate2 __attribute__((annotate("objc_instance_variable_invalidator")));
@end
@protocol Invalidation3;
@protocol Invalidation2;
@interface Invalidation2Class <Invalidation2>
@end
@interface Invalidation1Class <Invalidation1>
@end
@interface ClassWithInvalidationMethodInCategory <NSObject>
@end
@interface ClassWithInvalidationMethodInCategory ()
- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
@end
@interface SomeInvalidationImplementingObject: NSObject <Invalidation3, Invalidation2> {
SomeInvalidationImplementingObject *ObjA; // invalidation in the parent
}
@end
@implementation SomeInvalidationImplementingObject
- (void)invalidate{
ObjA = 0;
}
- (void)invalidate2 {
[self invalidate];
}
@end
@interface SomeSubclassInvalidatableObject : SomeInvalidationImplementingObject {
SomeInvalidationImplementingObject *Ivar1; // regular ivar
SomeInvalidationImplementingObject *Ivar2; // regular ivar, sending invalidate message
SomeInvalidationImplementingObject *_Ivar3; // no property, call -description
SomeInvalidationImplementingObject *_Ivar4; // no property, provide as argument to NSLog()
SomeInvalidationImplementingObject *_Prop1; // partially implemented property, set to 0 with dot syntax
SomeInvalidationImplementingObject *_Prop2; // fully implemented prop, set to 0 with dot syntax
SomeInvalidationImplementingObject *_propIvar; // property with custom named ivar, set to 0 via setter
Invalidation1Class *MultipleProtocols; // regular ivar belonging to a different class
Invalidation2Class *MultInheritance; // regular ivar belonging to a different class
SomeInvalidationImplementingObject *_Prop3; // property, invalidate via sending a message to a getter method
SomeInvalidationImplementingObject *_Prop4; // property with @synthesize, invalidate via property
SomeInvalidationImplementingObject *_Prop5; // property with @synthesize, invalidate via getter method
SomeInvalidationImplementingObject *_Prop8;
// Ivars invalidated by the partial invalidator.
SomeInvalidationImplementingObject *Ivar9;
SomeInvalidationImplementingObject *_Prop10;
SomeInvalidationImplementingObject *Ivar11;
// No warnings on these as they are not invalidatable.
NSObject *NIvar1;
NSObject *NObj2;
NSObject *_NProp1;
NSObject *_NpropIvar;
}
@property (assign) SomeInvalidationImplementingObject* Prop0;
@property (nonatomic, assign) SomeInvalidationImplementingObject* Prop1;
@property (assign) SomeInvalidationImplementingObject* Prop2;
@property (assign) SomeInvalidationImplementingObject* Prop3;
@property (assign) SomeInvalidationImplementingObject *Prop5;
@property (assign) SomeInvalidationImplementingObject *Prop4;
@property (assign) SomeInvalidationImplementingObject* Prop6; // automatically synthesized prop
@property (assign) SomeInvalidationImplementingObject* Prop7; // automatically synthesized prop
@property (assign) SomeInvalidationImplementingObject *SynthIvarProp;
@property (assign) NSObject* NProp0;
@property (nonatomic, assign) NSObject* NProp1;
@property (assign) NSObject* NProp2;
-(void)setProp1: (SomeInvalidationImplementingObject*) InO;
-(void)setNProp1: (NSObject*) InO;
-(void)invalidate;
// Partial invalidators invalidate only some ivars. They are guaranteed to be
// called before the invalidation methods.
-(void)partialInvalidator1 __attribute__((annotate("objc_instance_variable_invalidator_partial")));
-(void)partialInvalidator2 __attribute__((annotate("objc_instance_variable_invalidator_partial")));
@end
@interface SomeSubclassInvalidatableObject()
@property (assign) SomeInvalidationImplementingObject* Prop8;
@property (assign) SomeInvalidationImplementingObject* Prop10;
@end
@implementation SomeSubclassInvalidatableObject{
@private
SomeInvalidationImplementingObject *Ivar5;
ClassWithInvalidationMethodInCategory *Ivar13;
}
@synthesize Prop7 = _propIvar;
@synthesize Prop3 = _Prop3;
@synthesize Prop5 = _Prop5;
@synthesize Prop4 = _Prop4;
@synthesize Prop8 = _Prop8;
@synthesize Prop10 = _Prop10;
- (void) setProp1: (SomeInvalidationImplementingObject*) InObj {
_Prop1 = InObj;
}
- (void) setProp2: (SomeInvalidationImplementingObject*) InObj {
_Prop2 = InObj;
}
- (SomeInvalidationImplementingObject*) Prop2 {
return _Prop2;
}
@synthesize NProp2 = _NpropIvar;
- (void) setNProp1: (NSObject*) InObj {
_NProp1 = InObj;
}
- (void) invalidate {
[Ivar2 invalidate];
self.Prop0 = 0;
self.Prop1 = 0;
[self setProp2:0];
[self setProp3:0];
[[self Prop5] invalidate2];
[self.Prop4 invalidate];
[self.Prop8 invalidate];
self.Prop6 = 0;
[[self Prop7] invalidate];
[_Ivar3 description];
NSLog(@"%@", _Ivar4);
[super invalidate];
}
#if RUN_IVAR_INVALIDATION
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated}}
// expected-warning@-3 {{Instance variable MultipleProtocols needs to be invalidated}}
// expected-warning@-4 {{Instance variable MultInheritance needs to be invalidated}}
// expected-warning@-5 {{Property SynthIvarProp needs to be invalidated or set to nil}}
// expected-warning@-6 {{Instance variable _Ivar3 needs to be invalidated}}
// expected-warning@-7 {{Instance variable _Ivar4 needs to be invalidated}}
// expected-warning@-8 {{Instance variable Ivar5 needs to be invalidated or set to nil}}
// expected-warning@-9 {{Instance variable Ivar13 needs to be invalidated or set to nil}}
#endif
-(void)partialInvalidator1 {
[Ivar9 invalidate];
[_Prop10 invalidate];
}
-(void)partialInvalidator2 {
[Ivar11 invalidate];
}
@end
// Example, where the same property is inherited through
// the parent and directly through a protocol. If a property backing ivar is
// synthesized in the parent, let the parent invalidate it.
@protocol IDEBuildable <NSObject>
@property (readonly, strong) id <Invalidation2> ObjB;
@end
@interface Parent : NSObject <IDEBuildable, Invalidation2> {
Invalidation2Class *_ObjB; // Invalidation of ObjB happens in the parent.
}
@end
@interface Child: Parent <Invalidation2, IDEBuildable>
@end
@implementation Parent{
@private
Invalidation2Class *Ivar10;
Invalidation2Class *Ivar11;
Invalidation2Class *Ivar12;
}
@synthesize ObjB = _ObjB;
- (void)invalidate{
_ObjB = ((void*)0);
assert(Ivar10 == 0);
if (__builtin_expect(!(Ivar11 == ((void*)0)), 0))
assert(0);
assert(0 == Ivar12);
}
@end
@implementation Child
- (void)invalidate{
// no-warning
}
@end
@protocol Invalidation <NSObject>
- (void)invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
@end
@interface Foo : NSObject <Invalidation>
@end
@class FooBar;
@protocol FooBar_Protocol <NSObject>
@end
@interface MissingInvalidationMethod : Foo <FooBar_Protocol>
@property (assign) MissingInvalidationMethod *foobar15_warn;
#if RUN_IVAR_INVALIDATION
// expected-warning@-2 {{Property foobar15_warn needs to be invalidated; no invalidation method is defined in the @implementation for MissingInvalidationMethod}}
#endif
@end
@implementation MissingInvalidationMethod
@end
@interface SuppressedMissingInvalidationMethod : Foo <FooBar_Protocol>
@property (assign) [[clang::suppress]] SuppressedMissingInvalidationMethod *foobar16_warn;
// FIXME: Suppression should have worked but decl-with-issue is the ivar, not the property.
#if RUN_IVAR_INVALIDATION
// expected-warning@-3 {{Property foobar16_warn needs to be invalidated; no invalidation method is defined in the @implementation for SuppressedMissingInvalidationMethod}}
#endif
@end
@implementation SuppressedMissingInvalidationMethod
@end
@interface MissingInvalidationMethod2 : Foo <FooBar_Protocol> {
Foo *Ivar1;
#if RUN_IVAR_INVALIDATION
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated; no invalidation method is defined in the @implementation for MissingInvalidationMethod2}}
#endif
}
@end
@implementation MissingInvalidationMethod2
@end
@interface MissingInvalidationMethodDecl : NSObject {
Foo *Ivar1;
#if RUN_MISSING_INVALIDATION_METHOD
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated; no invalidation method is declared for MissingInvalidationMethodDecl}}
#endif
}
@end
@implementation MissingInvalidationMethodDecl
@end
@interface MissingInvalidationMethodDecl2 : NSObject {
@private
Foo *_foo1;
#if RUN_MISSING_INVALIDATION_METHOD
// expected-warning@-2 {{Instance variable _foo1 needs to be invalidated; no invalidation method is declared for MissingInvalidationMethodDecl2}}
#endif
}
@property (strong) Foo *bar1;
@end
@implementation MissingInvalidationMethodDecl2
@end
@interface InvalidatedInPartial : SomeInvalidationImplementingObject {
SomeInvalidationImplementingObject *Ivar1;
SomeInvalidationImplementingObject *Ivar2;
[[clang::suppress]]
SomeInvalidationImplementingObject *Ivar3; // no-warning
}
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
@end
@implementation InvalidatedInPartial
-(void)partialInvalidator {
[Ivar1 invalidate];
Ivar2 = 0;
}
@end
@interface NotInvalidatedInPartial : SomeInvalidationImplementingObject {
SomeInvalidationImplementingObject *Ivar1;
}
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
-(void)partialInvalidatorCallsPartial __attribute__((annotate("objc_instance_variable_invalidator_partial")));
@end
@implementation NotInvalidatedInPartial
-(void)partialInvalidator {
}
-(void)partialInvalidatorCallsPartial {
[self partialInvalidator];
}
-(void)invalidate {
}
#if RUN_IVAR_INVALIDATION
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated or set to nil}}
#endif
@end
@interface SomeNotInvalidatedInPartial : SomeInvalidationImplementingObject {
SomeInvalidationImplementingObject *Ivar1;
SomeInvalidationImplementingObject *Ivar2;
#if RUN_IVAR_INVALIDATION
// expected-warning@-2 {{Instance variable Ivar2 needs to be invalidated or set to nil}}
#endif
}
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
-(void)partialInvalidatorCallsPartial __attribute__((annotate("objc_instance_variable_invalidator_partial")));
@end
@implementation SomeNotInvalidatedInPartial {
SomeInvalidationImplementingObject *Ivar3;
#if RUN_IVAR_INVALIDATION
// expected-warning@-2 {{Instance variable Ivar3 needs to be invalidated or set to nil}}
#endif
}
-(void)partialInvalidator {
Ivar1 = 0;
}
-(void)partialInvalidatorCallsPartial {
[self partialInvalidator];
}
@end
@interface OnlyPartialDeclsBase : NSObject
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
@end
@implementation OnlyPartialDeclsBase
-(void)partialInvalidator {}
@end
@interface OnlyPartialDecls : OnlyPartialDeclsBase {
SomeInvalidationImplementingObject *Ivar1;
#if RUN_IVAR_INVALIDATION
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated; no invalidation method is defined in the @implementation for OnlyPartialDecls}}
#endif
}
@end
@implementation OnlyPartialDecls
@end
// False negative.
@interface PartialCallsFull : SomeInvalidationImplementingObject {
SomeInvalidationImplementingObject *Ivar1;
}
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
@end
@implementation PartialCallsFull
-(void)partialInvalidator {
[self invalidate];
} // TODO: It would be nice to check that the full invalidation method actually invalidates the ivar.
@end