arphaman created this revision. Herald added a subscriber: javed.absar. This patch extends the -Wavailability warning to warn about cases where a method declaration is missing an availability attribute clause that's present in the method's definition. We also warn about missing `deprecated` attributes as well.
rdar://15540962 Repository: rL LLVM https://reviews.llvm.org/D39913 Files: include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclAttr.cpp test/SemaObjC/method-attributes.m test/SemaObjC/unguarded-availability.m test/SemaObjC/warn-missing-method-decl-availability.m
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<DontWarnOnProtocol> +@end + +@implementation DontWarnOnProtocolImpl + +- (void)missingIDO +__attribute__((availability(macos, introduced=10.1, deprecated=10.2, obsoleted=10.3))) +{} + +@end 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/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: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -2277,6 +2277,62 @@ return false; } +void Sema::checkMissingAvailabilityClausesInDeclaration(NamedDecl *Original, NamedDecl *Implementation) { + if (!Implementation->hasAttrs()) + return; + + auto CheckIfMissing = [&](const IdentifierInfo *Platform, llvm::function_ref<bool (const AvailabilityAttr &)> Callback) -> bool { + for (const auto *A : Original->attrs()) { + if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) { + if (Availability->getPlatform() != Platform) + continue; + if (Callback(*Availability)) + return false; + } + } + return true; + }; + auto CheckIfVersionClauseMissing = [&](VersionTuple Version, const IdentifierInfo *Platform, llvm::function_ref<bool (const AvailabilityAttr &)> Callback) -> bool { + if (Version.empty()) + return false; + return CheckIfMissing(Platform, Callback); + }; + + llvm::SmallPtrSet<const IdentifierInfo *, 4> MissingPlatformAttributes; + for (const auto *A : Implementation->attrs()) { + if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) { + const IdentifierInfo *Platform = Availability->getPlatform(); + + bool MissingIntroduced = CheckIfVersionClauseMissing(Availability->getIntroduced(), Platform, [] (const AvailabilityAttr &OriginalAA) -> bool { + return !OriginalAA.getIntroduced().empty(); + }); + bool MissingDeprecated = CheckIfVersionClauseMissing(Availability->getDeprecated(), Platform, [] (const AvailabilityAttr &OriginalAA) -> bool { + return !OriginalAA.getDeprecated().empty(); + }); + bool MissingObsoleted = CheckIfVersionClauseMissing(Availability->getObsoleted(), Platform, [] (const AvailabilityAttr &OriginalAA) -> bool { + return !OriginalAA.getObsoleted().empty(); + }); + bool MissingUnavailable = Availability->getUnavailable() && CheckIfMissing(Platform, [] (const AvailabilityAttr &OriginalAA) -> bool { + return OriginalAA.getUnavailable(); + }); + + // Warn once about missing attribute for each platform. + if ((MissingIntroduced || MissingDeprecated || MissingObsoleted || MissingUnavailable) && + MissingPlatformAttributes.count(Platform) == 0) { + MissingPlatformAttributes.insert(Platform); + StringRef PlatformSpelling = AvailabilityAttr::getPrettyPlatformName(Platform->getName()); + Diag(Original->getLocation(), diag::warn_availability_on_implementation_not_interface) + << PlatformSpelling; + Diag(Availability->getLocation(), diag::note_definition_with_availability_here) + << PlatformSpelling; + } + } else if (isa<DeprecatedAttr>(A) && !Original->hasAttr<DeprecatedAttr>()) { + Diag(Original->getLocation(), diag::warn_deprecated_on_implementation_not_interface); + Diag(A->getLocation(), diag::note_definition_with_deprecated_here); + } + } +} + AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, IdentifierInfo *Platform, bool Implicit, Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -3572,6 +3572,17 @@ : isa<ObjCImplDecl>(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<ObjCImplDecl>(newMethod->getDeclContext())) { + const Decl *DC = cast<Decl>(oldMethod->getDeclContext()); + if (Imp->getClassInterface() == DC || + (isa<ObjCCategoryDecl>(DC) && cast<ObjCCategoryDecl>(DC)->IsClassExtension() && + Imp->getClassInterface() == cast<ObjCCategoryDecl>(DC)->getClassInterface()) || + (isa<ObjCCategoryImplDecl>(Imp) && + cast<ObjCCategoryImplDecl>(Imp)->getCategoryDecl() == DC)) + checkMissingAvailabilityClausesInDeclaration(oldMethod, newMethod); + } mergeDeclAttributes(newMethod, oldMethod, MergeKind); // Merge attributes from the parameters. Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2399,6 +2399,12 @@ 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: 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<Availability>; +def warn_availability_on_implementation_not_interface : Warning< + "method declaration is missing an availability attribute for " + "%0 that is specified in the definition">, + InGroup<Availability>; +def warn_deprecated_on_implementation_not_interface : Warning< + "method declaration is missing a deprecated attribute that is specified in " + "the definition">, + InGroup<Availability>; +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<
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits