r231265 - New ObjC warning: circular containers.

Alex Denisov 1101.debian at gmail.com
Wed Mar 4 09:55:52 PST 2015


Author: alexdenisov
Date: Wed Mar  4 11:55:52 2015
New Revision: 231265

URL: http://llvm.org/viewvc/llvm-project?rev=231265&view=rev
Log:
New ObjC warning: circular containers.

This commit adds new warning to prevent user from creating 'circular containers'.

Mutable collections from NSFoundation allows user to add collection to itself, e.g.: 

NSMutableArray *a = [NSMutableArray new]; 
[a addObject:a]; 

The code above leads to really weird behaviour (crashes, 'endless' recursion) and 
retain cycles (collection retains itself) if ARC enabled.

Patch checks the following collections: 
  - NSMutableArray, 
  - NSMutableDictionary, 
  - NSMutableSet, 
  - NSMutableOrderedSet, 
  - NSCountedSet. 


Added:
    cfe/trunk/test/SemaObjC/circular-container.m
Modified:
    cfe/trunk/include/clang/AST/NSAPI.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/NSAPI.cpp
    cfe/trunk/lib/Sema/SemaChecking.cpp
    cfe/trunk/lib/Sema/SemaExprObjC.cpp

Modified: cfe/trunk/include/clang/AST/NSAPI.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/NSAPI.h?rev=231265&r1=231264&r2=231265&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/NSAPI.h (original)
+++ cfe/trunk/include/clang/AST/NSAPI.h Wed Mar  4 11:55:52 2015
@@ -33,9 +33,12 @@ public:
     ClassId_NSMutableArray,
     ClassId_NSDictionary,
     ClassId_NSMutableDictionary,
-    ClassId_NSNumber
+    ClassId_NSNumber,
+    ClassId_NSMutableSet,
+    ClassId_NSCountedSet,
+    ClassId_NSMutableOrderedSet,
   };
-  static const unsigned NumClassIds = 7;
+  static const unsigned NumClassIds = 10;
 
   enum NSStringMethodKind {
     NSStr_stringWithString,
@@ -67,7 +70,8 @@ public:
     return isObjCEnumerator(E, "NSASCIIStringEncoding",NSASCIIStringEncodingId);
   }
 
-  /// \brief Enumerates the NSArray methods used to generate literals.
+  /// \brief Enumerates the NSArray/NSMutableArray methods used to generate
+  /// literals and to apply some checks.
   enum NSArrayMethodKind {
     NSArr_array,
     NSArr_arrayWithArray,
@@ -77,9 +81,12 @@ public:
     NSArr_initWithArray,
     NSArr_initWithObjects,
     NSArr_objectAtIndex,
-    NSMutableArr_replaceObjectAtIndex
+    NSMutableArr_replaceObjectAtIndex,
+    NSMutableArr_addObject,
+    NSMutableArr_insertObjectAtIndex,
+    NSMutableArr_setObjectAtIndexedSubscript
   };
-  static const unsigned NumNSArrayMethods = 9;
+  static const unsigned NumNSArrayMethods = 12;
 
   /// \brief The Objective-C NSArray selectors.
   Selector getNSArraySelector(NSArrayMethodKind MK) const;
@@ -87,7 +94,8 @@ public:
   /// \brief Return NSArrayMethodKind if \p Sel is such a selector.
   Optional<NSArrayMethodKind> getNSArrayMethodKind(Selector Sel);
 
-  /// \brief Enumerates the NSDictionary methods used to generate literals.
+  /// \brief Enumerates the NSDictionary/NSMutableDictionary methods used
+  /// to generate literals and to apply some checks.
   enum NSDictionaryMethodKind {
     NSDict_dictionary,
     NSDict_dictionaryWithDictionary,
@@ -99,9 +107,11 @@ public:
     NSDict_initWithObjectsAndKeys,
     NSDict_initWithObjectsForKeys,
     NSDict_objectForKey,
-    NSMutableDict_setObjectForKey
+    NSMutableDict_setObjectForKey,
+    NSMutableDict_setObjectForKeyedSubscript,
+    NSMutableDict_setValueForKey
   };
-  static const unsigned NumNSDictionaryMethods = 12;
+  static const unsigned NumNSDictionaryMethods = 14;
   
   /// \brief The Objective-C NSDictionary selectors.
   Selector getNSDictionarySelector(NSDictionaryMethodKind MK) const;
