Skip to content

Commit e4219c8

Browse files
committed
Add RX_CATCH_ERROR macro to work around implicit nullable state of NSError parameters in Objective-C methods
1 parent c50efe3 commit e4219c8

File tree

2 files changed

+38
-33
lines changed

2 files changed

+38
-33
lines changed

RxCocoa/Common/_RX.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@
3232
#define CLASS_VALUE(x) [NSValue valueWithNonretainedObject:(x)]
3333
#define IMP_VALUE(x) [NSValue valueWithPointer:(x)]
3434

35+
/**
36+
Checks that the local `error` instance exists before assigning it's value by reference.
37+
This macro exists to work around static analysis warnings — `NSError` is always assumed to be `nullable`, even though we explictly define the method parameter as `nonnull`. See http://www.openradar.me/21766176 for more details.
38+
*/
39+
#define RX_SAFE_ERROR(errorValue) if (error != nil) { *error = (errorValue); }
40+
3541
// Inspired by http://p99.gforge.inria.fr
3642

3743
// https://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC26

RxCocoa/Common/_RXObjCRuntime.m

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -596,19 +596,19 @@ +(void)registerOptimizedObserver:(RXInterceptWithOptimizedObserver)registration
596596
-(IMP __nullable)ensurePrepared:(id __nonnull)target forObserving:(SEL __nonnull)selector error:(NSError** __nonnull)error {
597597
Method instanceMethod = class_getInstanceMethod([target class], selector);
598598
if (instanceMethod == nil) {
599-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
600-
code:RXObjCRuntimeErrorSelectorNotImplemented
601-
userInfo:nil];
599+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
600+
code:RXObjCRuntimeErrorSelectorNotImplemented
601+
userInfo:nil]);
602602
return nil;
603603
}
604604

605605
if (selector == @selector(class)
606606
|| selector == @selector(forwardingTargetForSelector:)
607607
|| selector == @selector(methodSignatureForSelector:)
608608
|| selector == @selector(respondsToSelector:)) {
609-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
610-
code:RXObjCRuntimeErrorObservingPerformanceSensitiveMessages
611-
userInfo:nil];
609+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
610+
code:RXObjCRuntimeErrorObservingPerformanceSensitiveMessages
611+
userInfo:nil]);
612612
return nil;
613613
}
614614

@@ -644,9 +644,9 @@ -(IMP __nullable)ensurePrepared:(id __nonnull)target forObserving:(SEL __nonnull
644644
RXInterceptWithOptimizedObserver optimizedIntercept = optimizedObserversByMethodEncoding[methodEncoding];
645645

646646
if (!RX_method_has_supported_return_type(instanceMethod)) {
647-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
648-
code:RXObjCRuntimeErrorObservingMessagesWithUnsupportedReturnType
649-
userInfo:nil];
647+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
648+
code:RXObjCRuntimeErrorObservingMessagesWithUnsupportedReturnType
649+
userInfo:nil]);
650650

651651
return nil;
652652
}
@@ -686,10 +686,9 @@ -(IMP __nullable)ensurePrepared:(id __nonnull)target forObserving:(SEL __nonnull
686686
}
687687
}
688688

689-
690-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
691-
code:RXObjCRuntimeErrorUnknown
692-
userInfo:nil];
689+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
690+
code:RXObjCRuntimeErrorUnknown
691+
userInfo:nil]);
693692

694693
return nil;
695694
}
@@ -711,9 +710,9 @@ -(Class __nullable)prepareTargetClassForObserving:(id __nonnull)target error:(NS
711710
BOOL isThisTollFreeFoundationClass = CFGetTypeID((CFTypeRef)target) != defaultTypeID;
712711

713712
if (isThisTollFreeFoundationClass) {
714-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
715-
code:RXObjCRuntimeErrorCantInterceptCoreFoundationTollFreeBridgedObjects
716-
userInfo:nil];
713+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
714+
code:RXObjCRuntimeErrorCantInterceptCoreFoundationTollFreeBridgedObjects
715+
userInfo:nil]);
717716
return nil;
718717
}
719718

@@ -745,11 +744,11 @@ Most common case when this would happen is when using KVO (`rx_observe`) and `rx
745744
if ([target class] != object_getClass(target)) {
746745
BOOL isKVO = [target respondsToSelector:NSSelectorFromString(@"_isKVOA")];
747746

748-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
749-
code:RXObjCRuntimeErrorObjectMessagesAlreadyBeingIntercepted
750-
userInfo:@{
751-
RXObjCRuntimeErrorIsKVOKey: @(isKVO)
752-
}];
747+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
748+
code:RXObjCRuntimeErrorObjectMessagesAlreadyBeingIntercepted
749+
userInfo:@{
750+
RXObjCRuntimeErrorIsKVOKey : @(isKVO)
751+
}]);
753752
return nil;
754753
}
755754

@@ -761,9 +760,9 @@ Most common case when this would happen is when using KVO (`rx_observe`) and `rx
761760

762761
Class previousClass = object_setClass(target, dynamicFakeSubclass);
763762
if (previousClass != wannaBeClass) {
764-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
765-
code:RXObjCRuntimeErrorThreadingCollisionWithOtherInterceptionMechanism
766-
userInfo:nil];
763+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
764+
code:RXObjCRuntimeErrorThreadingCollisionWithOtherInterceptionMechanism
765+
userInfo:nil]);
767766
THREADING_HAZARD(wannaBeClass);
768767
return nil;
769768
}
@@ -816,25 +815,25 @@ -(BOOL)observeByForwardingMessages:(Class __nonnull)swizzlingImplementorClass
816815
IMP implementation = method_getImplementation(instanceMethod);
817816

818817
if (implementation == nil) {
819-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
820-
code:RXObjCRuntimeErrorSelectorNotImplemented
821-
userInfo:nil];
818+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
819+
code:RXObjCRuntimeErrorSelectorNotImplemented
820+
userInfo:nil]);
822821

823822
return NO;
824823
}
825824

826825
if (!class_addMethod(swizzlingImplementorClass, rxSelector, implementation, methodEncoding)) {
827-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
828-
code:RXObjCRuntimeErrorSavingOriginalForwardingMethodFailed
829-
userInfo:nil];
826+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
827+
code:RXObjCRuntimeErrorSavingOriginalForwardingMethodFailed
828+
userInfo:nil]);
830829
return NO;
831830
}
832831

833832
if (!class_addMethod(swizzlingImplementorClass, selector, _objc_msgForward, methodEncoding)) {
834833
if (implementation != method_setImplementation(instanceMethod, _objc_msgForward)) {
835-
*error = [NSError errorWithDomain:RXObjCRuntimeErrorDomain
836-
code:RXObjCRuntimeErrorReplacingMethodWithForwardingImplementation
837-
userInfo:nil];
834+
RX_SAFE_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
835+
code:RXObjCRuntimeErrorReplacingMethodWithForwardingImplementation
836+
userInfo:nil]);
838837
THREADING_HAZARD(swizzlingImplementorClass);
839838
return NO;
840839
}

0 commit comments

Comments
 (0)