From 2d243bfe2f258b23667a91ae0c1a51527772c142 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Sun, 6 May 2012 19:56:25 +0000 Subject: [PATCH] Split mergeAvailabilityAttr out of handleAvailabilityAttr. This is important for having a uniform logic for adding attributes to a decl. This in turn is needed to fix the FIXME: // FIXME: This needs to happen before we merge declarations. Then, // let attribute merging cope with attribute conflicts. ProcessDeclAttributes(S, NewFD, D, /*NonInheritable=*/false, /*Inheritable=*/true); The idea is that mergeAvailabilityAttr will become a method. Once attributes are processed before merging, it will be called from handleAvailabilityAttr to handle multiple attributes in one decl: void f(int) __attribute__((availability(ios,deprecated=3.0), availability(ios,introduced=2.0))); and from SemaDecl.cpp to handle multiple decls: void f(int) __attribute__((availability(ios,deprecated=3.0))); void f(int) __attribute__((availability(ios,introduced=2.0))); As a bonus, use the new structure to diagnose incompatible availability attributes added to different decls (see included testcases). llvm-svn: 156269 --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Sema/SemaDecl.cpp | 7 + clang/lib/Sema/SemaDeclAttr.cpp | 161 +++++++++++++----- clang/test/Sema/attr-availability.c | 13 ++ 4 files changed, 142 insertions(+), 41 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7680ae17dff8..e7960e9fe4aa 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1672,6 +1672,8 @@ def warn_availability_version_ordering : Warning< "feature cannot be %select{introduced|deprecated|obsoleted}0 in %1 version " "%2 before it was %select{introduced|deprecated|obsoleted}3 in version %4; " "attribute ignored">, InGroup; +def warn_mismatched_availability: Warning< + "availability does not match previous declaration">, InGroup; // Thread Safety Attributes def warn_thread_attribute_ignored : Warning< diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d1cc73c3f8fa..500de928bf6d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1633,6 +1633,13 @@ void Sema::MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls) { /// attribute. static bool DeclHasAttr(const Decl *D, const Attr *A) { + // There can be multiple AvailabilityAttr in a Decl. Make sure we copy + // all of them. It is mergeAvailabilityAttr in SemaDeclAttr.cpp that is + // responsible for making sure they are consistent. + const AvailabilityAttr *AA = dyn_cast(A); + if (AA) + return false; + const OwnershipAttr *OA = dyn_cast(A); const AnnotateAttr *Ann = dyn_cast(A); for (Decl::attr_iterator i = D->attr_begin(), e = D->attr_end(); i != e; ++i) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index af3fabb9d135..c0bc3690129e 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1690,64 +1690,143 @@ static void handleObjCRequiresPropertyDefsAttr(Sema &S, Decl *D, Attr.getRange(), S.Context)); } +bool checkAvailabilityAttr(Sema &S, SourceRange Range, + IdentifierInfo *Platform, + VersionTuple Introduced, + VersionTuple Deprecated, + VersionTuple Obsoleted) { + StringRef PlatformName + = AvailabilityAttr::getPrettyPlatformName(Platform->getName()); + if (PlatformName.empty()) + PlatformName = Platform->getName(); + + // Ensure that Introduced <= Deprecated <= Obsoleted (although not all + // of these steps are needed). + if (!Introduced.empty() && !Deprecated.empty() && + !(Introduced <= Deprecated)) { + S.Diag(Range.getBegin(), diag::warn_availability_version_ordering) + << 1 << PlatformName << Deprecated.getAsString() + << 0 << Introduced.getAsString(); + return true; + } + + if (!Introduced.empty() && !Obsoleted.empty() && + !(Introduced <= Obsoleted)) { + S.Diag(Range.getBegin(), diag::warn_availability_version_ordering) + << 2 << PlatformName << Obsoleted.getAsString() + << 0 << Introduced.getAsString(); + return true; + } + + if (!Deprecated.empty() && !Obsoleted.empty() && + !(Deprecated <= Obsoleted)) { + S.Diag(Range.getBegin(), diag::warn_availability_version_ordering) + << 2 << PlatformName << Obsoleted.getAsString() + << 1 << Deprecated.getAsString(); + return true; + } + + return false; +} + +static void mergeAvailabilityAttr(Sema &S, Decl *D, SourceRange Range, + IdentifierInfo *Platform, + VersionTuple Introduced, + VersionTuple Deprecated, + VersionTuple Obsoleted, + bool IsUnavailable, + StringRef Message) { + VersionTuple MergedIntroduced; + VersionTuple MergedDeprecated; + VersionTuple MergedObsoleted; + bool FoundAny = false; + + for (specific_attr_iterator + i = D->specific_attr_begin(), + e = D->specific_attr_end(); + i != e ; ++i) { + const AvailabilityAttr *OldAA = *i; + IdentifierInfo *OldPlatform = OldAA->getPlatform(); + if (OldPlatform != Platform) + continue; + FoundAny = true; + VersionTuple OldIntroduced = OldAA->getIntroduced(); + 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)) { + S.Diag(Range.getBegin(), diag::warn_mismatched_availability); + S.Diag(OldAA->getLocation(), diag::note_previous_attribute); + return; + } + if (MergedIntroduced.empty()) + MergedIntroduced = OldIntroduced; + if (MergedDeprecated.empty()) + MergedDeprecated = OldDeprecated; + if (MergedObsoleted.empty()) + MergedObsoleted = OldObsoleted; + } + + if (FoundAny && + MergedIntroduced == Introduced && + MergedDeprecated == Deprecated && + MergedObsoleted == Obsoleted) + return; + + if (MergedIntroduced.empty()) + MergedIntroduced = Introduced; + if (MergedDeprecated.empty()) + MergedDeprecated = Deprecated; + if (MergedObsoleted.empty()) + MergedObsoleted = Obsoleted; + + if (!checkAvailabilityAttr(S, Range, Platform, MergedIntroduced, + MergedDeprecated, MergedObsoleted)) { + D->addAttr(::new (S.Context) AvailabilityAttr(Range, S.Context, + Platform, + Introduced, + Deprecated, + Obsoleted, + IsUnavailable, + Message)); + } +} + static void handleAvailabilityAttr(Sema &S, Decl *D, const AttributeList &Attr) { IdentifierInfo *Platform = Attr.getParameterName(); SourceLocation PlatformLoc = Attr.getParameterLoc(); - StringRef PlatformName - = AvailabilityAttr::getPrettyPlatformName(Platform->getName()); - if (PlatformName.empty()) { + if (AvailabilityAttr::getPrettyPlatformName(Platform->getName()).empty()) S.Diag(PlatformLoc, diag::warn_availability_unknown_platform) << Platform; - PlatformName = Platform->getName(); - } - AvailabilityChange Introduced = Attr.getAvailabilityIntroduced(); AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); bool IsUnavailable = Attr.getUnavailableLoc().isValid(); - - // Ensure that Introduced <= Deprecated <= Obsoleted (although not all - // of these steps are needed). - if (Introduced.isValid() && Deprecated.isValid() && - !(Introduced.Version <= Deprecated.Version)) { - S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering) - << 1 << PlatformName << Deprecated.Version.getAsString() - << 0 << Introduced.Version.getAsString(); - return; - } - - if (Introduced.isValid() && Obsoleted.isValid() && - !(Introduced.Version <= Obsoleted.Version)) { - S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering) - << 2 << PlatformName << Obsoleted.Version.getAsString() - << 0 << Introduced.Version.getAsString(); - return; - } - - if (Deprecated.isValid() && Obsoleted.isValid() && - !(Deprecated.Version <= Obsoleted.Version)) { - S.Diag(Deprecated.KeywordLoc, diag::warn_availability_version_ordering) - << 2 << PlatformName << Obsoleted.Version.getAsString() - << 1 << Deprecated.Version.getAsString(); - return; - } - StringRef Str; const StringLiteral *SE = dyn_cast_or_null(Attr.getMessageExpr()); if (SE) Str = SE->getString(); - - D->addAttr(::new (S.Context) AvailabilityAttr(Attr.getRange(), S.Context, - Platform, - Introduced.Version, - Deprecated.Version, - Obsoleted.Version, - IsUnavailable, - Str)); + + mergeAvailabilityAttr(S, D, Attr.getRange(), + Platform, + Introduced.Version, + Deprecated.Version, + Obsoleted.Version, + IsUnavailable, + Str); } static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr) { diff --git a/clang/test/Sema/attr-availability.c b/clang/test/Sema/attr-availability.c index 5fb63cd72205..89252a6cc40b 100644 --- a/clang/test/Sema/attr-availability.c +++ b/clang/test/Sema/attr-availability.c @@ -24,3 +24,16 @@ enum { NSDataWritingFileProtectionWriteOnly = 0x30000000, NSDataWritingFileProtectionCompleteUntilUserAuthentication = 0x40000000, }; + +void f4(int) __attribute__((availability(ios,deprecated=3.0))); +void f4(int) __attribute__((availability(ios,introduced=4.0))); // expected-warning {{feature cannot be deprecated in iOS version 3.0 before it was introduced in version 4.0; attribute ignored}} + +void f5(int) __attribute__((availability(ios,deprecated=3.0), // expected-warning {{feature cannot be deprecated in iOS version 3.0 before it was introduced in version 4.0; attribute ignored}} + availability(ios,introduced=4.0))); + +void f6(int) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{previous attribute is here}} +void f6(int) __attribute__((availability(ios,deprecated=4.0))); // expected-warning {{availability does not match previous declaration}} + +void f7(int) __attribute__((availability(ios,introduced=2.0))); +void f7(int) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{previous attribute is here}} +void f7(int) __attribute__((availability(ios,deprecated=4.0))); // expected-warning {{availability does not match previous declaration}}