@@ -109,6 +119,23 @@ public:
   /// \brief Return NSDictionaryMethodKind if \p Sel is such a selector.
   Optional<NSDictionaryMethodKind> getNSDictionaryMethodKind(Selector Sel);
 
+  /// \brief Enumerates the NSMutableSet/NSOrderedSet methods used
+  /// to apply some checks.
+  enum NSSetMethodKind {
+    NSMutableSet_addObject,
+    NSOrderedSet_insertObjectAtIndex,
+    NSOrderedSet_setObjectAtIndex,
+    NSOrderedSet_setObjectAtIndexedSubscript,
+    NSOrderedSet_replaceObjectAtIndexWithObject
+  };
+  static const unsigned NumNSSetMethods = 5;
+
+  /// \brief The Objective-C NSSet selectors.
+  Selector getNSSetSelector(NSSetMethodKind MK) const;
+
+  /// \brief Return NSSetMethodKind if \p Sel is such a selector.
+  Optional<NSSetMethodKind> getNSSetMethodKind(Selector Sel);
+
   /// \brief Returns selector for "objectForKeyedSubscript:".
   Selector getObjectForKeyedSubscriptSelector() const {
     return getOrInitSelector(StringRef("objectForKeyedSubscript"),
@@ -207,6 +234,9 @@ private:
   /// \brief The selectors for Objective-C NSDictionary methods.
   mutable Selector NSDictionarySelectors[NumNSDictionaryMethods];
 
+  /// \brief The selectors for Objective-C NSSet methods.
+  mutable Selector NSSetSelectors[NumNSSetMethods];
+
   /// \brief The Objective-C NSNumber selectors used to create NSNumber literals.
   mutable Selector NSNumberClassSelectors[NumNSNumberLiteralMethods];
   mutable Selector NSNumberInstanceSelectors[NumNSNumberLiteralMethods];

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=231265&r1=231264&r2=231265&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Mar  4 11:55:52 2015
@@ -5263,6 +5263,10 @@ def err_objc_object_catch : Error<
   "can't catch an Objective-C object by value">;
 def err_incomplete_type_objc_at_encode : Error<
   "'@encode' of incomplete type %0">;
+def warn_objc_circular_container : Warning<
+  "adding '%0' to '%0' might cause circular dependency in container">,
+  InGroup<DiagGroup<"objc-circular-container">>;
+def note_objc_circular_container_declared_here : Note<"'%0' declared here">;
 
 def warn_setter_getter_impl_required : Warning<
   "property %0 requires method %1 to be defined - "

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=231265&r1=231264&r2=231265&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Wed Mar  4 11:55:52 2015
@@ -683,12 +683,27 @@ public:
   /// \brief The declaration of the Objective-C NSArray class.
   ObjCInterfaceDecl *NSArrayDecl;
 
+  /// \brief Pointer to NSMutableArray type (NSMutableArray *).
+  QualType NSMutableArrayPointer;
+
   /// \brief The declaration of the arrayWithObjects:count: method.
   ObjCMethodDecl *ArrayWithObjectsMethod;
 
   /// \brief The declaration of the Objective-C NSDictionary class.
   ObjCInterfaceDecl *NSDictionaryDecl;
 
+  /// \brief Pointer to NSMutableDictionary type (NSMutableDictionary *).
+  QualType NSMutableDictionaryPointer;
+
+  /// \brief Pointer to NSMutableSet type (NSMutableSet *).
+  QualType NSMutableSetPointer;
+
+  /// \brief Pointer to NSCountedSet type (NSCountedSet *).
+  QualType NSCountedSetPointer;
+
+  /// \brief Pointer to NSMutableOrderedSet type (NSMutableOrderedSet *).
+  QualType NSMutableOrderedSetPointer;
+
   /// \brief The declaration of the dictionaryWithObjects:forKeys:count: method.
   ObjCMethodDecl *DictionaryWithObjectsMethod;
 
@@ -8645,6 +8660,10 @@ private:
   /// statement that produces control flow different from GCC.
   void CheckBreakContinueBinding(Expr *E);
 
+  /// \brief Check whether receiver is mutable ObjC container which
+  /// attempts to add itself into the container
+  void CheckObjCCircularContainer(ObjCMessageExpr *Message);
+
 public:
   /// \brief Register a magic integral constant to be used as a type tag.
   void RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind,

Modified: cfe/trunk/lib/AST/NSAPI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/NSAPI.cpp?rev=231265&r1=231264&r2=231265&view=diff
==============================================================================
--- cfe/trunk/lib/AST/NSAPI.cpp (original)
+++ cfe/trunk/lib/AST/NSAPI.cpp Wed Mar  4 11:55:52 2015
@@ -27,7 +27,10 @@ IdentifierInfo *NSAPI::getNSClassId(NSCl
     "NSMutableArray",
     "NSDictionary",
     "NSMutableDictionary",
-    "NSNumber"
+    "NSNumber",
+    "NSMutableSet",
+    "NSCountedSet",
+    "NSMutableOrderedSet"
   };
 
   if (!ClassIds[K])
@@ -124,6 +127,25 @@ Selector NSAPI::getNSArraySelector(NSArr
       Sel = Ctx.Selectors.getSelector(2, KeyIdents);
       break;
     }
+    case NSMutableArr_addObject:
+      Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("addObject"));
+      break;
+    case NSMutableArr_insertObjectAtIndex: {
+      IdentifierInfo *KeyIdents[] = {
+        &Ctx.Idents.get("insertObject"),
+        &Ctx.Idents.get("atIndex")
+      };
+      Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+      break;
+    }
+    case NSMutableArr_setObjectAtIndexedSubscript: {
+      IdentifierInfo *KeyIdents[] = {
+        &Ctx.Idents.get("setObject"),
+        &Ctx.Idents.get("atIndexedSubscript")
+      };
+      Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+      break;
+    }
     }
     return (NSArraySelectors[MK] = Sel);
   }
