mirror of https://github.com/microsoft/clang.git
Warn when an Objective-C collection literal element is converted to an incompatible type.
Objective-C collection literals produce unspecialized NSArray/NSDictionary objects that can then be implicitly converted to specialized versions of these types. In such cases, check that the elements in the collection are suitable for the specialized collection. Part of rdar://problem/6294649. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@241546 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
f09aad8652
commit
61fd1cbc6d
|
@ -2131,6 +2131,11 @@ def warn_concatenated_nsarray_literal : Warning<
|
|||
InGroup<ObjCStringConcatenation>;
|
||||
def note_objc_literal_comparison_isequal : Note<
|
||||
"use 'isEqual:' instead">;
|
||||
def warn_objc_collection_literal_element : Warning<
|
||||
"object of type %0 is not compatible with "
|
||||
"%select{array element type|dictionary key type|dictionary value type}1 %2">,
|
||||
InGroup<ObjCLiteralConversion>;
|
||||
|
||||
def err_attribute_argument_is_zero : Error<
|
||||
"%0 attribute must be greater than 0">;
|
||||
def warn_attribute_argument_n_negative : Warning<
|
||||
|
|
|
@ -6884,6 +6884,101 @@ static void DiagnoseNullConversion(Sema &S, Expr *E, QualType T,
|
|||
S.getFixItZeroLiteralForType(T, Loc));
|
||||
}
|
||||
|
||||
static void checkObjCArrayLiteral(Sema &S, QualType TargetType,
|
||||
ObjCArrayLiteral *ArrayLiteral);
|
||||
static void checkObjCDictionaryLiteral(Sema &S, QualType TargetType,
|
||||
ObjCDictionaryLiteral *DictionaryLiteral);
|
||||
|
||||
/// Check a single element within a collection literal against the
|
||||
/// target element type.
|
||||
static void checkObjCCollectionLiteralElement(Sema &S,
|
||||
QualType TargetElementType,
|
||||
Expr *Element,
|
||||
unsigned ElementKind) {
|
||||
// Skip a bitcast to 'id' or qualified 'id'.
|
||||
if (auto ICE = dyn_cast<ImplicitCastExpr>(Element)) {
|
||||
if (ICE->getCastKind() == CK_BitCast &&
|
||||
ICE->getSubExpr()->getType()->getAs<ObjCObjectPointerType>())
|
||||
Element = ICE->getSubExpr();
|
||||
}
|
||||
|
||||
QualType ElementType = Element->getType();
|
||||
ExprResult ElementResult(Element);
|
||||
if (ElementType->getAs<ObjCObjectPointerType>() &&
|
||||
S.CheckSingleAssignmentConstraints(TargetElementType,
|
||||
ElementResult,
|
||||
false, false)
|
||||
!= Sema::Compatible) {
|
||||
S.Diag(Element->getLocStart(),
|
||||
diag::warn_objc_collection_literal_element)
|
||||
<< ElementType << ElementKind << TargetElementType
|
||||
<< Element->getSourceRange();
|
||||
}
|
||||
|
||||
if (auto ArrayLiteral = dyn_cast<ObjCArrayLiteral>(Element))
|
||||
checkObjCArrayLiteral(S, TargetElementType, ArrayLiteral);
|
||||
else if (auto DictionaryLiteral = dyn_cast<ObjCDictionaryLiteral>(Element))
|
||||
checkObjCDictionaryLiteral(S, TargetElementType, DictionaryLiteral);
|
||||
}
|
||||
|
||||
/// Check an Objective-C array literal being converted to the given
|
||||
/// target type.
|
||||
static void checkObjCArrayLiteral(Sema &S, QualType TargetType,
|
||||
ObjCArrayLiteral *ArrayLiteral) {
|
||||
if (!S.NSArrayDecl)
|
||||
return;
|
||||
|
||||
const auto *TargetObjCPtr = TargetType->getAs<ObjCObjectPointerType>();
|
||||
if (!TargetObjCPtr)
|
||||
return;
|
||||
|
||||
if (TargetObjCPtr->isUnspecialized() ||
|
||||
TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl()
|
||||
!= S.NSArrayDecl->getCanonicalDecl())
|
||||
return;
|
||||
|
||||
auto TypeArgs = TargetObjCPtr->getTypeArgs();
|
||||
if (TypeArgs.size() != 1)
|
||||
return;
|
||||
|
||||
QualType TargetElementType = TypeArgs[0];
|
||||
for (unsigned I = 0, N = ArrayLiteral->getNumElements(); I != N; ++I) {
|
||||
checkObjCCollectionLiteralElement(S, TargetElementType,
|
||||
ArrayLiteral->getElement(I),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check an Objective-C dictionary literal being converted to the given
|
||||
/// target type.
|
||||
static void checkObjCDictionaryLiteral(
|
||||
Sema &S, QualType TargetType,
|
||||
ObjCDictionaryLiteral *DictionaryLiteral) {
|
||||
if (!S.NSDictionaryDecl)
|
||||
return;
|
||||
|
||||
const auto *TargetObjCPtr = TargetType->getAs<ObjCObjectPointerType>();
|
||||
if (!TargetObjCPtr)
|
||||
return;
|
||||
|
||||
if (TargetObjCPtr->isUnspecialized() ||
|
||||
TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl()
|
||||
!= S.NSDictionaryDecl->getCanonicalDecl())
|
||||
return;
|
||||
|
||||
auto TypeArgs = TargetObjCPtr->getTypeArgs();
|
||||
if (TypeArgs.size() != 2)
|
||||
return;
|
||||
|
||||
QualType TargetKeyType = TypeArgs[0];
|
||||
QualType TargetObjectType = TypeArgs[1];
|
||||
for (unsigned I = 0, N = DictionaryLiteral->getNumElements(); I != N; ++I) {
|
||||
auto Element = DictionaryLiteral->getKeyValueElement(I);
|
||||
checkObjCCollectionLiteralElement(S, TargetKeyType, Element.Key, 1);
|
||||
checkObjCCollectionLiteralElement(S, TargetObjectType, Element.Value, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
|
||||
SourceLocation CC, bool *ICContext = nullptr) {
|
||||
if (E->isTypeDependent() || E->isValueDependent()) return;
|
||||
|
@ -6923,6 +7018,13 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
|
|||
}
|
||||
}
|
||||
|
||||
// Check implicit casts from Objective-C collection literals to specialized
|
||||
// collection types, e.g., NSArray<NSString *> *.
|
||||
if (auto *ArrayLiteral = dyn_cast<ObjCArrayLiteral>(E))
|
||||
checkObjCArrayLiteral(S, QualType(Target, 0), ArrayLiteral);
|
||||
else if (auto *DictionaryLiteral = dyn_cast<ObjCDictionaryLiteral>(E))
|
||||
checkObjCDictionaryLiteral(S, QualType(Target, 0), DictionaryLiteral);
|
||||
|
||||
// Strip vector types.
|
||||
if (isa<VectorType>(Source)) {
|
||||
if (!isa<VectorType>(Target)) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -verify %s
|
||||
|
||||
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
|
||||
typedef unsigned long NSUInteger;
|
||||
#else
|
||||
typedef unsigned int NSUInteger;
|
||||
#endif
|
||||
|
||||
@protocol NSObject
|
||||
@end
|
||||
|
||||
@protocol NSCopying
|
||||
@end
|
||||
|
||||
__attribute__((objc_root_class))
|
||||
@interface NSObject <NSObject>
|
||||
@end
|
||||
|
||||
@interface NSString : NSObject <NSCopying>
|
||||
@end
|
||||
|
||||
@interface NSNumber : NSObject <NSCopying>
|
||||
+ (NSNumber *)numberWithInt:(int)value;
|
||||
@end
|
||||
|
||||
@interface NSArray<T> : NSObject <NSCopying>
|
||||
+ (instancetype)arrayWithObjects:(const T [])objects count:(NSUInteger)cnt;
|
||||
@end
|
||||
|
||||
@interface NSDictionary<K, V> : NSObject <NSCopying>
|
||||
+ (instancetype)dictionaryWithObjects:(const V [])objects forKeys:(const K [])keys count:(NSUInteger)cnt;
|
||||
@end
|
||||
|
||||
void testArrayLiteral(void) {
|
||||
NSArray<NSString *> *array1 = @[@"hello",
|
||||
@1, // expected-warning{{of type 'NSNumber *' is not compatible with array element type 'NSString *'}}
|
||||
@"world",
|
||||
@[@1, @2]]; // expected-warning{{of type 'NSArray *' is not compatible with array element type 'NSString *'}}
|
||||
|
||||
NSArray<NSArray<NSString *> *> *array2 = @[@[@"hello", @"world"],
|
||||
@"blah", // expected-warning{{object of type 'NSString *' is not compatible with array element type 'NSArray<NSString *> *'}}
|
||||
@[@1]]; // expected-warning{{object of type 'NSNumber *' is not compatible with array element type 'NSString *'}}
|
||||
}
|
||||
|
||||
void testDictionaryLiteral(void) {
|
||||
NSDictionary<NSString *, NSNumber *> *dict1 = @{
|
||||
@"hello" : @17,
|
||||
@18 : @18, // expected-warning{{object of type 'NSNumber *' is not compatible with dictionary key type 'NSString *'}}
|
||||
@"world" : @"blah" // expected-warning{{object of type 'NSString *' is not compatible with dictionary value type 'NSNumber *'}}
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue