Permalink
Browse files

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. 



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@231265 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information...
AlexDenisov committed Mar 4, 2015
1 parent 7a4bb7e commit 5dc6c6cd87f3a86fe9d5ba9d1b3892252c7de248
@@ -33,9 +33,12 @@ class NSAPI {
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 @@ class NSAPI {
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,17 +81,21 @@ class NSAPI {
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;

/// \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,16 +107,35 @@ class NSAPI {
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;

/// \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 @@ class NSAPI {
/// \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];
@@ -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 - "
@@ -683,12 +683,27 @@ class Sema {
/// \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 @@ class Sema {
/// 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,
@@ -27,7 +27,10 @@ IdentifierInfo *NSAPI::getNSClassId(NSClassIdKindKind K) const {
"NSMutableArray",
"NSDictionary",
"NSMutableDictionary",
"NSNumber"
"NSNumber",
"NSMutableSet",
"NSCountedSet",
"NSMutableOrderedSet"
};

if (!ClassIds[K])
@@ -124,6 +127,25 @@ Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const {
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);
}
@@ -227,6 +265,63 @@ NSAPI::getNSDictionaryMethodKind(Selector Sel) {
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;
}

Selector NSAPI::getNSNumberLiteralSelector(NSNumberLiteralMethodKind MK,
bool Instance) const {
static const char *ClassSelectorName[NumNSNumberLiteralMethods] = {
Oops, something went wrong.

0 comments on commit 5dc6c6c

Please sign in to comment.