Skip to content

[clang][ObjC] Allow different availability annotation on a method #2976

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3135,6 +3135,9 @@ class Sema final {
/// Merge availability attributes for an implementation of
/// a protocol requirement.
AMK_ProtocolImplementation,
/// Merge availability attributes for an implementation of
/// an optional protocol requirement.
AMK_OptionalProtocolImplementation
};

/// Describes the kind of priority given to an availability attribute.
Expand Down
13 changes: 8 additions & 5 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2602,7 +2602,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
NewAttr = nullptr;
else if ((isa<DeprecatedAttr>(Attr) || isa<UnavailableAttr>(Attr)) &&
(AMK == Sema::AMK_Override ||
AMK == Sema::AMK_ProtocolImplementation))
AMK == Sema::AMK_ProtocolImplementation ||
AMK == Sema::AMK_OptionalProtocolImplementation))
NewAttr = nullptr;
else if (isa<SwiftPrivateAttr>(Attr) && AMK == Sema::AMK_Override)
NewAttr = nullptr;
Expand Down Expand Up @@ -2951,6 +2952,7 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
case AMK_Redeclaration:
case AMK_Override:
case AMK_ProtocolImplementation:
case AMK_OptionalProtocolImplementation:
LocalAMK = AMK;
break;
}
Expand Down Expand Up @@ -3852,10 +3854,11 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
ObjCMethodDecl *oldMethod) {
// Merge the attributes, including deprecated/unavailable
AvailabilityMergeKind MergeKind =
isa<ObjCProtocolDecl>(oldMethod->getDeclContext())
? AMK_ProtocolImplementation
: isa<ObjCImplDecl>(newMethod->getDeclContext()) ? AMK_Redeclaration
: AMK_Override;
isa<ObjCProtocolDecl>(oldMethod->getDeclContext())
? (oldMethod->isOptional() ? AMK_OptionalProtocolImplementation
: AMK_ProtocolImplementation)
: isa<ObjCImplDecl>(newMethod->getDeclContext()) ? AMK_Redeclaration
: AMK_Override;

mergeDeclAttributes(newMethod, oldMethod, MergeKind);

Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2358,6 +2358,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(

case AMK_Override:
case AMK_ProtocolImplementation:
case AMK_OptionalProtocolImplementation:
OverrideOrImpl = true;
break;
}
Expand Down Expand Up @@ -2426,6 +2427,14 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
diag::warn_mismatched_availability_override_unavail)
<< AvailabilityAttr::getPrettyPlatformName(Platform->getName())
<< (AMK == AMK_Override);
} else if (Which != 1 && AMK == AMK_OptionalProtocolImplementation) {
// Allow different 'introduced' / 'obsoleted' availability versions
// on a method that implements an optional protocol requirement. It
// makes less sense to allow this for 'deprecated' as the user can't
// see if the method is 'deprecated' as 'respondsToSelector' will
// still return true when the method is deprecated.
++i;
continue;
} else {
Diag(OldAA->getLocation(),
diag::warn_mismatched_availability_override)
Expand Down
7 changes: 3 additions & 4 deletions clang/test/SemaObjC/attr-availability.m
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,7 @@ void use_myEnum() {
// inherited be implementations of those protocol methods.
@protocol AvailabilityP2
@optional
-(void)methodA __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note 4{{'methodA' has been explicitly marked deprecated here}} \
// expected-note 2{{protocol method is here}}
-(void)methodA __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note 4{{'methodA' has been explicitly marked deprecated here}}
-(void)methodB __attribute__((unavailable)); // expected-note 4{{'methodB' has been explicitly marked unavailable here}}
-(void)methodC;
@end
Expand Down Expand Up @@ -279,7 +278,7 @@ void testImplementsAvailabilityP2b(ImplementsAvailabilityP2b *obj) {

__attribute__((objc_root_class))
@interface ImplementsAvailabilityP2c <AvailabilityP2>
-(void)methodA __attribute__((availability(macosx,introduced=10.2))); // expected-warning{{method introduced after the protocol method it implements on macOS (10.2 vs. 10.1)}}
-(void)methodA __attribute__((availability(macosx,introduced=10.2)));
-(void)methodB __attribute__((unavailable));
@end

Expand All @@ -288,7 +287,7 @@ @interface ImplementsAvailabilityP2d <AvailabilityP2>
@end

@implementation ImplementsAvailabilityP2d
-(void)methodA __attribute__((availability(macosx,introduced=10.2))) // expected-warning{{method introduced after the protocol method it implements on macOS (10.2 vs. 10.1)}}
-(void)methodA __attribute__((availability(macosx,introduced=10.2)))
{
}
-(void)methodB __attribute__((unavailable)) {
Expand Down
47 changes: 47 additions & 0 deletions clang/test/SemaObjC/override-opt-prop-availability.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: %clang_cc1 -triple arm64-apple-ios11 -fsyntax-only -verify %s

@protocol P

@property (nonatomic) int reqProp __attribute__((availability(ios, introduced=12.0))); // expected-note 2 {{is here}}



@optional
@property (nonatomic) int myProp __attribute__((availability(ios, introduced=12.0))); // expected-note {{has been marked as being introduced in}}

@optional
@property (nonatomic, readonly) int depProp __attribute__((availability(ios, introduced=8.0, deprecated=12.0))); // expected-note {{protocol method is here}}

@optional
@property (nonatomic) int obsProp __attribute__((availability(ios, introduced=8.0, obsoleted=12.0)));

@optional
- (void) unavaibleInClass __attribute__((availability(ios, introduced=12.0))); // expected-note {{method is here}}

@end

@interface X <P>

@property (nonatomic) int myProp __attribute__((availability(ios, introduced=13.0))); // expected-note 2 {{has been marked as being introduced in}}

@property (nonatomic) int reqProp __attribute__((availability(ios, introduced=13.0))); // expected-warning 2 {{method introduced after the protocol method it implements on iOS}}

@property (nonatomic, readonly) int depProp __attribute__((availability(ios, introduced=8.0, deprecated=10.0))); // expected-warning {{method deprecated before the protocol method it implements on iOS (12.0 vs. 10.0)}} expected-note {{been explicitly marked deprecated here}}

@property (nonatomic) int obsProp __attribute__((availability(ios, introduced=8.0, obsoleted=10.0))); // expected-note {{been explicitly marked unavailable here}}

- (void) unavaibleInClass __attribute__((availability(ios, unavailable))); // expected-warning {{method cannot be unavailable on iOS when the protocol method it implements is available}}

@end


void test(X *x) {
int i = x.myProp; // expected-warning {{'myProp' is only available on iOS 13.0 or newer}} expected-note {{enclose}}
x.myProp = i; // expected-warning {{'setMyProp:' is only available on iOS 13.0 or newer}} expected-note {{enclose}}
int i2 = x.depProp; // expected-warning {{'depProp' is deprecated: first deprecated in iOS 10.0}}
int i3 = x.obsProp; // expected-error {{'obsProp' is unavailable: obsoleted in iOS 10.0}}
}

void testProto(id<P> x) {
int i = x.myProp; // expected-warning {{'myProp' is only available on iOS 12.0 or newer}} expected-note {{enclose}}
}