forked from OSchip/llvm-project
[ObjC] Circular containers: add support of subclasses
llvm-svn: 244193
This commit is contained in:
parent
0f4e2c4d0f
commit
5dfac81c60
|
@ -16,6 +16,7 @@
|
|||
|
||||
namespace clang {
|
||||
class ASTContext;
|
||||
class ObjCInterfaceDecl;
|
||||
class QualType;
|
||||
class Expr;
|
||||
|
||||
|
@ -35,11 +36,10 @@ public:
|
|||
ClassId_NSMutableDictionary,
|
||||
ClassId_NSNumber,
|
||||
ClassId_NSMutableSet,
|
||||
ClassId_NSCountedSet,
|
||||
ClassId_NSMutableOrderedSet,
|
||||
ClassId_NSValue
|
||||
};
|
||||
static const unsigned NumClassIds = 11;
|
||||
static const unsigned NumClassIds = 10;
|
||||
|
||||
enum NSStringMethodKind {
|
||||
NSStr_stringWithString,
|
||||
|
@ -220,6 +220,10 @@ public:
|
|||
/// \brief Returns \c true if \p Id is currently defined as a macro.
|
||||
bool isMacroDefined(StringRef Id) const;
|
||||
|
||||
/// \brief Returns \c true if \p InterfaceDecl is subclass of \p NSClassKind
|
||||
bool isSubclassOfNSClass(ObjCInterfaceDecl *InterfaceDecl,
|
||||
NSClassIdKindKind NSClassKind) const;
|
||||
|
||||
private:
|
||||
bool isObjCTypedef(QualType T, StringRef name, IdentifierInfo *&II) const;
|
||||
bool isObjCEnumerator(const Expr *E,
|
||||
|
|
|
@ -5370,7 +5370,7 @@ def err_objc_object_catch : Error<
|
|||
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">,
|
||||
"adding '%0' to '%1' might cause circular dependency in container">,
|
||||
InGroup<DiagGroup<"objc-circular-container">>;
|
||||
def note_objc_circular_container_declared_here : Note<"'%0' declared here">;
|
||||
|
||||
|
|
|
@ -729,27 +729,12 @@ 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;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "clang/AST/NSAPI.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
|
||||
|
@ -29,7 +30,6 @@ IdentifierInfo *NSAPI::getNSClassId(NSClassIdKindKind K) const {
|
|||
"NSMutableDictionary",
|
||||
"NSNumber",
|
||||
"NSMutableSet",
|
||||
"NSCountedSet",
|
||||
"NSMutableOrderedSet",
|
||||
"NSValue"
|
||||
};
|
||||
|
@ -511,6 +511,26 @@ bool NSAPI::isMacroDefined(StringRef Id) const {
|
|||
return Ctx.Idents.get(Id).hasMacroDefinition();
|
||||
}
|
||||
|
||||
bool NSAPI::isSubclassOfNSClass(ObjCInterfaceDecl *InterfaceDecl,
|
||||
NSClassIdKindKind NSClassKind) const {
|
||||
if (!InterfaceDecl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IdentifierInfo *NSClassID = getNSClassId(NSClassKind);
|
||||
|
||||
bool IsSubclass = false;
|
||||
do {
|
||||
IsSubclass = NSClassID == InterfaceDecl->getIdentifier();
|
||||
|
||||
if (IsSubclass) {
|
||||
break;
|
||||
}
|
||||
} while ((InterfaceDecl = InterfaceDecl->getSuperClass()));
|
||||
|
||||
return IsSubclass;
|
||||
}
|
||||
|
||||
bool NSAPI::isObjCTypedef(QualType T,
|
||||
StringRef name, IdentifierInfo *&II) const {
|
||||
if (!Ctx.getLangOpts().ObjC1)
|
||||
|
|
|
@ -8727,23 +8727,10 @@ static bool isSetterLikeSelector(Selector sel) {
|
|||
|
||||
static 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()) {
|
||||
bool IsMutableArray = S.NSAPIObj->isSubclassOfNSClass(
|
||||
Message->getReceiverInterface(),
|
||||
NSAPI::ClassId_NSMutableArray);
|
||||
if (!IsMutableArray) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -8775,24 +8762,10 @@ static Optional<int> GetNSMutableArrayArgumentIndex(Sema &S,
|
|||
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()) {
|
||||
bool IsMutableDictionary = S.NSAPIObj->isSubclassOfNSClass(
|
||||
Message->getReceiverInterface(),
|
||||
NSAPI::ClassId_NSMutableDictionary);
|
||||
if (!IsMutableDictionary) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -8820,63 +8793,14 @@ Optional<int> GetNSMutableDictionaryArgumentIndex(Sema &S,
|
|||
}
|
||||
|
||||
static Optional<int> GetNSSetArgumentIndex(Sema &S, ObjCMessageExpr *Message) {
|
||||
bool IsMutableSet = S.NSAPIObj->isSubclassOfNSClass(
|
||||
Message->getReceiverInterface(),
|
||||
NSAPI::ClassId_NSMutableSet);
|
||||
|
||||
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) {
|
||||
bool IsMutableOrderedSet = S.NSAPIObj->isSubclassOfNSClass(
|
||||
Message->getReceiverInterface(),
|
||||
NSAPI::ClassId_NSMutableOrderedSet);
|
||||
if (!IsMutableSet && !IsMutableOrderedSet) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -8917,38 +8841,51 @@ void Sema::CheckObjCCircularContainer(ObjCMessageExpr *Message) {
|
|||
|
||||
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 (Message->getReceiverKind() == ObjCMessageExpr::SuperInstance) {
|
||||
if (DeclRefExpr *ArgRE = dyn_cast<DeclRefExpr>(Arg)) {
|
||||
if (ReceiverRE->getDecl() == ArgRE->getDecl()) {
|
||||
ValueDecl *Decl = ReceiverRE->getDecl();
|
||||
if (ArgRE->isObjCSelfExpr()) {
|
||||
Diag(Message->getSourceRange().getBegin(),
|
||||
diag::warn_objc_circular_container)
|
||||
<< Decl->getName();
|
||||
Diag(Decl->getLocation(),
|
||||
diag::note_objc_circular_container_declared_here)
|
||||
<< Decl->getName();
|
||||
<< ArgRE->getDecl()->getName() << StringRef("super");
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
} else {
|
||||
Expr *Receiver = Message->getInstanceReceiver()->IgnoreImpCasts();
|
||||
|
||||
if (OpaqueValueExpr *OE = dyn_cast<OpaqueValueExpr>(Receiver)) {
|
||||
Receiver = 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() << Decl->getName();
|
||||
if (!ArgRE->isObjCSelfExpr()) {
|
||||
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() << Decl->getName();
|
||||
Diag(Decl->getLocation(),
|
||||
diag::note_objc_circular_container_declared_here)
|
||||
<< Decl->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,3 +144,64 @@ void checkNSMutableOrderedSet() {
|
|||
[s replaceObjectAtIndex:0 withObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
|
||||
}
|
||||
|
||||
// Test subclassing
|
||||
|
||||
@interface FootableSet : NSMutableSet
|
||||
@end
|
||||
|
||||
@implementation FootableSet
|
||||
- (void)meth {
|
||||
[super addObject:self]; // expected-warning {{adding 'self' to 'super' might cause circular dependency in container}}
|
||||
[super addObject:nil]; // no-warning
|
||||
[self addObject:self]; // expected-warning {{adding 'self' to 'self' might cause circular dependency in container}}
|
||||
}
|
||||
@end
|
||||
|
||||
@interface FootableArray : NSMutableArray
|
||||
@end
|
||||
|
||||
@implementation FootableArray
|
||||
- (void)meth {
|
||||
[super addObject:self]; // expected-warning {{adding 'self' to 'super' might cause circular dependency in container}}
|
||||
[super addObject:nil]; // no-warning
|
||||
[self addObject:self]; // expected-warning {{adding 'self' to 'self' might cause circular dependency in container}}
|
||||
}
|
||||
@end
|
||||
|
||||
@interface FootableDictionary : NSMutableDictionary
|
||||
@end
|
||||
|
||||
@implementation FootableDictionary
|
||||
- (void)meth {
|
||||
[super setObject:self forKey:@"key"]; // expected-warning {{adding 'self' to 'super' might cause circular dependency in container}}
|
||||
[super setObject:nil forKey:@"key"]; // no-warning
|
||||
[self setObject:self forKey:@"key"]; // expected-warning {{adding 'self' to 'self' might cause circular dependency in container}}
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
void subclassingNSMutableArray() {
|
||||
FootableArray *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 subclassingNSMutableDictionary() {
|
||||
FootableDictionary *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 subclassingNSMutableSet() {
|
||||
FootableSet *s = nil; // expected-note {{'s' declared here}}
|
||||
|
||||
[s addObject:s]; // expected-warning {{adding 's' to 's' might cause circular dependency in container}}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue