[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-03-06 Thread Alex Lorenz via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL297019: [Sema][ObjC] Warn about 'performSelector' calls with 
selectors (authored by arphaman).

Changed prior to commit:
  https://reviews.llvm.org/D30174?vs=90473=90709#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D30174

Files:
  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
  cfe/trunk/test/SemaObjC/unsafe-perform-selector.m

Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
===
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6016,6 +6016,12 @@
   "adding '%0' to '%1' might cause circular dependency in container">,
   InGroup>;
 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>;
+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 - "
Index: cfe/trunk/test/SemaObjC/unsafe-perform-selector.m
===
--- cfe/trunk/test/SemaObjC/unsafe-perform-selector.m
+++ cfe/trunk/test/SemaObjC/unsafe-perform-selector.m
@@ -0,0 +1,127 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -verify %s
+// rdar://12056271
+
+@class Thread;
+
+__attribute__((objc_root_class))
+@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;
+
+@end
+
+typedef struct { int x; int y; int width; int height; } Rectangle;
+
+struct Struct { Rectangle r; };
+
+typedef union { int x; float f; } Union;
+
+@interface Base : NSObject
+
+- (struct Struct)returnsStruct2; // expected-note {{method 'returnsStruct2' that returns 'struct Struct' declared here}}
+- (Union)returnsId;
+
+@end
+
+@protocol IP
+
+- (Union)returnsUnion; // expected-note 2 {{method 'returnsUnion' that returns 'Union' declared here}}
+
+@end
+
+typedef __attribute__((__ext_vector_type__(3))) float float3;
+typedef int int4 __attribute__ ((vector_size (16)));
+
+@interface I : Base
+
+- (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
+
+@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)];
+}
+
+@interface SubClass: I
+
+@end
+
+@interface SubClass ()
+- (struct Struct)returnsSubStructExt; // expected-note {{method 'returnsSubStructExt' that returns 'struct Struct' declared here}} expected-note {{method 'returnsSubStructExt' declared here}}
+@end
+
+@implementation SubClass // 

[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-03-05 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak accepted this revision.
ahatanak added a comment.
This revision is now accepted and ready to land.

Looks good, thanks!


Repository:
  rL LLVM

https://reviews.llvm.org/D30174



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-03-03 Thread Alex Lorenz via Phabricator via cfe-commits
arphaman updated this revision to Diff 90473.
arphaman marked 3 inline comments as done.
arphaman added a comment.

The updated diff:

- Warns for vector types.
- Addresses Akira's comments.


Repository:
  rL LLVM

https://reviews.llvm.org/D30174

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/AST/DeclObjC.cpp
  lib/Basic/IdentifierTable.cpp
  lib/Sema/SemaExprObjC.cpp
  test/SemaObjC/unsafe-perform-selector.m

Index: test/SemaObjC/unsafe-perform-selector.m
===
--- /dev/null
+++ test/SemaObjC/unsafe-perform-selector.m
@@ -0,0 +1,127 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -verify %s
+// rdar://12056271
+
+@class Thread;
+
+__attribute__((objc_root_class))
+@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;
+
+@end
+
+typedef struct { int x; int y; int width; int height; } Rectangle;
+
+struct Struct { Rectangle r; };
+
+typedef union { int x; float f; } Union;
+
+@interface Base : NSObject
+
+- (struct Struct)returnsStruct2; // expected-note {{method 'returnsStruct2' that returns 'struct Struct' declared here}}
+- (Union)returnsId;
+
+@end
+
+@protocol IP
+
+- (Union)returnsUnion; // expected-note 2 {{method 'returnsUnion' that returns 'Union' declared here}}
+
+@end
+
+typedef __attribute__((__ext_vector_type__(3))) float float3;
+typedef int int4 __attribute__ ((vector_size (16)));
+
+@interface I : Base
+
+- (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
+
+@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)];
+}
+
+@interface SubClass: I
+
+@end
+
+@interface SubClass ()
+- (struct Struct)returnsSubStructExt; // expected-note {{method 'returnsSubStructExt' that returns 'struct Struct' declared here}} expected-note {{method 'returnsSubStructExt' declared here}}
+@end
+
+@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 