@@ -209,6 +231,22 @@ Selector NSAPI::getNSDictionarySelector(
       Sel = Ctx.Selectors.getSelector(2, KeyIdents);
       break;
     }
+    case NSMutableDict_setObjectForKeyedSubscript: {
+      IdentifierInfo *KeyIdents[] = {
+        &Ctx.Idents.get("setObject"),
+        &Ctx.Idents.get("forKeyedSubscript")
+      };
+      Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+      break;
+    }
+    case NSMutableDict_setValueForKey: {
+      IdentifierInfo *KeyIdents[] = {
+        &Ctx.Idents.get("setValue"),
+        &Ctx.Idents.get("forKey")
+      };
+      Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+      break;
+    }
     }
     return (NSDictionarySelectors[MK] = Sel);
   }
@@ -224,6 +262,63 @@ NSAPI::getNSDictionaryMethodKind(Selecto
       return MK;
   }
 
+  return None;
+}
+
+Selector NSAPI::getNSSetSelector(NSSetMethodKind MK) const {
+  if (NSSetSelectors[MK].isNull()) {
+    Selector Sel;
+    switch (MK) {
+    case NSMutableSet_addObject:
+      Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("addObject"));
+      break;
+    case NSOrderedSet_insertObjectAtIndex: {
+      IdentifierInfo *KeyIdents[] = {
+        &Ctx.Idents.get("insertObject"),
+        &Ctx.Idents.get("atIndex")
+      };
+      Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+      break;
+    }
+    case NSOrderedSet_setObjectAtIndex: {
+      IdentifierInfo *KeyIdents[] = {
+        &Ctx.Idents.get("setObject"),
+        &Ctx.Idents.get("atIndex")
+      };
+      Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+      break;
+    }
+    case NSOrderedSet_setObjectAtIndexedSubscript: {
+      IdentifierInfo *KeyIdents[] = {
+        &Ctx.Idents.get("setObject"),
+        &Ctx.Idents.get("atIndexedSubscript")
+      };
+      Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+      break;
+    }
+    case NSOrderedSet_replaceObjectAtIndexWithObject: {
+      IdentifierInfo *KeyIdents[] = {
+        &Ctx.Idents.get("replaceObjectAtIndex"),
+        &Ctx.Idents.get("withObject")
+      };
+      Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+      break;
+    }
+    }
+    return (NSSetSelectors[MK] = Sel);
+  }
+
+  return NSSetSelectors[MK];
+}
+
+Optional<NSAPI::NSSetMethodKind>
+NSAPI::getNSSetMethodKind(Selector Sel) {
+  for (unsigned i = 0; i != NumNSSetMethods; ++i) {
+    NSSetMethodKind MK = NSSetMethodKind(i);
+    if (Sel == getNSSetSelector(MK))
+      return MK;
+  }
+
   return None;
 }
 

