r241546 - Warn when an Objective-C collection literal element is converted to an incompatible type.

Douglas Gregor dgregor at apple.com
Mon Jul 6 20:58:22 PDT 2015


Author: dgregor
Date: Mon Jul  6 22:58:22 2015
New Revision: 241546

URL: http://llvm.org/viewvc/llvm-project?rev=241546&view=rev
Log:
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.

Added:
    cfe/trunk/test/SemaObjC/parameterized_classes_collection_literal.m
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaChecking.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=241546&r1=241545&r2=241546&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Jul  6 22:58:22 2015
@@ -2131,6 +2131,11 @@ def warn_concatenated_nsarray_literal :
   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<

Modified: cfe/trunk/lib/Sema/SemaChecking.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=241546&r1=241545&r2=241546&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Mon Jul  6 22:58:22 2015
@@ -6884,6 +6884,101 @@ static void DiagnoseNullConversion(Sema
                                       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, Ex
     }
   }
 
+  // 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)) {

Added: cfe/trunk/test/SemaObjC/parameterized_classes_collection_literal.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/parameterized_classes_collection_literal.m?rev=241546&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjC/parameterized_classes_collection_literal.m (added)
+++ cfe/trunk/test/SemaObjC/parameterized_classes_collection_literal.m Mon Jul  6 22:58:22 2015
@@ -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
+
+ at protocol NSObject
+ at end
+
+ at protocol NSCopying
+ at end
+
+__attribute__((objc_root_class))
+ at interface NSObject <NSObject>
+ at end
+
+ at interface NSString : NSObject <NSCopying>
+ at end
+
+ at interface NSNumber : NSObject <NSCopying>
++ (NSNumber *)numberWithInt:(int)value;
+ at end
+
+ at interface NSArray<T> : NSObject <NSCopying>
++ (instancetype)arrayWithObjects:(const T [])objects count:(NSUInteger)cnt;
+ at end
+
+ at interface NSDictionary<K, V> : NSObject <NSCopying>
++ (instancetype)dictionaryWithObjects:(const V [])objects forKeys:(const K [])keys count:(NSUInteger)cnt;
+ at 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 *'}}
+  };
+}





More information about the cfe-commits mailing list