[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-03-03 Thread Alex Lorenz via Phabricator via cfe-commits
arphaman marked 3 inline comments as done.
arphaman added inline comments.



Comment at: lib/AST/DeclObjC.cpp:987
   unsigned noParams = param_size();
   if (noParams < 1 || noParams > 3)
 family = OMF_None;

ahatanak wrote:
> It seems like this code would set "family" to OMF_None for some of the 
> performSelector functions. For example:
> 
> https://developer.apple.com/reference/objectivec/nsobject/1411637-performselectoronmainthread?language=objc
> https://developer.apple.com/reference/objectivec/nsobject/1417922-performselector?language=objc
> 
> Do those functions belong to the performSelector family of methods?
Good catch! Yes, we should be more inclusive here.



Comment at: lib/Sema/SemaExprObjC.cpp:2280
+ImpliedMethod =
+OPT->getInterfaceDecl()->lookupInstanceMethod(SE->getSelector());
+  } else {

ahatanak wrote:
> Do you need to check if OPT->getInterfaceDecl() returns null here? What 
> happens if OPT is id?
You're right, my mistake.



Comment at: lib/Sema/SemaExprObjC.cpp:2499
   checkCocoaAPI(*this, Result);
+if (Method)
+  checkFoundationAPI(*this, SelLoc, Method, makeArrayRef(Args, NumArgs),

ahatanak wrote:
> I'm not sure why checkFoundationAPI has to be called inside the else 
> statement. Was there a reason you didn't or couldn't move it outside?
We should check for 'super' calls as wells, you're right.


Repository:
  rL LLVM

https://reviews.llvm.org/D30174



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-02-21 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak added a comment.

I'm not sure how common it is to pass a function that doesn't return an object 
or void, I think it's OK to allow returning primitive types if you think being 
too strict would catch too many false positives.




Comment at: lib/AST/DeclObjC.cpp:987
   unsigned noParams = param_size();
   if (noParams < 1 || noParams > 3)
 family = OMF_None;

It seems like this code would set "family" to OMF_None for some of the 
performSelector functions. For example:

https://developer.apple.com/reference/objectivec/nsobject/1411637-performselectoronmainthread?language=objc
https://developer.apple.com/reference/objectivec/nsobject/1417922-performselector?language=objc

Do those functions belong to the performSelector family of methods?



Comment at: lib/Sema/SemaExprObjC.cpp:2280
+ImpliedMethod =
+OPT->getInterfaceDecl()->lookupInstanceMethod(SE->getSelector());
+  } else {

Do you need to check if OPT->getInterfaceDecl() returns null here? What happens 
if OPT is id?



Comment at: lib/Sema/SemaExprObjC.cpp:2499
   checkCocoaAPI(*this, Result);
+if (Method)
+  checkFoundationAPI(*this, SelLoc, Method, makeArrayRef(Args, NumArgs),

I'm not sure why checkFoundationAPI has to be called inside the else statement. 
Was there a reason you didn't or couldn't move it outside?


Repository:
  rL LLVM

https://reviews.llvm.org/D30174



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-02-21 Thread Alex Lorenz via Phabricator via cfe-commits
arphaman added a comment.

In https://reviews.llvm.org/D30174#681890, @ahatanak wrote:

> In https://reviews.llvm.org/D30174#681843, @arphaman wrote:
>
> > Yes, we do. Primarily for the following reason: even if some target may 
> > return a struct in a register, another target isn't guaranteed to do the 
> > same thing. It's better to always warn about this rather than accept some 
> > small structs. Furthermore, the official documentation states that "For 
> > methods that return anything other than an object, use NSInvocation." [1], 
> > so methods that return records are completely prohibited by the docs.
>
>
> OK, I think you are right. It doesn't seem like a good idea to warn when 
> compiling for one target and not warn when compiling for another target.
>
> If the official documentation says the method should return an object, why 
> not prohibit all methods that don't return a pointer to an object (methods 
> like returnsInt in the test case)? Doing so will also take care of methods 
> that don't return struct but still return via sret (for example, I think some 
> targets return vectors via sret).


I think it would catch too many "false positives" if we prohibit all primitive 
types. This might make the warning less effective as well, since users might 
get annoyed and completely disable the warning if they think that it's too 
strict. Maybe we can have a stricter version of the warning that's not on by 
default? I agree that we can warn on vectors as well though.

> Also, the documentation says that methods passed to 
> performSelectorInBackground and performSelectorOnMainThread should not have a 
> significant return value. Does it mean that the methods can have any return 
> type but the return value will be ignored (I noticed that those two methods 
> return "void" unlike performSelector which returns "id")?

No, they still use objc_msgSend, so the memory corruption problem still applies 
to them as they can write to the object thinking that it's the pointer to the 
return value. The fact that `performSelectorInBackground` and 
`performSelectorOnMainThread` don't return anything doesn't really make a 
difference.


Repository:
  rL LLVM

https://reviews.llvm.org/D30174



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-02-20 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak added a comment.

In https://reviews.llvm.org/D30174#681843, @arphaman wrote:

> Yes, we do. Primarily for the following reason: even if some target may 
> return a struct in a register, another target isn't guaranteed to do the same 
> thing. It's better to always warn about this rather than accept some small 
> structs. Furthermore, the official documentation states that "For methods 
> that return anything other than an object, use NSInvocation." [1], so methods 
> that return records are completely prohibited by the docs.


OK, I think you are right. It doesn't seem like a good idea to warn when 
compiling for one target and not warn when compiling for another target.

If the official documentation says the method should return an object, why not 
prohibit all methods that don't return a pointer to an object (methods like 
returnsInt in the test case)? Doing so will also take care of methods that 
don't return struct but still return via sret (for example, I think some 
targets return vectors via sret).

