forked from OSchip/llvm-project
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. llvm-svn: 231265
This commit is contained in:
parent
4d9347993b
commit
e1d882c726
|
@ -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];
|
||||
|
|
|
@ -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 @@ 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,
|
||||
|
|
|
@ -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] = {
|
||||
|
|
|
@ -8187,6 +8187,236 @@ static bool isSetterLikeSelector(Selector sel) {
|
|||
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.
|
||||
|
|
|
@ -2799,7 +2799,9 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CheckObjCCircularContainer(Result);
|
||||
|
||||
return MaybeBindToTemporary(Result);
|
||||
}
|
||||
|
||||
|
|
|
@ -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; // 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}}
|
||||
}
|
||||
@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 { // 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}}
|
||||
}
|
||||
|
||||
@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}}
|
||||
}
|
||||
|
Loading…
Reference in New Issue