Index: include/clang/AST/NSAPI.h =================================================================== --- include/clang/AST/NSAPI.h +++ include/clang/AST/NSAPI.h @@ -33,9 +33,12 @@ 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 @@ 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 @@ 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 @@ /// \brief Return NSArrayMethodKind if \p Sel is such a selector. Optional 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 @@ 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 @@ /// \brief Return NSDictionaryMethodKind if \p Sel is such a selector. Optional 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 getNSSetMethodKind(Selector Sel); + /// \brief Returns selector for "objectForKeyedSubscript:". Selector getObjectForKeyedSubscriptSelector() const { return getOrInitSelector(StringRef("objectForKeyedSubscript"), @@ -207,6 +234,9 @@ /// \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]; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -5263,6 +5263,9 @@ "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>; def warn_setter_getter_impl_required : Warning< "property %0 requires method %1 to be defined - " Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -683,12 +683,27 @@ /// \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 @@ /// 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, Index: lib/AST/NSAPI.cpp =================================================================== --- lib/AST/NSAPI.cpp +++ lib/AST/NSAPI.cpp @@ -27,7 +27,10 @@ "NSMutableArray", "NSDictionary", "NSMutableDictionary", - "NSNumber" + "NSNumber", + "NSMutableSet", + "NSCountedSet", + "NSMutableOrderedSet" }; if (!ClassIds[K]) @@ -124,6 +127,25 @@ 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 @@ 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); } @@ -227,6 +265,63 @@ 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::getNSSetMethodKind(Selector Sel) { + for (unsigned i = 0; i != NumNSSetMethods; ++i) { + NSSetMethodKind MK = NSSetMethodKind(i); + if (Sel == getNSSetSelector(MK)) + return MK; + } + + return None; +} + Selector NSAPI::getNSNumberLiteralSelector(NSNumberLiteralMethodKind MK, bool Instance) const { static const char *ClassSelectorName[NumNSNumberLiteralMethods] = { Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -8169,6 +8169,231 @@ return !isLowercase(str.front()); } +Optional 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(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 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 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(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 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 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(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(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(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 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 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(Receiver)) { + Receiver = OE->getSourceExpr()->IgnoreImpCasts(); + } + + Expr *Arg = Message->getArg(ArgIndex)->IgnoreImpCasts(); + if (OpaqueValueExpr *OE = dyn_cast(Arg)) { + Arg = OE->getSourceExpr()->IgnoreImpCasts(); + } + + if (DeclRefExpr *ReceiverRE = dyn_cast(Receiver)) { + if (DeclRefExpr *ArgRE = dyn_cast(Arg)) { + if (ReceiverRE->getDecl() == ArgRE->getDecl()) { + Diag(Message->getSourceRange().getBegin(), + diag::warn_objc_circular_container) + << ArgRE->getDecl()->getName(); + return; + } + } + } + + if (ObjCIvarRefExpr *ReceiverRE = dyn_cast(Receiver)) { + if (ObjCIvarRefExpr *ArgRE = dyn_cast(Arg)) { + if (ReceiverRE->getDecl() == ArgRE->getDecl()) { + Diag(Message->getSourceRange().getBegin(), + diag::warn_objc_circular_container) + << ArgRE->getDecl()->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. Index: lib/Sema/SemaExprObjC.cpp =================================================================== --- lib/Sema/SemaExprObjC.cpp +++ lib/Sema/SemaExprObjC.cpp @@ -2799,7 +2799,9 @@ } } } - + + CheckObjCCircularContainer(Result); + return MaybeBindToTemporary(Result); } Index: test/SemaObjC/circular-container.m =================================================================== --- /dev/null +++ test/SemaObjC/circular-container.m @@ -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 +@class NSString; + +@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; + +@end + +@interface NSMutableDictionary + +- (void)setObject:(id)object forKey:(id)key; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +- (void)setValue:(id)value forKey:(NSString *)key; + +@end + +@interface NSMutableSet + +- (void)addObject:(id)object; + +@end + +@interface NSCountedSet : NSMutableSet + +@end + +@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; + +@end + +@interface SelfRefClass +{ + NSMutableArray *_array; + NSMutableDictionary *_dictionary; + NSMutableSet *_set; + NSCountedSet *_countedSet; + NSMutableOrderedSet *_orderedSet; +} +@end + +@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 { + [a addObject:a]; // expected-warning {{adding 'a' to 'a' might cause circular dependency in container}} +} + +- (void)checkNSMutableDictionary:(NSMutableDictionary *)d { + [d setObject:d forKey:@"key"]; // expected-warning {{adding 'd' to 'd' might cause circular dependency in container}} +} + +- (void)checkNSMutableSet:(NSMutableSet *)s { + [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}} +} + +- (void)checkNSCountedSet:(NSCountedSet *)s { + [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}} +} + +- (void)checkNSMutableOrderedSet:(NSMutableOrderedSet *)s { + [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}} +} + +@end + +void checkNSMutableArrayParam(NSMutableArray *a) { + [a addObject:a]; // expected-warning {{adding 'a' to 'a' might cause circular dependency in container}} +} + +void checkNSMutableDictionaryParam(NSMutableDictionary *d) { + [d setObject:d forKey:@"key"]; // expected-warning {{adding 'd' to 'd' might cause circular dependency in container}} +} + +void checkNSMutableSetParam(NSMutableSet *s) { + [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}} +} + +void checkNSCountedSetParam(NSCountedSet *s) { + [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}} +} + +void checkNSMutableOrderedSetParam(NSMutableOrderedSet *s) { + [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}} +} + +void checkNSMutableArray() { + NSMutableArray *a = nil; + + [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; + + [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; + + [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}} +} + +void checkNSCountedSet() { + NSCountedSet *s = nil; + + [s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}} +} + +void checkNSMutableOrderedSet() { + NSMutableOrderedSet *s = nil; + + [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}} +} +