r297019 - [Sema][ObjC] Warn about 'performSelector' calls with selectors
Alex Lorenz via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 6 07:58:35 PST 2017
Author: arphaman
Date: Mon Mar 6 09:58:34 2017
New Revision: 297019
URL: http://llvm.org/viewvc/llvm-project?rev=297019&view=rev
Log:
[Sema][ObjC] Warn about 'performSelector' calls with selectors
that return record or vector types
The performSelector family of methods from Foundation use objc_msgSend to
dispatch the selector invocations to objects. However, method calls to methods
that return record types might have to use the objc_msgSend_stret as the return
value won't find into the register. This is also supported by this sentence from
performSelector documentation: "The method should not have a significant return
value and should take a single argument of type id, or no arguments". This
commit adds a new warning that warns when a selector which corresponds to a
method that returns a record type is passed into performSelector.
rdar://12056271
Differential Revision: https://reviews.llvm.org/D30174
Added:
cfe/trunk/test/SemaObjC/unsafe-perform-selector.m
Modified:
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/AST/DeclObjC.cpp
cfe/trunk/lib/Basic/IdentifierTable.cpp
cfe/trunk/lib/Sema/SemaExprObjC.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=297019&r1=297018&r2=297019&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Mar 6 09:58:34 2017
@@ -6016,6 +6016,12 @@ def warn_objc_circular_container : Warni
"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">;
+def warn_objc_unsafe_perform_selector : Warning<
+ "%0 is incompatible with selectors that return a "
+ "%select{struct|union|vector}1 type">,
+ InGroup<DiagGroup<"objc-unsafe-perform-selector">>;
+def note_objc_unsafe_perform_selector_method_declared_here : Note<
+ "method %0 that returns %1 declared here">;
def warn_setter_getter_impl_required : Warning<
"property %0 requires method %1 to be defined - "
Modified: cfe/trunk/lib/AST/DeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclObjC.cpp?rev=297019&r1=297018&r2=297019&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclObjC.cpp (original)
+++ cfe/trunk/lib/AST/DeclObjC.cpp Mon Mar 6 09:58:34 2017
@@ -979,11 +979,12 @@ ObjCMethodFamily ObjCMethodDecl::getMeth
break;
case OMF_performSelector:
- if (!isInstanceMethod() || !getReturnType()->isObjCIdType())
+ if (!isInstanceMethod() ||
+ !(getReturnType()->isObjCIdType() || getReturnType()->isVoidType()))
family = OMF_None;
else {
unsigned noParams = param_size();
- if (noParams < 1 || noParams > 3)
+ if (noParams < 1 || noParams > 5)
family = OMF_None;
else {
ObjCMethodDecl::param_type_iterator it = param_type_begin();
@@ -992,10 +993,11 @@ ObjCMethodFamily ObjCMethodDecl::getMeth
family = OMF_None;
break;
}
- while (--noParams) {
- it++;
- ArgT = (*it);
- if (!ArgT->isObjCIdType()) {
+ // The first type should generally always be 'id' or 'Thread *', the
+ // other types can vary.
+ if (noParams > 1) {
+ ArgT = *(it + 1);
+ if (!ArgT->isObjCObjectPointerType()) {
family = OMF_None;
break;
}
Modified: cfe/trunk/lib/Basic/IdentifierTable.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/IdentifierTable.cpp?rev=297019&r1=297018&r2=297019&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/IdentifierTable.cpp (original)
+++ cfe/trunk/lib/Basic/IdentifierTable.cpp Mon Mar 6 09:58:34 2017
@@ -487,8 +487,10 @@ ObjCMethodFamily Selector::getMethodFami
if (name == "self") return OMF_self;
if (name == "initialize") return OMF_initialize;
}
-
- if (name == "performSelector") return OMF_performSelector;
+
+ if (name == "performSelector" || name == "performSelectorInBackground" ||
+ name == "performSelectorOnMainThread")
+ return OMF_performSelector;
// The other method families may begin with a prefix of underscores.
while (!name.empty() && name.front() == '_')
Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=297019&r1=297018&r2=297019&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Mon Mar 6 09:58:34 2017
@@ -2266,6 +2266,52 @@ static void checkCocoaAPI(Sema &S, const
edit::rewriteObjCRedundantCallWithLiteral);
}
+static void checkFoundationAPI(Sema &S, SourceLocation Loc,
+ const ObjCMethodDecl *Method,
+ ArrayRef<Expr *> Args, QualType ReceiverType,
+ bool IsClassObjectCall) {
+ // Check if this is a performSelector method that uses a selector that returns
+ // a record or a vector type.
+ if (Method->getMethodFamily() != OMF_performSelector || Args.empty())
+ return;
+ const auto *SE = dyn_cast<ObjCSelectorExpr>(Args[0]->IgnoreParens());
+ if (!SE)
+ return;
+ ObjCMethodDecl *ImpliedMethod;
+ if (!IsClassObjectCall) {
+ const auto *OPT = ReceiverType->getAs<ObjCObjectPointerType>();
+ if (!OPT || !OPT->getInterfaceDecl())
+ return;
+ ImpliedMethod =
+ OPT->getInterfaceDecl()->lookupInstanceMethod(SE->getSelector());
+ if (!ImpliedMethod)
+ ImpliedMethod =
+ OPT->getInterfaceDecl()->lookupPrivateMethod(SE->getSelector());
+ } else {
+ const auto *IT = ReceiverType->getAs<ObjCInterfaceType>();
+ if (!IT)
+ return;
+ ImpliedMethod = IT->getDecl()->lookupClassMethod(SE->getSelector());
+ if (!ImpliedMethod)
+ ImpliedMethod =
+ IT->getDecl()->lookupPrivateClassMethod(SE->getSelector());
+ }
+ if (!ImpliedMethod)
+ return;
+ QualType Ret = ImpliedMethod->getReturnType();
+ if (Ret->isRecordType() || Ret->isVectorType() || Ret->isExtVectorType()) {
+ QualType Ret = ImpliedMethod->getReturnType();
+ S.Diag(Loc, diag::warn_objc_unsafe_perform_selector)
+ << Method->getSelector()
+ << (!Ret->isRecordType()
+ ? /*Vector*/ 2
+ : Ret->isUnionType() ? /*Union*/ 1 : /*Struct*/ 0);
+ S.Diag(ImpliedMethod->getLocStart(),
+ diag::note_objc_unsafe_perform_selector_method_declared_here)
+ << ImpliedMethod->getSelector() << Ret;
+ }
+}
+
/// \brief Diagnose use of %s directive in an NSString which is being passed
/// as formatting string to formatting method.
static void
@@ -2468,6 +2514,9 @@ ExprResult Sema::BuildClassMessage(TypeS
if (!isImplicit)
checkCocoaAPI(*this, Result);
}
+ if (Method)
+ checkFoundationAPI(*this, SelLoc, Method, makeArrayRef(Args, NumArgs),
+ ReceiverType, /*IsClassObjectCall=*/true);
return MaybeBindToTemporary(Result);
}
@@ -2994,6 +3043,26 @@ ExprResult Sema::BuildInstanceMessage(Ex
if (!isImplicit)
checkCocoaAPI(*this, Result);
}
+ if (Method) {
+ bool IsClassObjectCall = ClassMessage;
+ // 'self' message receivers in class methods should be treated as message
+ // sends to the class object in order for the semantic checks to be
+ // performed correctly. Messages to 'super' already count as class messages,
+ // so they don't need to be handled here.
+ if (Receiver && isSelfExpr(Receiver)) {
+ if (const auto *OPT = ReceiverType->getAs<ObjCObjectPointerType>()) {
+ if (OPT->getObjectType()->isObjCClass()) {
+ if (const auto *CurMeth = getCurMethodDecl()) {
+ IsClassObjectCall = true;
+ ReceiverType =
+ Context.getObjCInterfaceType(CurMeth->getClassInterface());
+ }
+ }
+ }
+ }
+ checkFoundationAPI(*this, SelLoc, Method, makeArrayRef(Args, NumArgs),
+ ReceiverType, IsClassObjectCall);
+ }
if (getLangOpts().ObjCAutoRefCount) {
// In ARC, annotate delegate init calls.
Added: cfe/trunk/test/SemaObjC/unsafe-perform-selector.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/unsafe-perform-selector.m?rev=297019&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjC/unsafe-perform-selector.m (added)
+++ cfe/trunk/test/SemaObjC/unsafe-perform-selector.m Mon Mar 6 09:58:34 2017
@@ -0,0 +1,127 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -verify %s
+// rdar://12056271
+
+ at class Thread;
+
+__attribute__((objc_root_class))
+ at interface NSObject
+
+- (id)performSelector:(SEL)sel;
+- (void)performSelectorInBackground:(SEL)sel withObject:(id)arg;
+- (void)performSelectorOnMainThread:(SEL)sel;
+
+- (void)performSelectorOnMainThread:(SEL)aSelector
+ onThread:(Thread *)thread
+ withObject:(id)arg
+ waitUntilDone:(int)wait
+ modes:(id *)array;
+
+ at end
+
+typedef struct { int x; int y; int width; int height; } Rectangle;
+
+struct Struct { Rectangle r; };
+
+typedef union { int x; float f; } Union;
+
+ at interface Base : NSObject
+
+- (struct Struct)returnsStruct2; // expected-note {{method 'returnsStruct2' that returns 'struct Struct' declared here}}
+- (Union)returnsId;
+
+ at end
+
+ at protocol IP
+
+- (Union)returnsUnion; // expected-note 2 {{method 'returnsUnion' that returns 'Union' declared here}}
+
+ at end
+
+typedef __attribute__((__ext_vector_type__(3))) float float3;
+typedef int int4 __attribute__ ((vector_size (16)));
+
+ at interface I : Base<IP>
+
+- (Rectangle)returnsStruct; // expected-note 4 {{method 'returnsStruct' that returns 'Rectangle' declared here}}
+- (id)returnsId; // shadows base 'returnsId'
+- (int)returnsInt;
+- (I *)returnPtr;
+- (float3)returnsExtVector; // expected-note {{method 'returnsExtVector' that returns 'float3' (vector of 3 'float' values) declared here}}
+- (int4)returnsVector; // expected-note {{method 'returnsVector' that returns 'int4' (vector of 4 'int' values) declared here}}
+
++ (Rectangle)returnsStructClass; // expected-note 2 {{method 'returnsStructClass' that returns 'Rectangle' declared here}}
++ (void)returnsUnion; // Not really
+
+ at end
+
+void foo(I *i) {
+ [i performSelector: @selector(returnsStruct)]; // expected-warning {{'performSelector:' is incompatible with selectors that return a struct type}}
+ [i performSelectorInBackground: @selector(returnsStruct) withObject:0]; // expected-warning {{'performSelectorInBackground:withObject:' is incompatible with selectors that return a struct type}}
+ [i performSelector: ((@selector(returnsUnion)))]; // expected-warning {{'performSelector:' is incompatible with selectors that return a union type}}
+ [i performSelectorOnMainThread: @selector(returnsStruct2)]; // expected-warning {{'performSelectorOnMainThread:' is incompatible with selectors that return a struct type}}
+ [I performSelector: (@selector(returnsStructClass))]; // expected-warning {{'performSelector:' is incompatible with selectors that return a struct type}}
+
+ [i performSelector: @selector(returnsId)];
+ [i performSelector: @selector(returnsInt)];
+ [i performSelector: @selector(returnsPtr)];
+ [I performSelector: @selector(returnsUnion)]; // No warning expected
+
+ id obj = i;
+ [obj performSelector: @selector(returnsId)];
+ [obj performSelector: @selector(returnsStruct)];
+}
+
+ at interface SubClass: I
+
+ at end
+
+ at interface SubClass ()
+- (struct Struct)returnsSubStructExt; // expected-note {{method 'returnsSubStructExt' that returns 'struct Struct' declared here}} expected-note {{method 'returnsSubStructExt' declared here}}
+ at end
+
+ at implementation SubClass // expected-warning {{method definition for 'returnsSubStructExt' not found}}
+
+- (struct Struct)returnsSubStructImpl { // expected-note {{method 'returnsSubStructImpl' that returns 'struct Struct' declared here}}
+ struct Struct Result;
+ return Result;
+}
+
+- (void)checkPrivateCalls {
+ [self performSelector: @selector(returnsSubStructExt)]; // expected-warning {{'performSelector:' is incompatible with selectors that return a struct type}}
+ [self performSelector: @selector(returnsSubStructImpl)]; // expected-warning {{'performSelector:' is incompatible with selectors that return a struct type}}
+}
+
+- (void)checkSuperCalls {
+ [super performSelector: @selector(returnsStruct)]; // expected-warning {{'performSelector:' is incompatible with selectors that return a struct type}}
+ [super performSelectorInBackground: @selector(returnsUnion) withObject: self]; // expected-warning {{'performSelectorInBackground:withObject:' is incompatible with selectors that return a union type}}
+ [super performSelector: @selector(returnsId)];
+}
+
++ (struct Struct)returnsSubStructClassImpl { // expected-note {{method 'returnsSubStructClassImpl' that returns 'struct Struct' declared here}}
+ struct Struct Result;
+ return Result;
+}
+
++ (void)checkClassPrivateCalls {
+ [self performSelector: @selector(returnsSubStructClassImpl)]; // expected-warning {{'performSelector:' is incompatible with selectors that return a struct type}}
+}
+
++ (void)checkClassSuperCalls {
+ [super performSelector: @selector(returnsStructClass)]; // expected-warning {{'performSelector:' is incompatible with selectors that return a struct type}}
+ [super performSelector: @selector(returnsUnion)]; // No warning expected
+}
+
+ at end
+
+ at implementation I (LongPerformSelectors)
+
+- (void)checkLongCallsFromCategory {
+ [self performSelectorOnMainThread: @selector(returnsStruct) onThread:0 withObject:self waitUntilDone:1 modes:0]; // expected-warning {{'performSelectorOnMainThread:onThread:withObject:waitUntilDone:modes:' is incompatible with selectors that return a struct type}}
+}
+
+- (void)checkVectorReturn {
+ [self performSelector: @selector(returnsExtVector)]; // expected-warning {{'performSelector:' is incompatible with selectors that return a vector type}}
+ [self performSelector: @selector(returnsVector)]; // expected-warning {{'performSelector:' is incompatible with selectors that return a vector type}}
+}
+
+ at end
More information about the cfe-commits
mailing list