From: Douglas Gregor Date: Tue, 15 Jan 2013 22:43:08 +0000 (+0000) Subject: When checking availability attributes for consistency between an X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f4d918fdf7da64215db8abe63945798c66e6d132;p=clang When checking availability attributes for consistency between an overriding and overridden method, allow the overridden method to have a narrower contract (introduced earlier, deprecated/obsoleted later) than the overriding method. Fixes . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@172567 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 3ea9df4d76..cd00919587 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1879,6 +1879,16 @@ def warn_availability_version_ordering : Warning< "attribute ignored">, InGroup; def warn_mismatched_availability: Warning< "availability does not match previous declaration">, InGroup; +def warn_mismatched_availability_override : Warning< + "overriding method %select{introduced after|" + "deprecated before|obsoleted before}0 overridden method on %1 (%2 vs. %3)">, + InGroup; +def warn_mismatched_availability_override_unavail : Warning< + "overriding method cannot be unavailable on %0 when its overridden method is " + "available">, + InGroup; +def note_overridden_method : Note< + "overridden method is here">; // Thread Safety Attributes def warn_thread_attribute_ignored : Warning< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 591ced11d5..5043fd75f8 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1677,7 +1677,8 @@ public: VersionTuple Deprecated, VersionTuple Obsoleted, bool IsUnavailable, - StringRef Message); + StringRef Message, + bool Override); VisibilityAttr *mergeVisibilityAttr(Decl *D, SourceRange Range, VisibilityAttr::VisibilityType Vis); DLLImportAttr *mergeDLLImportAttr(Decl *D, SourceRange Range); @@ -1685,10 +1686,24 @@ public: FormatAttr *mergeFormatAttr(Decl *D, SourceRange Range, StringRef Format, int FormatIdx, int FirstArg); SectionAttr *mergeSectionAttr(Decl *D, SourceRange Range, StringRef Name); - bool mergeDeclAttribute(NamedDecl *New, InheritableAttr *Attr); + bool mergeDeclAttribute(NamedDecl *New, InheritableAttr *Attr, + bool Override); + + /// \brief Describes the kind of merge to perform for availability + /// attributes (including "deprecated", "unavailable", and "availability"). + enum AvailabilityMergeKind { + /// \brief Don't merge availability attributes at all. + AMK_None, + /// \brief Merge availability attributes for a redeclaration, which requires + /// an exact match. + AMK_Redeclaration, + /// \brief Merge availability attributes for an override, which requires + /// an exact match or a weakening of constraints. + AMK_Override + }; void mergeDeclAttributes(NamedDecl *New, Decl *Old, - bool MergeDeprecation = true); + AvailabilityMergeKind AMK = AMK_Redeclaration); void MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls); bool MergeFunctionDecl(FunctionDecl *New, Decl *Old, Scope *S); bool MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old, @@ -6383,8 +6398,7 @@ public: /// \brief Check whether the given new method is a valid override of the /// given overridden method, and set any properties that should be inherited. void CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, - const ObjCMethodDecl *Overridden, - bool IsImplementation); + const ObjCMethodDecl *Overridden); /// \brief Describes the compatibility of a result type with its method. enum ResultTypeCompatibilityKind { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 537e70bfbe..5d655e24c0 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1822,13 +1822,14 @@ DeclHasAttr(const Decl *D, const Attr *A) { return false; } -bool Sema::mergeDeclAttribute(NamedDecl *D, InheritableAttr *Attr) { +bool Sema::mergeDeclAttribute(NamedDecl *D, InheritableAttr *Attr, + bool Override) { InheritableAttr *NewAttr = NULL; if (AvailabilityAttr *AA = dyn_cast(Attr)) NewAttr = mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), AA->getIntroduced(), AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(), - AA->getMessage()); + AA->getMessage(), Override); else if (VisibilityAttr *VA = dyn_cast(Attr)) NewAttr = mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility()); else if (DLLImportAttr *ImportA = dyn_cast(Attr)) @@ -1902,7 +1903,7 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) { /// mergeDeclAttributes - Copy attributes from the Old decl to the New one. void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, - bool MergeDeprecation) { + AvailabilityMergeKind AMK) { // attributes declared post-definition are currently ignored checkNewAttributesAfterDef(*this, New, Old); @@ -1919,14 +1920,25 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, i = Old->specific_attr_begin(), e = Old->specific_attr_end(); i != e; ++i) { + bool Override = false; // Ignore deprecated/unavailable/availability attributes if requested. - if (!MergeDeprecation && - (isa(*i) || - isa(*i) || - isa(*i))) - continue; + if (isa(*i) || + isa(*i) || + isa(*i)) { + switch (AMK) { + case AMK_None: + continue; + + case AMK_Redeclaration: + break; + + case AMK_Override: + Override = true; + break; + } + } - if (mergeDeclAttribute(New, *i)) + if (mergeDeclAttribute(New, *i, Override)) foundAny = true; } @@ -2475,7 +2487,7 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, ObjCMethodDecl *oldMethod) { // Merge the attributes, including deprecated/unavailable - mergeDeclAttributes(newMethod, oldMethod, /* mergeDeprecation */true); + mergeDeclAttributes(newMethod, oldMethod, AMK_Override); // Merge attributes from the parameters. ObjCMethodDecl::param_const_iterator oi = oldMethod->param_begin(), @@ -2485,7 +2497,7 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, ni != ne && oi != oe; ++ni, ++oi) mergeParamDeclAttributes(*ni, *oi, Context); - CheckObjCMethodOverride(newMethod, oldMethod, true); + CheckObjCMethodOverride(newMethod, oldMethod); } /// MergeVarDeclTypes - We parsed a variable 'New' which has the same name and diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index bd81c27ad1..74a6bc5546 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -2001,13 +2001,32 @@ static bool checkAvailabilityAttr(Sema &S, SourceRange Range, return false; } +/// \brief Check whether the two versions match. +/// +/// If either version tuple is empty, then they are assumed to match. If +/// \p BeforeIsOkay is true, then \p X can be less than or equal to \p Y. +static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y, + bool BeforeIsOkay) { + if (X.empty() || Y.empty()) + return true; + + if (X == Y) + return true; + + if (BeforeIsOkay && X < Y) + return true; + + return false; +} + AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, IdentifierInfo *Platform, VersionTuple Introduced, VersionTuple Deprecated, VersionTuple Obsoleted, bool IsUnavailable, - StringRef Message) { + StringRef Message, + bool Override) { VersionTuple MergedIntroduced = Introduced; VersionTuple MergedDeprecated = Deprecated; VersionTuple MergedObsoleted = Obsoleted; @@ -2033,18 +2052,47 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, VersionTuple OldDeprecated = OldAA->getDeprecated(); VersionTuple OldObsoleted = OldAA->getObsoleted(); bool OldIsUnavailable = OldAA->getUnavailable(); - StringRef OldMessage = OldAA->getMessage(); - - if ((!OldIntroduced.empty() && !Introduced.empty() && - OldIntroduced != Introduced) || - (!OldDeprecated.empty() && !Deprecated.empty() && - OldDeprecated != Deprecated) || - (!OldObsoleted.empty() && !Obsoleted.empty() && - OldObsoleted != Obsoleted) || - (OldIsUnavailable != IsUnavailable) || - (OldMessage != Message)) { - Diag(OldAA->getLocation(), diag::warn_mismatched_availability); - Diag(Range.getBegin(), diag::note_previous_attribute); + + if (!versionsMatch(OldIntroduced, Introduced, Override) || + !versionsMatch(Deprecated, OldDeprecated, Override) || + !versionsMatch(Obsoleted, OldObsoleted, Override) || + !(OldIsUnavailable == IsUnavailable || + (Override && OldIsUnavailable && !IsUnavailable))) { + if (Override) { + int Which = -1; + VersionTuple FirstVersion; + VersionTuple SecondVersion; + if (!versionsMatch(OldIntroduced, Introduced, Override)) { + Which = 0; + FirstVersion = OldIntroduced; + SecondVersion = Introduced; + } else if (!versionsMatch(Deprecated, OldDeprecated, Override)) { + Which = 1; + FirstVersion = Deprecated; + SecondVersion = OldDeprecated; + } else if (!versionsMatch(Obsoleted, OldObsoleted, Override)) { + Which = 2; + FirstVersion = Obsoleted; + SecondVersion = OldObsoleted; + } + + if (Which == -1) { + Diag(OldAA->getLocation(), + diag::warn_mismatched_availability_override_unavail) + << AvailabilityAttr::getPrettyPlatformName(Platform->getName()); + } else { + Diag(OldAA->getLocation(), + diag::warn_mismatched_availability_override) + << Which + << AvailabilityAttr::getPrettyPlatformName(Platform->getName()) + << FirstVersion.getAsString() << SecondVersion.getAsString(); + } + Diag(Range.getBegin(), diag::note_overridden_method); + } else { + Diag(OldAA->getLocation(), diag::warn_mismatched_availability); + Diag(Range.getBegin(), diag::note_previous_attribute); + } + Attrs.erase(Attrs.begin() + i); --e; continue; @@ -2121,7 +2169,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Introduced.Version, Deprecated.Version, Obsoleted.Version, - IsUnavailable, Str); + IsUnavailable, Str, + /*Override=*/false); if (NewAttr) D->addAttr(NewAttr); } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index d22d7a14f6..870c52b6e6 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -109,8 +109,7 @@ bool Sema::checkInitMethod(ObjCMethodDecl *method, } void Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, - const ObjCMethodDecl *Overridden, - bool IsImplementation) { + const ObjCMethodDecl *Overridden) { if (Overridden->hasRelatedResultType() && !NewMethod->hasRelatedResultType()) { // This can only happen when the method follows a naming convention that diff --git a/test/SemaObjC/attr-availability.m b/test/SemaObjC/attr-availability.m index ed6b7608b9..0905733906 100644 --- a/test/SemaObjC/attr-availability.m +++ b/test/SemaObjC/attr-availability.m @@ -6,11 +6,24 @@ @interface A

