Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2917,6 +2917,18 @@ "%select{the protocol method it implements|its overridden method}1 is " "available">, InGroup; +def warn_availability_on_implementation_not_interface : Warning< + "method declaration is missing an availability attribute for " + "%0 that is specified in the definition">, + InGroup; +def warn_deprecated_on_implementation_not_interface : Warning< + "method declaration is missing a deprecated attribute that is specified in " + "the definition">, + InGroup; +def note_definition_with_availability_here : Note< + "definition with %0 availability attribute is here">; +def note_definition_with_deprecated_here : Note< + "definition with deprecated attribute is here">; def note_overridden_method : Note< "overridden method is here">; def note_protocol_method : Note< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2399,6 +2399,13 @@ AMK_ProtocolImplementation, }; + /// Verifies that availability attribute clauses that are specified on an + /// method definition match the clauses from the method's declaration. + /// + /// This will warn on any missing clauses in the declaration. + void checkMissingAvailabilityClausesInDeclaration(NamedDecl *Original, + NamedDecl *Implementation); + /// Attribute merging methods. Return true if a new attribute was added. AvailabilityAttr *mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, IdentifierInfo *Platform, Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -3572,6 +3572,20 @@ : isa(newMethod->getDeclContext()) ? AMK_Redeclaration : AMK_Override; + // Warn on any availability clauses that are missing in the method's + // declaration but specified in the definition. + if (const ObjCImplDecl *Imp = + dyn_cast(newMethod->getDeclContext())) { + const Decl *DC = cast(oldMethod->getDeclContext()); + if (Imp->getClassInterface() == DC || + (isa(DC) && + cast(DC)->IsClassExtension() && + Imp->getClassInterface() == + cast(DC)->getClassInterface()) || + (isa(Imp) && + cast(Imp)->getCategoryDecl() == DC)) + checkMissingAvailabilityClausesInDeclaration(oldMethod, newMethod); + } mergeDeclAttributes(newMethod, oldMethod, MergeKind); // Merge attributes from the parameters. Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -2277,6 +2277,50 @@ return false; } +void Sema::checkMissingAvailabilityClausesInDeclaration( + NamedDecl *Original, NamedDecl *Implementation) { + if (!Implementation->hasAttrs()) + return; + + llvm::SmallPtrSet MissingPlatformAttributes; + for (auto *Def : Implementation->specific_attrs()) { + bool MissingIntroduced = !Def->getIntroduced().empty(); + bool MissingDeprecated = !Def->getDeprecated().empty(); + bool MissingObsoleted = !Def->getObsoleted().empty(); + bool MissingUnavailable = Def->getUnavailable(); + for (auto *Decl : Original->specific_attrs()) { + if (Def->getPlatform() != Decl->getPlatform()) + continue; + MissingIntroduced = + MissingIntroduced ? Decl->getIntroduced().empty() : false; + MissingDeprecated = + MissingDeprecated ? Decl->getDeprecated().empty() : false; + MissingObsoleted = + MissingObsoleted ? Decl->getObsoleted().empty() : false; + MissingUnavailable = MissingUnavailable ? !Decl->getUnavailable() : false; + } + if (MissingIntroduced || MissingDeprecated || MissingObsoleted || + MissingUnavailable) { + if (!MissingPlatformAttributes.insert(Def->getPlatform()).second) + continue; + StringRef PlatformSpelling = AvailabilityAttr::getPrettyPlatformName( + Def->getPlatform()->getName()); + Diag(Original->getLocation(), + diag::warn_availability_on_implementation_not_interface) + << PlatformSpelling; + Diag(Def->getLocation(), diag::note_definition_with_availability_here) + << PlatformSpelling; + } + } + if (const auto *Dep = Implementation->getAttr()) { + if (!Original->hasAttr()) { + Diag(Original->getLocation(), + diag::warn_deprecated_on_implementation_not_interface); + Diag(Dep->getLocation(), diag::note_definition_with_deprecated_here); + } + } +} + AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, IdentifierInfo *Platform, bool Implicit, Index: test/SemaObjC/method-attributes.m =================================================================== --- test/SemaObjC/method-attributes.m +++ test/SemaObjC/method-attributes.m @@ -15,14 +15,14 @@ @interface INTF - (int) foo1: (int)arg1 __attribute__((deprecated)); -- (int) foo: (int)arg1; +- (int) foo: (int)arg1; // expected-warning {{method declaration is missing a deprecated attribute that is specified in the definition}} - (int) foo2: (int)arg1 __attribute__((deprecated)) __attribute__((unavailable)); - (int) foo3: (int)arg1 __attribute__((deprecated)) __attribute__((unavailable)) __attribute__((ns_consumes_self)); @end @implementation INTF -- (int) foo: (int)arg1 __attribute__((deprecated)){ +- (int) foo: (int)arg1 __attribute__((deprecated)){ // expected-note {{definition with deprecated attribute is here}} return 10; } - (int) foo1: (int)arg1 { Index: test/SemaObjC/unguarded-availability.m =================================================================== --- test/SemaObjC/unguarded-availability.m +++ test/SemaObjC/unguarded-availability.m @@ -77,7 +77,7 @@ int_10_12 bar; // expected-warning {{'int_10_12' is only available on macOS 10.12 or newer}} } - (void)method1; -- (void)method2; +- (void)method2 AVAILABLE_10_12; @end @implementation Class_10_11 Index: test/SemaObjC/warn-missing-method-decl-availability.m =================================================================== --- /dev/null +++ test/SemaObjC/warn-missing-method-decl-availability.m @@ -0,0 +1,139 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9.0.0 -fsyntax-only -verify -Wno-objc-root-class %s + +// Warn about availability attribute when they're specified in the definition +// of the method instead of the declaration. +// rdar://15540962 + +@interface MissingAvailabilityThingsInInterface + +- (void)missingIDO; // expected-warning {{method declaration is missing an availability attribute for macOS that is specified in the definition}} + +- (void)missingDO __attribute__((availability(macos, introduced=10.1))); // expected-warning {{method declaration is missing an availability attribute for macOS that is specified in the definition}} + +- (void)missingD __attribute__((availability(macos, introduced=10.1))); // expected-warning {{method declaration is missing an availability attribute for macOS that is specified in the definition}} + +- (void)missingIx2; // expected-warning {{method declaration is missing an availability attribute for macOS that is specified in the definition}} +// expected-warning@-1 {{method declaration is missing an availability attribute for iOS that is specified in the definition}} + +- (void)missingIDOiOS __attribute__((availability(macos, introduced=10.1))); // expected-warning {{method declaration is missing an availability attribute for iOS that is specified in the definition}} + +- (void)differentIMissingD __attribute__((availability(macos, introduced=10.1))); // expected-warning {{method declaration is missing an availability attribute for macOS that is specified in the definition}} +// expected-note@-1 {{previous attribute is here}} + +- (void)missingUnavailable; // expected-warning {{method declaration is missing an availability attribute for macOS that is specified in the definition}} + +- (void)same +__attribute__((availability(macos, introduced=10.1))) +__attribute__((availability(ios, unavailable))); + +- (void)missingDeprecatedAttr; // expected-warning {{method declaration is missing a deprecated attribute that is specified in the definition}} + +- (void)sameDeprecatedAttr __attribute__((deprecated("y"))); + +@end + +@interface MissingAvailabilityThingsInInterface() + +- (void)missingInClassExtension; // expected-warning {{method declaration is missing an availability attribute for macOS that is specified in the definition}} + +@end + +@implementation MissingAvailabilityThingsInInterface + +- (void)missingIDO +__attribute__((availability(macos, introduced=10.1, deprecated=10.2, obsoleted=10.3))) // expected-note {{definition with macOS availability attribute is here}} +{ } + +- (void)missingDO +__attribute__((availability(macos, introduced=10.1))) +__attribute__((availability(macos, deprecated=10.2, obsoleted=10.3))) // expected-note {{definition with macOS availability attribute is here}} +{ } + +- (void)missingD +__attribute__((availability(macos, introduced=10.1, deprecated=10.2))) // expected-note {{definition with macOS availability attribute is here}} +{ } + +- (void)missingIx2 +__attribute__((availability(ios, introduced=10))) // expected-note {{definition with iOS availability attribute is here}} +__attribute__((availability(macos, introduced=10.1))) // expected-note {{definition with macOS availability attribute is here}} +{ } + +- (void)missingIDOiOS +__attribute__((availability(ios, introduced=10, deprecated=11, obsoleted=11.1))) // expected-note {{definition with iOS availability attribute is here}} +__attribute__((availability(macOS, introduced=10.1))) +{ } + +- (void)differentIMissingD __attribute__((availability(macos, introduced=10.2, deprecated=10.3))) // expected-note {{definition with macOS availability attribute is here}} +{ } // expected-warning@-1{{availability does not match previous declaration}} + +- (void)missingInClassExtension +__attribute__((availability(macos, introduced=10.1, deprecated=10.2))) // expected-note {{definition with macOS availability attribute is here}} +{ } + +- (void)missingUnavailable +__attribute__((availability(macos, unavailable))); // expected-note {{definition with macOS availability attribute is here}} +{ } + +- (void)same +__attribute__((availability(macos, introduced=10.1))) +__attribute__((availability(ios, unavailable))) +{ } + +- (void)missingDeprecatedAttr +__attribute__((deprecated("x"))) // expected-note {{definition with deprecated attribute is here}} +{ } + +- (void)sameDeprecatedAttr __attribute__((deprecated("y"))) +{ } + +@end + +@interface MissingAvailabilityThingsInInterface (Category) + +- (void)missingInCategory; // expected-warning {{method declaration is missing an availability attribute for tvOS that is specified in the definition}} + +@end + +@implementation MissingAvailabilityThingsInInterface (Category) + +- (void)missingInCategory +__attribute__((availability(tvOS, introduced=10))) // expected-note {{definition with tvOS availability attribute is here}} +{ } + +@end + +@interface DontWarnOnOverrideImpl + +- (void)missingIDO +__attribute__((availability(macos, introduced=10.1, deprecated=10.2, obsoleted=10.3))); // ok + +@end + +@implementation DontWarnOnOverrideImpl + +- (void)missingIDO { } + +// ok +- (void)missingDO +__attribute__((availability(macos, introduced=10.1))) +__attribute__((availability(macos, deprecated=10.2, obsoleted=10.3))) +{ } + +@end + +@protocol DontWarnOnProtocol + +- (void)missingIDO; + +@end + +@interface DontWarnOnProtocolImpl +@end + +@implementation DontWarnOnProtocolImpl + +- (void)missingIDO +__attribute__((availability(macos, introduced=10.1, deprecated=10.2, obsoleted=10.3))) +{} + +@end