Search code examples
objective-ccastingtype-conversionclang

Clang: promoting warning for type-casting like id<protocol> to class<Protocol>?


I'm doing some code refactoring but facing a problem that clang has no any warning for this situation:

@protocol CommonProtocol <NSObject>
- (void)common;
@end
=========
@interface Foo : NSObject <CommonProtocol>
- (void)foo;
@end
=========
@interface Bar : NSObject <CommonProtocol>
@end
=========
static id<CommonProtocol> getObject(void) {
    return [Bar new];
}

Foo *casting = getObject(); /// ⚠️ there is no warning for this
[casting common];
[casting foo]; /// 💥 crash because the casting object do not respond to '-foo'

I think type 'Foo *' and 'id<CommondProtocol>' are not interchangeable and looking a way to promote this warning. Any compile flag to achieve this?

NOTE: I have try '-Weverything' and it still present no warning for conversion.


Solution

  • This is a natural consequence of the weak type system of Objective-C, where id can be virtually downcasted to any kind of object. Specializing id with a given protocol doesn't help much in this direction, this is why you can easily run into unexpected situations, like the current one.

    If you want to force the casting, one solution would be to change the signature of getObject to return NSObject instead of id:

    static NSObject<CommonProtocol> *getObject(void) {
        return [Bar new];
    }
    

    With this setup in place the naive Foo *casting = getObject(); will generate a clear warning:

    Incompatible pointer types initializing 'Foo *' with an expression of type 'NSObject *'

    , and the person writing the code will have to explicitly cast:

    Foo *casting = (Foo *)getObject();