- (void)method __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note {{method 'method' declared here}} + +- (void)overridden __attribute__((availability(macosx,introduced=10.3))); // expected-note{{overridden method is here}} +- (void)overridden2 __attribute__((availability(macosx,introduced=10.3))); +- (void)overridden3 __attribute__((availability(macosx,deprecated=10.3))); +- (void)overridden4 __attribute__((availability(macosx,deprecated=10.3))); // expected-note{{overridden method is here}} +- (void)overridden5 __attribute__((availability(macosx,unavailable))); // expected-note{{overridden method is here}} +- (void)overridden6 __attribute__((availability(macosx,introduced=10.3))); @end // rdar://11475360 @interface B : A - (void)method; // expected-note {{method 'method' declared here}} +- (void)overridden __attribute__((availability(macosx,introduced=10.4))); // expected-warning{{overriding method introduced after overridden method on OS X (10.4 vs. 10.3)}} +- (void)overridden2 __attribute__((availability(macosx,introduced=10.2))); +- (void)overridden3 __attribute__((availability(macosx,deprecated=10.4))); +- (void)overridden4 __attribute__((availability(macosx,deprecated=10.2))); // expected-warning{{overriding method deprecated before overridden method on OS X (10.3 vs. 10.2)}} +- (void)overridden5 __attribute__((availability(macosx,introduced=10.3))); // expected-warning{{overriding method cannot be unavailable on OS X when its overridden method is available}} +- (void)overridden6 __attribute__((availability(macosx,unavailable))); @end void f(A *a, B *b) {