Also, the documentation says that methods passed to performSelectorInBackground 
and performSelectorOnMainThread should not have a significant return value. 
Does it mean that the methods can have any return type but the return value 
will be ignored (I noticed that those two methods return "void" unlike 
performSelector which returns "id")?


Repository:
  rL LLVM

https://reviews.llvm.org/D30174



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-02-20 Thread Alex Lorenz via Phabricator via cfe-commits
arphaman added a comment.

In https://reviews.llvm.org/D30174#681801, @ahatanak wrote:

> Do we still issue a warning even when the struct can be returned in a 
> register? For example, x86 can return a small struct (for example, a struct 
> with one int field) in a single register, in which case it's fine to pass it 
> to performSelector via @selector.


Yes, we do. Primarily for the following reason: even if some target may return 
a struct in a register, another target isn't guaranteed to do the same thing. 
It's better to always warn about this rather than accept some small structs. 
Furthermore, the official documentation states that "For methods that return 
anything other than an object, use NSInvocation." [1], so methods that return 
records are completely prohibited by the docs.

> If we should warn only when the method has to return via sret, then it looks 
> like we have to delay issuing the warning until we know where the return 
> value goes (IRGen?).

[1] 
https://developer.apple.com/reference/objectivec/1418956-nsobject/1418867-performselector?language=objc


Repository:
  rL LLVM

https://reviews.llvm.org/D30174



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-02-20 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak added a comment.

Do we still issue a warning even when the struct can be returned in a register? 
For example, x86 can return a small struct (for example, a struct with one int 
field) in a single register, in which case it's fine to pass it to 
performSelector via @selector.

If we should warn only when the method has to return via sret, then it looks 
like we have to delay issuing the warning until we know where the return value 
goes (IRGen?).


Repository:
  rL LLVM

https://reviews.llvm.org/D30174



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D30174: [Sema][ObjC] Warn about 'performSelector' calls with selectors that return record types

