Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
// RUN: %clang_cc1 -fblocks -fsyntax-only -Wnullable-to-nonnull-conversion %s -verify
|
|
|
|
//
|
|
|
|
// Test the substitution of type arguments for type parameters when
|
|
|
|
// using parameterized classes in Objective-C.
|
|
|
|
|
Warn when an intended Objective-C specialization was actually a useless protocol qualification.
Warn in cases where one has provided redundant protocol qualification
that might be a typo for a specialization, e.g., NSArray<NSObject>,
which is pointless (NSArray declares that it conforms to NSObject) and
is likely to be a typo for NSArray<NSObject *>, i.e., an array of
NSObject pointers. This warning is very narrow, only applying when the
base type being qualified is parameterized, has the same number of
parameters as their are protocols listed, all of the names can also
refer to types (including Objective-C class types, of course), and at
least one of those types is an Objective-C class (making this a typo
for a missing '*'). The limitations are partly for performance reasons
(we don't want to do redundant name lookup unless we really need to),
and because we want the warning to apply in very limited cases to
limit false positives.
Part of rdar://problem/6294649.
llvm-svn: 241547
2015-07-07 11:58:28 +08:00
|
|
|
@protocol NSObject
|
|
|
|
@end
|
|
|
|
|
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
__attribute__((objc_root_class))
|
Warn when an intended Objective-C specialization was actually a useless protocol qualification.
Warn in cases where one has provided redundant protocol qualification
that might be a typo for a specialization, e.g., NSArray<NSObject>,
which is pointless (NSArray declares that it conforms to NSObject) and
is likely to be a typo for NSArray<NSObject *>, i.e., an array of
NSObject pointers. This warning is very narrow, only applying when the
base type being qualified is parameterized, has the same number of
parameters as their are protocols listed, all of the names can also
refer to types (including Objective-C class types, of course), and at
least one of those types is an Objective-C class (making this a typo
for a missing '*'). The limitations are partly for performance reasons
(we don't want to do redundant name lookup unless we really need to),
and because we want the warning to apply in very limited cases to
limit false positives.
Part of rdar://problem/6294649.
llvm-svn: 241547
2015-07-07 11:58:28 +08:00
|
|
|
@interface NSObject <NSObject>
|
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
+ (instancetype)alloc;
|
|
|
|
- (instancetype)init;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@protocol NSCopying
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSString : NSObject <NSCopying>
|
|
|
|
@end
|
|
|
|
|
2015-07-07 11:58:42 +08:00
|
|
|
@interface NSMutableString : NSString
|
|
|
|
@end
|
|
|
|
|
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
@interface NSNumber : NSObject <NSCopying>
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSArray<T> : NSObject <NSCopying> {
|
|
|
|
@public
|
|
|
|
T *data; // don't try this at home
|
|
|
|
}
|
|
|
|
- (T)objectAtIndexedSubscript:(int)index;
|
|
|
|
+ (NSArray<T> *)array;
|
|
|
|
+ (void)setArray:(NSArray <T> *)array;
|
|
|
|
@property (copy,nonatomic) T lastObject;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSMutableArray<T> : NSArray<T>
|
|
|
|
-(instancetype)initWithArray:(NSArray<T> *)array; // expected-note{{passing argument}}
|
|
|
|
- (void)setObject:(T)object atIndexedSubscript:(int)index; // expected-note 2{{passing argument to parameter 'object' here}}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSStringArray : NSArray<NSString *>
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSSet<T> : NSObject <NSCopying>
|
|
|
|
- (T)firstObject;
|
|
|
|
@property (nonatomic, copy) NSArray<T> *allObjects;
|
|
|
|
@end
|
|
|
|
|
|
|
|
// Parameterized inheritance (simple case)
|
|
|
|
@interface NSMutableSet<U : id<NSCopying>> : NSSet<U>
|
|
|
|
- (void)addObject:(U)object; // expected-note 7{{passing argument to parameter 'object' here}}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface Widget : NSObject <NSCopying>
|
|
|
|
@end
|
|
|
|
|
|
|
|
// Non-parameterized class inheriting from a specialization of a
|
|
|
|
// parameterized class.
|
|
|
|
@interface WidgetSet : NSMutableSet<Widget *>
|
|
|
|
@end
|
|
|
|
|
|
|
|
// Parameterized inheritance with a more interesting transformation in
|
|
|
|
// the specialization.
|
|
|
|
@interface MutableSetOfArrays<T> : NSMutableSet<NSArray<T>*>
|
|
|
|
@end
|
|
|
|
|
|
|
|
// Inheriting from an unspecialized form of a parameterized type.
|
|
|
|
@interface UntypedMutableSet : NSMutableSet
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface Window : NSObject
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSDictionary<K, V> : NSObject <NSCopying>
|
|
|
|
- (V)objectForKeyedSubscript:(K)key; // expected-note 2{{parameter 'key'}}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSMutableDictionary<K : id<NSCopying>, V> : NSDictionary<K, V>
|
|
|
|
- (void)setObject:(V)object forKeyedSubscript:(K)key;
|
|
|
|
// expected-note@-1 {{parameter 'object' here}}
|
|
|
|
// expected-note@-2 {{parameter 'object' here}}
|
|
|
|
// expected-note@-3 {{parameter 'key' here}}
|
|
|
|
// expected-note@-4 {{parameter 'key' here}}
|
|
|
|
|
|
|
|
@property (strong) K someRandomKey;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface WindowArray : NSArray<Window *>
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSSet<T> (Searching)
|
|
|
|
- (T)findObject:(T)object;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSView : NSObject
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSControl : NSView
|
|
|
|
- (void)toggle;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSViewController<ViewType : NSView *> : NSObject
|
|
|
|
@property (nonatomic,retain) ViewType view;
|
|
|
|
@end
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Nullability
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
typedef NSControl * _Nonnull Nonnull_NSControl;
|
|
|
|
|
|
|
|
@interface NSNullableTest<ViewType : NSView *> : NSObject
|
|
|
|
- (ViewType)view;
|
|
|
|
- (nullable ViewType)maybeView;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSNullableTest2<ViewType : NSView * _Nullable> : NSObject // expected-error{{type parameter 'ViewType' bound 'NSView * _Nullable' cannot explicitly specify nullability}}
|
|
|
|
@end
|
|
|
|
|
|
|
|
void test_nullability(void) {
|
|
|
|
NSControl * _Nonnull nonnull_NSControl;
|
|
|
|
|
|
|
|
// Nullability introduced by substitution.
|
|
|
|
NSNullableTest<NSControl *> *unspecifiedControl;
|
|
|
|
nonnull_NSControl = [unspecifiedControl view];
|
|
|
|
nonnull_NSControl = [unspecifiedControl maybeView]; // expected-warning{{from nullable pointer 'NSControl * _Nullable' to non-nullable pointer type 'NSControl * _Nonnull'}}
|
|
|
|
|
|
|
|
// Nullability overridden by substitution.
|
|
|
|
NSNullableTest<Nonnull_NSControl> *nonnullControl;
|
|
|
|
nonnull_NSControl = [nonnullControl view];
|
|
|
|
nonnull_NSControl = [nonnullControl maybeView]; // expected-warning{{from nullable pointer 'Nonnull_NSControl _Nullable' (aka 'NSControl *') to non-nullable pointer type 'NSControl * _Nonnull'}}
|
|
|
|
|
|
|
|
// Nullability cannot be specified directly on a type argument.
|
|
|
|
NSNullableTest<NSControl * _Nonnull> *nonnullControl2; // expected-error{{type argument 'NSControl *' cannot explicitly specify nullability}}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Message sends.
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
void test_message_send_result(
|
|
|
|
NSSet<NSString *> *stringSet,
|
|
|
|
NSMutableSet<NSString *> *mutStringSet,
|
|
|
|
WidgetSet *widgetSet,
|
|
|
|
UntypedMutableSet *untypedMutSet,
|
|
|
|
MutableSetOfArrays<NSString *> *mutStringArraySet,
|
|
|
|
NSSet *set,
|
|
|
|
NSMutableSet *mutSet,
|
|
|
|
MutableSetOfArrays *mutArraySet,
|
|
|
|
NSArray<NSString *> *stringArray,
|
2015-07-07 11:58:42 +08:00
|
|
|
NSArray<__kindof NSString *> *kindofStringArray,
|
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
void (^block)(void)) {
|
|
|
|
int *ip;
|
|
|
|
ip = [stringSet firstObject]; // expected-warning{{from 'NSString *'}}
|
|
|
|
ip = [mutStringSet firstObject]; // expected-warning{{from 'NSString *'}}
|
|
|
|
ip = [widgetSet firstObject]; // expected-warning{{from 'Widget *'}}
|
|
|
|
ip = [untypedMutSet firstObject]; // expected-warning{{from 'id'}}
|
|
|
|
ip = [mutStringArraySet firstObject]; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
ip = [set firstObject]; // expected-warning{{from 'id'}}
|
|
|
|
ip = [mutSet firstObject]; // expected-warning{{from 'id'}}
|
|
|
|
ip = [mutArraySet firstObject]; // expected-warning{{from 'id'}}
|
|
|
|
ip = [block firstObject]; // expected-warning{{from 'id'}}
|
|
|
|
|
|
|
|
ip = [stringSet findObject:@"blah"]; // expected-warning{{from 'NSString *'}}
|
|
|
|
|
|
|
|
// Class messages.
|
|
|
|
ip = [NSSet<NSString *> alloc]; // expected-warning{{from 'NSSet<NSString *> *'}}
|
|
|
|
ip = [NSSet alloc]; // expected-warning{{from 'NSSet *'}}
|
|
|
|
ip = [MutableSetOfArrays<NSString *> alloc]; // expected-warning{{from 'MutableSetOfArrays<NSString *> *'}}
|
|
|
|
ip = [MutableSetOfArrays alloc]; // expected-warning{{from 'MutableSetOfArrays *'}}
|
|
|
|
ip = [NSArray<NSString *> array]; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
ip = [NSArray<NSString *><NSCopying> array]; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
|
|
|
|
ip = [[NSMutableArray<NSString *> alloc] init]; // expected-warning{{from 'NSMutableArray<NSString *> *'}}
|
|
|
|
|
|
|
|
[[NSMutableArray alloc] initWithArray: stringArray]; // okay
|
|
|
|
[[NSMutableArray<NSString *> alloc] initWithArray: stringArray]; // okay
|
|
|
|
[[NSMutableArray<NSNumber *> alloc] initWithArray: stringArray]; // expected-warning{{sending 'NSArray<NSString *> *' to parameter of type 'NSArray<NSNumber *> *'}}
|
2015-07-07 11:58:42 +08:00
|
|
|
|
|
|
|
ip = [[[NSViewController alloc] init] view]; // expected-warning{{from '__kindof NSView *'}}
|
|
|
|
[[[[NSViewController alloc] init] view] toggle];
|
|
|
|
|
|
|
|
NSMutableString *mutStr = kindofStringArray[0];
|
|
|
|
NSNumber *number = kindofStringArray[0]; // expected-warning{{of type '__kindof NSString *'}}
|
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void test_message_send_param(
|
|
|
|
NSMutableSet<NSString *> *mutStringSet,
|
|
|
|
WidgetSet *widgetSet,
|
|
|
|
UntypedMutableSet *untypedMutSet,
|
|
|
|
MutableSetOfArrays<NSString *> *mutStringArraySet,
|
|
|
|
NSMutableSet *mutSet,
|
|
|
|
MutableSetOfArrays *mutArraySet,
|
|
|
|
void (^block)(void)) {
|
|
|
|
Window *window;
|
|
|
|
|
|
|
|
[mutStringSet addObject: window]; // expected-warning{{parameter of type 'NSString *'}}
|
|
|
|
[widgetSet addObject: window]; // expected-warning{{parameter of type 'Widget *'}}
|
|
|
|
[untypedMutSet addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}}
|
|
|
|
[mutStringArraySet addObject: window]; // expected-warning{{parameter of type 'NSArray<NSString *> *'}}
|
|
|
|
[mutSet addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}}
|
|
|
|
[mutArraySet addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}}
|
|
|
|
[block addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Property accesses.
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
void test_property_read(
|
|
|
|
NSSet<NSString *> *stringSet,
|
|
|
|
NSMutableSet<NSString *> *mutStringSet,
|
|
|
|
WidgetSet *widgetSet,
|
|
|
|
UntypedMutableSet *untypedMutSet,
|
|
|
|
MutableSetOfArrays<NSString *> *mutStringArraySet,
|
|
|
|
NSSet *set,
|
|
|
|
NSMutableSet *mutSet,
|
|
|
|
MutableSetOfArrays *mutArraySet,
|
|
|
|
NSMutableDictionary *mutDict) {
|
|
|
|
int *ip;
|
|
|
|
ip = stringSet.allObjects; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
ip = mutStringSet.allObjects; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
ip = widgetSet.allObjects; // expected-warning{{from 'NSArray<Widget *> *'}}
|
|
|
|
ip = untypedMutSet.allObjects; // expected-warning{{from 'NSArray *'}}
|
|
|
|
ip = mutStringArraySet.allObjects; // expected-warning{{from 'NSArray<NSArray<NSString *> *> *'}}
|
|
|
|
ip = set.allObjects; // expected-warning{{from 'NSArray *'}}
|
|
|
|
ip = mutSet.allObjects; // expected-warning{{from 'NSArray *'}}
|
|
|
|
ip = mutArraySet.allObjects; // expected-warning{{from 'NSArray *'}}
|
|
|
|
|
2015-07-07 11:58:42 +08:00
|
|
|
ip = mutDict.someRandomKey; // expected-warning{{from '__kindof id<NSCopying>'}}
|
|
|
|
|
|
|
|
ip = [[NSViewController alloc] init].view; // expected-warning{{from '__kindof NSView *'}}
|
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void test_property_write(
|
|
|
|
NSMutableSet<NSString *> *mutStringSet,
|
|
|
|
WidgetSet *widgetSet,
|
|
|
|
UntypedMutableSet *untypedMutSet,
|
|
|
|
MutableSetOfArrays<NSString *> *mutStringArraySet,
|
|
|
|
NSMutableSet *mutSet,
|
|
|
|
MutableSetOfArrays *mutArraySet,
|
|
|
|
NSMutableDictionary *mutDict) {
|
|
|
|
int *ip;
|
|
|
|
|
|
|
|
mutStringSet.allObjects = ip; // expected-warning{{to 'NSArray<NSString *> *'}}
|
|
|
|
widgetSet.allObjects = ip; // expected-warning{{to 'NSArray<Widget *> *'}}
|
|
|
|
untypedMutSet.allObjects = ip; // expected-warning{{to 'NSArray *'}}
|
|
|
|
mutStringArraySet.allObjects = ip; // expected-warning{{to 'NSArray<NSArray<NSString *> *> *'}}
|
|
|
|
mutSet.allObjects = ip; // expected-warning{{to 'NSArray *'}}
|
|
|
|
mutArraySet.allObjects = ip; // expected-warning{{to 'NSArray *'}}
|
|
|
|
|
|
|
|
mutDict.someRandomKey = ip; // expected-warning{{to 'id<NSCopying>'}}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Subscripting
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
void test_subscripting(
|
|
|
|
NSArray<NSString *> *stringArray,
|
|
|
|
NSMutableArray<NSString *> *mutStringArray,
|
|
|
|
NSArray *array,
|
|
|
|
NSMutableArray *mutArray,
|
|
|
|
NSDictionary<NSString *, Widget *> *stringWidgetDict,
|
|
|
|
NSMutableDictionary<NSString *, Widget *> *mutStringWidgetDict,
|
|
|
|
NSDictionary *dict,
|
|
|
|
NSMutableDictionary *mutDict) {
|
|
|
|
int *ip;
|
|
|
|
NSString *string;
|
|
|
|
Widget *widget;
|
|
|
|
Window *window;
|
|
|
|
|
|
|
|
ip = stringArray[0]; // expected-warning{{from 'NSString *'}}
|
|
|
|
|
|
|
|
ip = mutStringArray[0]; // expected-warning{{from 'NSString *'}}
|
|
|
|
mutStringArray[0] = ip; // expected-warning{{parameter of type 'NSString *'}}
|
|
|
|
|
|
|
|
ip = array[0]; // expected-warning{{from 'id'}}
|
|
|
|
|
|
|
|
ip = mutArray[0]; // expected-warning{{from 'id'}}
|
|
|
|
mutArray[0] = ip; // expected-warning{{parameter of type 'id'}}
|
|
|
|
|
|
|
|
ip = stringWidgetDict[string]; // expected-warning{{from 'Widget *'}}
|
|
|
|
widget = stringWidgetDict[widget]; // expected-warning{{to parameter of type 'NSString *'}}
|
|
|
|
|
|
|
|
ip = mutStringWidgetDict[string]; // expected-warning{{from 'Widget *'}}
|
|
|
|
widget = mutStringWidgetDict[widget]; // expected-warning{{to parameter of type 'NSString *'}}
|
|
|
|
mutStringWidgetDict[string] = ip; // expected-warning{{to parameter of type 'Widget *'}}
|
|
|
|
mutStringWidgetDict[widget] = widget; // expected-warning{{to parameter of type 'NSString *'}}
|
|
|
|
|
|
|
|
ip = dict[string]; // expected-warning{{from 'id'}}
|
|
|
|
|
|
|
|
ip = mutDict[string]; // expected-warning{{from 'id'}}
|
|
|
|
mutDict[string] = ip; // expected-warning{{to parameter of type 'id'}}
|
|
|
|
|
|
|
|
widget = mutDict[window];
|
|
|
|
mutDict[window] = widget; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Instance variable access.
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
void test_instance_variable(NSArray<NSString *> *stringArray,
|
|
|
|
NSArray *array) {
|
|
|
|
int *ip;
|
|
|
|
|
|
|
|
ip = stringArray->data; // expected-warning{{from 'NSString **'}}
|
|
|
|
ip = array->data; // expected-warning{{from 'id *'}}
|
|
|
|
}
|
|
|
|
|
|
|
|
@implementation WindowArray
|
|
|
|
- (void)testInstanceVariable {
|
|
|
|
int *ip;
|
|
|
|
|
|
|
|
ip = data; // expected-warning{{from 'Window **'}}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Implicit conversions.
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
void test_implicit_conversions(NSArray<NSString *> *stringArray,
|
|
|
|
NSArray<NSNumber *> *numberArray,
|
|
|
|
NSMutableArray<NSString *> *mutStringArray,
|
|
|
|
NSArray *array,
|
|
|
|
NSMutableArray *mutArray) {
|
|
|
|
// Specialized -> unspecialized (same level)
|
|
|
|
array = stringArray;
|
|
|
|
|
|
|
|
// Unspecialized -> specialized (same level)
|
|
|
|
stringArray = array;
|
|
|
|
|
|
|
|
// Specialized -> specialized failure (same level).
|
|
|
|
stringArray = numberArray; // expected-warning{{incompatible pointer types assigning to 'NSArray<NSString *> *' from 'NSArray<NSNumber *> *'}}
|
|
|
|
|
|
|
|
// Specialized -> specialized (different levels).
|
|
|
|
stringArray = mutStringArray;
|
|
|
|
|
|
|
|
// Specialized -> specialized failure (different levels).
|
|
|
|
numberArray = mutStringArray; // expected-warning{{incompatible pointer types assigning to 'NSArray<NSNumber *> *' from 'NSMutableArray<NSString *> *'}}
|
|
|
|
|
|
|
|
// Unspecialized -> specialized (different levels).
|
|
|
|
stringArray = mutArray;
|
|
|
|
|
|
|
|
// Specialized -> unspecialized (different levels).
|
|
|
|
array = mutStringArray;
|
|
|
|
}
|
|
|
|
|
2015-07-07 11:58:54 +08:00
|
|
|
@interface NSCovariant1<__covariant T>
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSContravariant1<__contravariant T>
|
|
|
|
@end
|
|
|
|
|
|
|
|
void test_variance(NSCovariant1<NSString *> *covariant1,
|
|
|
|
NSCovariant1<NSMutableString *> *covariant2,
|
|
|
|
NSCovariant1<NSString *(^)(void)> *covariant3,
|
|
|
|
NSCovariant1<NSMutableString *(^)(void)> *covariant4,
|
|
|
|
NSCovariant1<id> *covariant5,
|
|
|
|
NSCovariant1<id<NSCopying>> *covariant6,
|
|
|
|
NSContravariant1<NSString *> *contravariant1,
|
|
|
|
NSContravariant1<NSMutableString *> *contravariant2) {
|
|
|
|
covariant1 = covariant2; // okay
|
|
|
|
covariant2 = covariant1; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1<NSMutableString *> *' from 'NSCovariant1<NSString *> *'}}
|
|
|
|
|
|
|
|
covariant3 = covariant4; // okay
|
|
|
|
covariant4 = covariant3; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1<NSMutableString *(^)(void)> *' from 'NSCovariant1<NSString *(^)(void)> *'}}
|
|
|
|
|
|
|
|
covariant5 = covariant1; // okay
|
|
|
|
covariant1 = covariant5; // okay: id is promiscuous
|
|
|
|
|
|
|
|
covariant5 = covariant3; // okay
|
|
|
|
covariant3 = covariant5; // okay
|
|
|
|
|
|
|
|
contravariant1 = contravariant2; // expected-warning{{incompatible pointer types assigning to 'NSContravariant1<NSString *> *' from 'NSContravariant1<NSMutableString *> *'}}
|
|
|
|
contravariant2 = contravariant1; // okay
|
|
|
|
}
|
|
|
|
|
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Ternary operator
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
void test_ternary_operator(NSArray<NSString *> *stringArray,
|
|
|
|
NSArray<NSNumber *> *numberArray,
|
|
|
|
NSMutableArray<NSString *> *mutStringArray,
|
|
|
|
NSStringArray *stringArray2,
|
|
|
|
NSArray *array,
|
|
|
|
NSMutableArray *mutArray,
|
|
|
|
int cond) {
|
|
|
|
int *ip;
|
|
|
|
id object;
|
|
|
|
|
|
|
|
ip = cond ? stringArray : mutStringArray; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
ip = cond ? mutStringArray : stringArray; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
|
2015-07-07 11:58:01 +08:00
|
|
|
ip = cond ? stringArray2 : mutStringArray; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
ip = cond ? mutStringArray : stringArray2; // expected-warning{{from 'NSArray<NSString *> *'}}
|
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
|
2015-07-07 11:58:01 +08:00
|
|
|
ip = cond ? stringArray : mutArray; // expected-warning{{from 'NSArray *'}}
|
|
|
|
|
|
|
|
ip = cond ? stringArray2 : mutArray; // expected-warning{{from 'NSArray *'}}
|
|
|
|
|
|
|
|
ip = cond ? mutArray : stringArray; // expected-warning{{from 'NSArray *'}}
|
|
|
|
|
|
|
|
ip = cond ? mutArray : stringArray2; // expected-warning{{from 'NSArray *'}}
|
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
2015-07-07 11:57:53 +08:00
|
|
|
|
|
|
|
object = cond ? stringArray : numberArray; // expected-warning{{incompatible operand types ('NSArray<NSString *> *' and 'NSArray<NSNumber *> *')}}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// super
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
@implementation NSStringArray
|
|
|
|
- (void)useSuperMethod {
|
|
|
|
int *ip;
|
|
|
|
ip = super.lastObject; // expected-warning{{from 'NSString *'}}
|
|
|
|
super.lastObject = ip; // expected-warning{{to 'NSString *'}}
|
|
|
|
ip = [super objectAtIndexedSubscript:0]; // expected-warning{{from 'NSString *'}}
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (void)useSuperMethod {
|
|
|
|
int *ip;
|
|
|
|
ip = super.array; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
super.array = ip; // expected-warning{{to 'NSArray<NSString *> *'}}
|
|
|
|
ip = [super array]; // expected-warning{{from 'NSArray<NSString *> *'}}
|
|
|
|
}
|
|
|
|
@end
|
Warn when an intended Objective-C specialization was actually a useless protocol qualification.
Warn in cases where one has provided redundant protocol qualification
that might be a typo for a specialization, e.g., NSArray<NSObject>,
which is pointless (NSArray declares that it conforms to NSObject) and
is likely to be a typo for NSArray<NSObject *>, i.e., an array of
NSObject pointers. This warning is very narrow, only applying when the
base type being qualified is parameterized, has the same number of
parameters as their are protocols listed, all of the names can also
refer to types (including Objective-C class types, of course), and at
least one of those types is an Objective-C class (making this a typo
for a missing '*'). The limitations are partly for performance reasons
(we don't want to do redundant name lookup unless we really need to),
and because we want the warning to apply in very limited cases to
limit false positives.
Part of rdar://problem/6294649.
llvm-svn: 241547
2015-07-07 11:58:28 +08:00
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// warning about likely protocol/class name typos.
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
typedef NSArray<NSObject> ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}}
|
2016-09-14 01:41:05 +08:00
|
|
|
|
|
|
|
// rdar://25060179
|
|
|
|
@interface MyMutableDictionary<KeyType, ObjectType> : NSObject
|
|
|
|
- (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key; // expected-note{{passing argument to parameter 'obj' here}} \
|
|
|
|
// expected-note{{passing argument to parameter 'key' here}}
|
|
|
|
@end
|
|
|
|
|
|
|
|
void bar(MyMutableDictionary<NSString *, NSString *> *stringsByString,
|
|
|
|
NSNumber *n1, NSNumber *n2) {
|
|
|
|
// We warn here when the key types do not match.
|
|
|
|
stringsByString[n1] = n2; // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} \
|
|
|
|
// expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString<NSCopying> *'}}
|
|
|
|
}
|
|
|
|
|
|
|
|
@interface MyTest<K, V> : NSObject <NSCopying>
|
|
|
|
- (V)test:(K)key;
|
|
|
|
- (V)test2:(K)key; // expected-note{{previous definition is here}}
|
|
|
|
- (void)mapUsingBlock:(id (^)(V))block;
|
|
|
|
- (void)mapUsingBlock2:(id (^)(V))block; // expected-note{{previous definition is here}}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation MyTest
|
|
|
|
- (id)test:(id)key {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
- (int)test2:(id)key{ // expected-warning{{conflicting return type in implementation}}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
- (void)mapUsingBlock:(id (^)(id))block {
|
|
|
|
}
|
|
|
|
- (void)mapUsingBlock2:(id)block { // expected-warning{{conflicting parameter types in implementation}}
|
|
|
|
}
|
|
|
|
@end
|