Modified: cfe/trunk/lib/Sema/SemaChecking.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=231265&r1=231264&r2=231265&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Wed Mar  4 11:55:52 2015
@@ -8187,6 +8187,236 @@ static bool isSetterLikeSelector(Selecto
   return !isLowercase(str.front());
 }
 
+Optional<int> GetNSMutableArrayArgumentIndex(Sema &S, ObjCMessageExpr *Message) {
+
+  if (S.NSMutableArrayPointer.isNull()) {
+    IdentifierInfo *NSMutableArrayId =
+      S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSMutableArray);
+    NamedDecl *IF = S.LookupSingleName(S.TUScope, NSMutableArrayId,
+                                       Message->getLocStart(),
+                                       Sema::LookupOrdinaryName);
+    ObjCInterfaceDecl *InterfaceDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF);
+    if (!InterfaceDecl) {
+      return None;
+    }
+    QualType NSMutableArrayObject =
+      S.Context.getObjCInterfaceType(InterfaceDecl);
+    S.NSMutableArrayPointer =
+      S.Context.getObjCObjectPointerType(NSMutableArrayObject);
+  }
+
+  if (S.NSMutableArrayPointer != Message->getReceiverType()) {
+    return None;
+  }
+
+  Selector Sel = Message->getSelector();
+
+  Optional<NSAPI::NSArrayMethodKind> MKOpt =
+    S.NSAPIObj->getNSArrayMethodKind(Sel);
+  if (!MKOpt) {
+    return None;
+  }
+
+  NSAPI::NSArrayMethodKind MK = *MKOpt;
+
+  switch (MK) {
+    case NSAPI::NSMutableArr_addObject:
+    case NSAPI::NSMutableArr_insertObjectAtIndex:
+    case NSAPI::NSMutableArr_setObjectAtIndexedSubscript:
+      return 0;
+    case NSAPI::NSMutableArr_replaceObjectAtIndex:
+      return 1;
+
+    default:
+      return None;
+  }
+
+  return None;
+}
+
+static
+Optional<int> GetNSMutableDictionaryArgumentIndex(Sema &S,
+                                                  ObjCMessageExpr *Message) {
+
+  if (S.NSMutableDictionaryPointer.isNull()) {
+    IdentifierInfo *NSMutableDictionaryId =
+      S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSMutableDictionary);
+    NamedDecl *IF = S.LookupSingleName(S.TUScope, NSMutableDictionaryId,
+                                       Message->getLocStart(),
+                                       Sema::LookupOrdinaryName);
+    ObjCInterfaceDecl *InterfaceDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF);
+    if (!InterfaceDecl) {
+      return None;
+    }
+    QualType NSMutableDictionaryObject =
+      S.Context.getObjCInterfaceType(InterfaceDecl);
+    S.NSMutableDictionaryPointer =
+      S.Context.getObjCObjectPointerType(NSMutableDictionaryObject);
+  }
+
+  if (S.NSMutableDictionaryPointer != Message->getReceiverType()) {
+    return None;
+  }
+
+  Selector Sel = Message->getSelector();
+
+  Optional<NSAPI::NSDictionaryMethodKind> MKOpt =
+    S.NSAPIObj->getNSDictionaryMethodKind(Sel);
+  if (!MKOpt) {
+    return None;
+  }
+
+  NSAPI::NSDictionaryMethodKind MK = *MKOpt;
+
+  switch (MK) {
+    case NSAPI::NSMutableDict_setObjectForKey:
+    case NSAPI::NSMutableDict_setValueForKey:
+    case NSAPI::NSMutableDict_setObjectForKeyedSubscript:
+      return 0;
+
+    default:
+      return None;
+  }
+
+  return None;
+}
+
+static Optional<int> GetNSSetArgumentIndex(Sema &S, ObjCMessageExpr *Message) {
+
+  ObjCInterfaceDecl *InterfaceDecl;
+  if (S.NSMutableSetPointer.isNull()) {
+    IdentifierInfo *NSMutableSetId =
+      S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSMutableSet);
+    NamedDecl *IF = S.LookupSingleName(S.TUScope, NSMutableSetId,
+                                       Message->getLocStart(),
+                                       Sema::LookupOrdinaryName);
+    InterfaceDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF);
+    if (InterfaceDecl) {
+      QualType NSMutableSetObject =
+        S.Context.getObjCInterfaceType(InterfaceDecl);
+      S.NSMutableSetPointer =
+        S.Context.getObjCObjectPointerType(NSMutableSetObject);
+    }
+  }
+
+  if (S.NSCountedSetPointer.isNull()) {
+    IdentifierInfo *NSCountedSetId =
+      S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSCountedSet);
+    NamedDecl *IF = S.LookupSingleName(S.TUScope, NSCountedSetId,
+                                       Message->getLocStart(),
+                                       Sema::LookupOrdinaryName);
+    InterfaceDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF);
+    if (InterfaceDecl) {
+      QualType NSCountedSetObject =
+        S.Context.getObjCInterfaceType(InterfaceDecl);
+      S.NSCountedSetPointer =
+        S.Context.getObjCObjectPointerType(NSCountedSetObject);
+    }
+  }
+
+  if (S.NSMutableOrderedSetPointer.isNull()) {
+    IdentifierInfo *NSOrderedSetId =
+      S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSMutableOrderedSet);
+    NamedDecl *IF = S.LookupSingleName(S.TUScope, NSOrderedSetId,
+                                       Message->getLocStart(),
+                                       Sema::LookupOrdinaryName);
+    InterfaceDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF);
+    if (InterfaceDecl) {
+      QualType NSOrderedSetObject =
+        S.Context.getObjCInterfaceType(InterfaceDecl);
+      S.NSMutableOrderedSetPointer =
+        S.Context.getObjCObjectPointerType(NSOrderedSetObject);
+    }
+  }
+
+  QualType ReceiverType = Message->getReceiverType();
+
+  bool IsMutableSet = !S.NSMutableSetPointer.isNull() &&
+    ReceiverType == S.NSMutableSetPointer;
+  bool IsMutableOrderedSet = !S.NSMutableOrderedSetPointer.isNull() &&
+    ReceiverType == S.NSMutableOrderedSetPointer;
+  bool IsCountedSet = !S.NSCountedSetPointer.isNull() &&
+    ReceiverType == S.NSCountedSetPointer;
+
+  if (!IsMutableSet && !IsMutableOrderedSet && !IsCountedSet) {
+    return None;
+  }
+
+  Selector Sel = Message->getSelector();
+
+  Optional<NSAPI::NSSetMethodKind> MKOpt = S.NSAPIObj->getNSSetMethodKind(Sel);
+  if (!MKOpt) {
+    return None;
+  }
+
+  NSAPI::NSSetMethodKind MK = *MKOpt;
+
+  switch (MK) {
+    case NSAPI::NSMutableSet_addObject:
+    case NSAPI::NSOrderedSet_setObjectAtIndex:
+    case NSAPI::NSOrderedSet_setObjectAtIndexedSubscript:
+    case NSAPI::NSOrderedSet_insertObjectAtIndex:
+      return 0;
+    case NSAPI::NSOrderedSet_replaceObjectAtIndexWithObject:
+      return 1;
+  }
+
+  return None;
+}
+
+void Sema::CheckObjCCircularContainer(ObjCMessageExpr *Message) {
+  if (!Message->isInstanceMessage()) {
+    return;
+  }
+
+  Optional<int> ArgOpt;
+
+  if (!(ArgOpt = GetNSMutableArrayArgumentIndex(*this, Message)) &&
+      !(ArgOpt = GetNSMutableDictionaryArgumentIndex(*this, Message)) &&
+      !(ArgOpt = GetNSSetArgumentIndex(*this, Message))) {
+    return;
+  }
+
+  int ArgIndex = *ArgOpt;
+
+  Expr *Receiver = Message->getInstanceReceiver()->IgnoreImpCasts();
+  if (OpaqueValueExpr *OE = dyn_cast<OpaqueValueExpr>(Receiver)) {
+    Receiver = OE->getSourceExpr()->IgnoreImpCasts();
+  }
+
+  Expr *Arg = Message->getArg(ArgIndex)->IgnoreImpCasts();
+  if (OpaqueValueExpr *OE = dyn_cast<OpaqueValueExpr>(Arg)) {
+    Arg = OE->getSourceExpr()->IgnoreImpCasts();
+  }
+
+  if (DeclRefExpr *ReceiverRE = dyn_cast<DeclRefExpr>(Receiver)) {
+    if (DeclRefExpr *ArgRE = dyn_cast<DeclRefExpr>(Arg)) {
+      if (ReceiverRE->getDecl() == ArgRE->getDecl()) {
+        ValueDecl *Decl = ReceiverRE->getDecl();
+        Diag(Message->getSourceRange().getBegin(),
+             diag::warn_objc_circular_container)
+          << Decl->getName();
+        Diag(Decl->getLocation(),
+             diag::note_objc_circular_container_declared_here)
+          << Decl->getName();
+      }
+    }
+  } else if (ObjCIvarRefExpr *IvarRE = dyn_cast<ObjCIvarRefExpr>(Receiver)) {
+    if (ObjCIvarRefExpr *IvarArgRE = dyn_cast<ObjCIvarRefExpr>(Arg)) {
+      if (IvarRE->getDecl() == IvarArgRE->getDecl()) {
+        ObjCIvarDecl *Decl = IvarRE->getDecl();
+        Diag(Message->getSourceRange().getBegin(),
+             diag::warn_objc_circular_container)
+          << Decl->getName();
+        Diag(Decl->getLocation(),
+             diag::note_objc_circular_container_declared_here)
+          << Decl->getName();
+      }
+    }
+  }
+
+}
+
 /// Check a message send to see if it's likely to cause a retain cycle.
 void Sema::checkRetainCycles(ObjCMessageExpr *msg) {
   // Only check instance methods whose selector looks like a setter.

Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=231265&r1=231264&r2=231265&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Wed Mar  4 11:55:52 2015
@@ -2799,7 +2799,9 @@ ExprResult Sema::BuildInstanceMessage(Ex
       }
     }
   }