2017-02-20 Thread Alex Lorenz via Phabricator via cfe-commits
arphaman created this revision.

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 [1]. 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" [2]. This patch adds a new warning that warns when a selector which 
corresponds to a method that returns a record type is passed into 
`performSelector`.

[1] 
http://www.tomdalling.com/blog/cocoa/why-performselector-is-more-dangerous-than-i-thought/
[2] 
https://developer.apple.com/reference/objectivec/nsobject/1416176-performselector?language=objc


Repository:
  rL LLVM

https://reviews.llvm.org/D30174

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/AST/DeclObjC.cpp
  lib/Basic/IdentifierTable.cpp
  lib/Sema/SemaExprObjC.cpp
  test/SemaObjC/unsafe-perform-selector.m

Index: test/SemaObjC/unsafe-perform-selector.m
===
--- /dev/null
+++ test/SemaObjC/unsafe-perform-selector.m
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -verify %s
+// rdar://12056271
+
+__attribute__((objc_root_class))
+@interface NSObject
+
+- (id)performSelector:(SEL)sel;
+- (void)performSelectorInBackground:(SEL)sel withObject:(id)arg;
+- (void)performSelectorOnMainThread:(SEL)sel;
+
+@end
+
+typedef struct { int x; int y; int width; int height; } Rectangle;
+
+struct Struct { Rectangle r; };
+
+typedef union { int x; float f; } Union;
+
+@interface Base : NSObject
+
+- (struct Struct)returnsStruct2; // expected-note {{method 'returnsStruct2' that returns 'struct Struct' declared here}}
+- (Union)returnsId;
+
+@end
+
+@protocol IP
+
+- (Union)returnsUnion; // expected-note {{method 'returnsUnion' that returns 'Union' declared here}}
+
+@end
+
+@interface I : Base
+
+- (Rectangle)returnsStruct; // expected-note 2 {{method 'returnsStruct' that returns 'Rectangle' declared here}}
+- (id)returnsId; // shadows base 'returnsId'
+- (int)returnsInt;
+- (I *)returnPtr;
+
++ (Rectangle)returnsStructClass; // expected-note {{method 'returnsStructClass' that returns 'Rectangle' declared here}}
++ (void)returnsUnion; // Not really
+
+@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
+}
Index: lib/Sema/SemaExprObjC.cpp
===
--- lib/Sema/SemaExprObjC.cpp
+++ lib/Sema/SemaExprObjC.cpp
@@ -2260,6 +2260,41 @@
  edit::rewriteObjCRedundantCallWithLiteral);
 }
 
+static void checkFoundationAPI(Sema , SourceLocation Loc,
+   const ObjCMethodDecl *Method,
+   ArrayRef Args, QualType ReceiverType,
+   bool IsInstanceMethod) {
+  // Check if this is a performSelector method that uses a selector that returns
+  // a record type.
+  if (Method->getMethodFamily() != OMF_performSelector || Args.empty())
+return;
+  const auto *SE = dyn_cast(Args[0]->IgnoreParens());
+  if (!SE)
+return;
+  ObjCMethodDecl *ImpliedMethod;
+  if (IsInstanceMethod) {
+const auto *OPT = ReceiverType->getAs();
+if (!OPT)
+  return;
+ImpliedMethod =
+OPT->getInterfaceDecl()->lookupInstanceMethod(SE->getSelector());
+  } else {
+const auto *IT = ReceiverType->getAs();
+if (!IT)
+  return;
+ImpliedMethod = IT->getDecl()->lookupClassMethod(SE->getSelector());
+  }
+  if (ImpliedMethod && ImpliedMethod->getReturnType()->isRecordType()) {
+QualType Ret = ImpliedMethod->getReturnType();
+S.Diag(Loc, diag::warn_objc_unsafe_perform_selector)
+<< Method->getSelector()
+<< (Ret->isUnionType() ? /*Union*/ 1 : /*Struct*/ 0);
+