-  
+
+  CheckObjCCircularContainer(Result);
+
   return MaybeBindToTemporary(Result);
 }
 

Added: cfe/trunk/test/SemaObjC/circular-container.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/circular-container.m?rev=231265&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjC/circular-container.m (added)
+++ cfe/trunk/test/SemaObjC/circular-container.m Wed Mar  4 11:55:52 2015
@@ -0,0 +1,146 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -verify -Wno-objc-root-class %s
+
+typedef long int NSUInteger;
+#define nil 0
+ at class NSString;
+
+ at interface NSMutableArray
+
+- (void)addObject:(id)object;
+- (void)insertObject:(id)object atIndex:(NSUInteger)index;
+- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)object;
+- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index;
+
+ at end
+
+ at interface NSMutableDictionary
+
+- (void)setObject:(id)object forKey:(id)key;
+- (void)setObject:(id)object forKeyedSubscript:(id)key;
+- (void)setValue:(id)value forKey:(NSString *)key;
+
+ at end
+
+ at interface NSMutableSet
+
+- (void)addObject:(id)object;
+
+ at end
+
+ at interface NSCountedSet : NSMutableSet
+
+ at end
+
+ at interface NSMutableOrderedSet
+
+- (void)addObject:(id)object;
+- (void)insertObject:(id)object atIndex:(NSUInteger)index;
+- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index;
+- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)object;
+- (void)setObject:(id)object atIndex:(NSUInteger)index;
+
+ at end
+
+ at interface SelfRefClass
+{
+  NSMutableArray *_array; // expected-note {{'_array' declared here}}
+  NSMutableDictionary *_dictionary; // expected-note {{'_dictionary' declared here}}
+  NSMutableSet *_set; // expected-note {{'_set' declared here}}
+  NSCountedSet *_countedSet; // expected-note {{'_countedSet' declared here}}
+  NSMutableOrderedSet *_orderedSet; // expected-note {{'_orderedSet' declared here}}
+}
+ at end
+
+ at implementation SelfRefClass
+
+- (void)check {
+  [_array addObject:_array]; // expected-warning {{adding '_array' to '_array' might cause circular dependency in container}}
+  [_dictionary setObject:_dictionary forKey:@"key"]; // expected-warning {{adding '_dictionary' to '_dictionary' might cause circular dependency in container}}
+  [_set addObject:_set]; // expected-warning {{adding '_set' to '_set' might cause circular dependency in container}}
+  [_countedSet addObject:_countedSet]; // expected-warning {{adding '_countedSet' to '_countedSet' might cause circular dependency in container}}
+  [_orderedSet addObject:_orderedSet]; // expected-warning {{adding '_orderedSet' to '_orderedSet' might cause circular dependency in container}}
+}
+
+- (void)checkNSMutableArray:(NSMutableArray *)a { // expected-note {{'a' declared here}}
+  [a addObject:a]; // expected-warning {{adding 'a' to 'a' might cause circular dependency in container}}
+}
+
+- (void)checkNSMutableDictionary:(NSMutableDictionary *)d { // expected-note {{'d' declared here}}
+  [d setObject:d forKey:@"key"]; // expected-warning {{adding 'd' to 'd' might cause circular dependency in container}}
+}
+
+- (void)checkNSMutableSet:(NSMutableSet *)s { // expected-note {{'s' declared here}}
+  [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+}
+
+- (void)checkNSCountedSet:(NSCountedSet *)s { // expected-note {{'s' declared here}}
+  [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+}
+
+- (void)checkNSMutableOrderedSet:(NSMutableOrderedSet *)s { // expected-note {{'s' declared here}}
+  [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+}
+
+ at end
+
+void checkNSMutableArrayParam(NSMutableArray *a) { // expected-note {{'a' declared here}}
+  [a addObject:a]; // expected-warning {{adding 'a' to 'a' might cause circular dependency in container}}
+}
+
+void checkNSMutableDictionaryParam(NSMutableDictionary *d) { // expected-note {{'d' declared here}}
+  [d setObject:d forKey:@"key"]; // expected-warning {{adding 'd' to 'd' might cause circular dependency in container}}
+}
+
+void checkNSMutableSetParam(NSMutableSet *s) { // expected-note {{'s' declared here}}
+  [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+}
+
+void checkNSCountedSetParam(NSCountedSet *s) { // expected-note {{'s' declared here}}
+  [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+}
+
+void checkNSMutableOrderedSetParam(NSMutableOrderedSet *s) { // expected-note {{'s' declared here}}
+  [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+}
+
+void checkNSMutableArray() {
+  NSMutableArray *a = nil; // expected-note 5 {{'a' declared here}} 5
+
+  [a addObject:a]; // expected-warning {{adding 'a' to 'a' might cause circular dependency in container}}
+  [a insertObject:a atIndex:0]; // expected-warning {{adding 'a' to 'a' might cause circular dependency in container}}
+  [a replaceObjectAtIndex:0 withObject:a]; // expected-warning {{adding 'a' to 'a' might cause circular dependency in container}}
+  [a setObject:a atIndexedSubscript:0]; // expected-warning {{adding 'a' to 'a' might cause circular dependency in container}}
+  a[0] = a; // expected-warning {{adding 'a' to 'a' might cause circular dependency in container}}
+}
+
+void checkNSMutableDictionary() {
+  NSMutableDictionary *d = nil; // expected-note 4 {{'d' declared here}}
+
+  [d setObject:d forKey:@"key"]; // expected-warning {{adding 'd' to 'd' might cause circular dependency in container}}
+  [d setObject:d forKeyedSubscript:@"key"]; // expected-warning {{adding 'd' to 'd' might cause circular dependency in container}}
+  [d setValue:d forKey:@"key"]; // expected-warning {{adding 'd' to 'd' might cause circular dependency in container}}
+  d[@"key"] = d; // expected-warning {{adding 'd' to 'd' might cause circular dependency in container}}
+}
+
+void checkNSMutableSet() {
+  NSMutableSet *s = nil; // expected-note {{'s' declared here}}
+
+  [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+}
+
+void checkNSCountedSet() {
+  NSCountedSet *s = nil; // expected-note {{'s' declared here}}
+
+  [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+}
+
+void checkNSMutableOrderedSet() {
+  NSMutableOrderedSet *s = nil; // expected-note 5 {{'s' declared here}}
+
+  [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+  [s insertObject:s atIndex:0]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+  [s setObject:s atIndex:0]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+  [s setObject:s atIndexedSubscript:0]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+  [s replaceObjectAtIndex:0 withObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
+}
+





More information about the cfe-commits mailing list