llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Daniel Grumberg (daniel-grumberg) <details> <summary>Changes</summary> Additionally this computes availability information for all platforms ahead of possibly introducing a flag to enable this behavior. rdar://123513706 --- Full diff: https://github.com/llvm/llvm-project/pull/103040.diff 4 Files Affected: - (modified) clang/include/clang/AST/Availability.h (+4) - (modified) clang/lib/AST/Availability.cpp (+84-16) - (modified) clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp (+17-14) - (added) clang/test/ExtractAPI/inherited_availability.m (+149) ``````````diff diff --git a/clang/include/clang/AST/Availability.h b/clang/include/clang/AST/Availability.h index 26ae622e5b4496..60ca1383f0a44e 100644 --- a/clang/include/clang/AST/Availability.h +++ b/clang/include/clang/AST/Availability.h @@ -97,6 +97,10 @@ struct AvailabilityInfo { return UnconditionallyUnavailable; } + /// Augments the existing information with additional constraints provided by + /// \c Other. + void mergeWith(AvailabilityInfo Other); + AvailabilityInfo(StringRef Domain, VersionTuple I, VersionTuple D, VersionTuple O, bool U, bool UD, bool UU) : Domain(Domain), Introduced(I), Deprecated(D), Obsoleted(O), diff --git a/clang/lib/AST/Availability.cpp b/clang/lib/AST/Availability.cpp index 238359a2dedfcf..376a625b41817a 100644 --- a/clang/lib/AST/Availability.cpp +++ b/clang/lib/AST/Availability.cpp @@ -16,33 +16,101 @@ #include "clang/AST/Decl.h" #include "clang/Basic/TargetInfo.h" -namespace clang { +namespace { + +struct AvailabilitySet { + llvm::SmallVector<clang::AvailabilityInfo> Availabilities; + bool UnconditionallyDeprecated = false; + bool UnconditionallyUnavailable = false; -AvailabilityInfo AvailabilityInfo::createFromDecl(const Decl *Decl) { - ASTContext &Context = Decl->getASTContext(); - StringRef PlatformName = Context.getTargetInfo().getPlatformName(); - AvailabilityInfo Availability; + void insert(clang::AvailabilityInfo &&Availability) { + auto *Found = getForPlatform(Availability.Domain); + if (Found) + Found->mergeWith(std::move(Availability)); + else + Availabilities.emplace_back(std::move(Availability)); + } + + clang::AvailabilityInfo *getForPlatform(llvm::StringRef Domain) { + auto *It = llvm::find_if(Availabilities, + [Domain](const clang::AvailabilityInfo &Info) { + return Domain.compare(Info.Domain) == 0; + }); + return It == Availabilities.end() ? nullptr : It; + } +}; +static void createInfoForDecl(const clang::Decl *Decl, + AvailabilitySet &Availabilities) { // Collect availability attributes from all redeclarations. for (const auto *RD : Decl->redecls()) { - for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) { - if (A->getPlatform()->getName() != PlatformName) - continue; - Availability = AvailabilityInfo( + for (const auto *A : RD->specific_attrs<clang::AvailabilityAttr>()) { + Availabilities.insert(clang::AvailabilityInfo( A->getPlatform()->getName(), A->getIntroduced(), A->getDeprecated(), - A->getObsoleted(), A->getUnavailable(), false, false); - break; + A->getObsoleted(), A->getUnavailable(), false, false)); } - if (const auto *A = RD->getAttr<UnavailableAttr>()) + if (const auto *A = RD->getAttr<clang::UnavailableAttr>()) if (!A->isImplicit()) - Availability.UnconditionallyUnavailable = true; + Availabilities.UnconditionallyUnavailable = true; - if (const auto *A = RD->getAttr<DeprecatedAttr>()) + if (const auto *A = RD->getAttr<clang::DeprecatedAttr>()) if (!A->isImplicit()) - Availability.UnconditionallyDeprecated = true; + Availabilities.UnconditionallyDeprecated = true; + } +} + +} // namespace + +namespace clang { + +void AvailabilityInfo::mergeWith(AvailabilityInfo Other) { + if (isDefault() && Other.isDefault()) + return; + + if (Domain.empty()) + Domain = Other.Domain; + + UnconditionallyUnavailable |= Other.UnconditionallyUnavailable; + UnconditionallyDeprecated |= Other.UnconditionallyDeprecated; + Unavailable |= Other.Unavailable; + + Introduced = std::max(Introduced, Other.Introduced); + + // Default VersionTuple is 0.0.0 so if both are non default let's pick the + // smallest version number, otherwise select the one that is non-zero if there + // is one. + if (!Deprecated.empty() && !Other.Deprecated.empty()) + Deprecated = std::min(Deprecated, Other.Deprecated); + else + Deprecated = std::max(Deprecated, Other.Deprecated); + + if (!Obsoleted.empty() && !Other.Obsoleted.empty()) + Obsoleted = std::min(Obsoleted, Other.Obsoleted); + else + Obsoleted = std::max(Obsoleted, Other.Obsoleted); +} + +AvailabilityInfo AvailabilityInfo::createFromDecl(const Decl *D) { + AvailabilitySet Availabilities; + createInfoForDecl(D, Availabilities); + // Traverse + for (const auto *Ctx = llvm::cast_or_null<Decl>(D->getDeclContext()); Ctx; + Ctx = llvm::cast_or_null<Decl>(Ctx->getDeclContext())) + createInfoForDecl(Ctx, Availabilities); + + if (auto *Avail = Availabilities.getForPlatform( + D->getASTContext().getTargetInfo().getPlatformName())) { + Avail->UnconditionallyDeprecated = Availabilities.UnconditionallyDeprecated; + Avail->UnconditionallyUnavailable = + Availabilities.UnconditionallyUnavailable; + return std::move(*Avail); } - return Availability; + + AvailabilityInfo Avail; + Avail.UnconditionallyDeprecated = Availabilities.UnconditionallyDeprecated; + Avail.UnconditionallyUnavailable = Availabilities.UnconditionallyUnavailable; + return Avail; } } // namespace clang diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp index 6e56ee5b573f66..84ed5467dd2fb9 100644 --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -171,22 +171,25 @@ std::optional<Array> serializeAvailability(const AvailabilityInfo &Avail) { UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true; AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated)); } - Object Availability; - - Availability["domain"] = Avail.Domain; - - if (Avail.isUnavailable()) { - Availability["isUnconditionallyUnavailable"] = true; - } else { - serializeObject(Availability, "introduced", - serializeSemanticVersion(Avail.Introduced)); - serializeObject(Availability, "deprecated", - serializeSemanticVersion(Avail.Deprecated)); - serializeObject(Availability, "obsoleted", - serializeSemanticVersion(Avail.Obsoleted)); + + if (Avail.Domain.str() != "") { + Object Availability; + Availability["domain"] = Avail.Domain; + + if (Avail.isUnavailable()) { + Availability["isUnconditionallyUnavailable"] = true; + } else { + serializeObject(Availability, "introduced", + serializeSemanticVersion(Avail.Introduced)); + serializeObject(Availability, "deprecated", + serializeSemanticVersion(Avail.Deprecated)); + serializeObject(Availability, "obsoleted", + serializeSemanticVersion(Avail.Obsoleted)); + } + + AvailabilityArray.emplace_back(std::move(Availability)); } - AvailabilityArray.emplace_back(std::move(Availability)); return AvailabilityArray; } diff --git a/clang/test/ExtractAPI/inherited_availability.m b/clang/test/ExtractAPI/inherited_availability.m new file mode 100644 index 00000000000000..6b62e58b022ae5 --- /dev/null +++ b/clang/test/ExtractAPI/inherited_availability.m @@ -0,0 +1,149 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing -triple arm64-apple-macosx \ +// RUN: -x objective-c-header %s -o %t/output.symbols.json -verify + + +// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix A +__attribute__((availability(macos, introduced=9.0, deprecated=12.0, obsoleted=20.0))) +@interface A +// A-LABEL: "!testLabel": "c:objc(cs)A" +// A: "availability": [ +// A-NEXT: { +// A-NEXT: "deprecated": { +// A-NEXT: "major": 12, +// A-NEXT: "minor": 0, +// A-NEXT: "patch": 0 +// A-NEXT: } +// A-NEXT: "domain": "macos" +// A-NEXT: "introduced": { +// A-NEXT: "major": 9, +// A-NEXT: "minor": 0, +// A-NEXT: "patch": 0 +// A-NEXT: } +// A-NEXT: "obsoleted": { +// A-NEXT: "major": 20, +// A-NEXT: "minor": 0, +// A-NEXT: "patch": 0 +// A-NEXT: } +// A-NEXT: } +// A-NEXT: ] + +// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix CP +@property(class) int CP; +// CP-LABEL: "!testLabel": "c:objc(cs)A(cpy)CP" +// CP: "availability": [ +// CP-NEXT: { +// CP-NEXT: "deprecated": { +// CP-NEXT: "major": 12, +// CP-NEXT: "minor": 0, +// CP-NEXT: "patch": 0 +// CP-NEXT: } +// CP-NEXT: "domain": "macos" +// CP-NEXT: "introduced": { +// CP-NEXT: "major": 9, +// CP-NEXT: "minor": 0, +// CP-NEXT: "patch": 0 +// CP-NEXT: } +// CP-NEXT: "obsoleted": { +// CP-NEXT: "major": 20, +// CP-NEXT: "minor": 0, +// CP-NEXT: "patch": 0 +// CP-NEXT: } +// CP-NEXT: } +// CP-NEXT: ] + +// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix IP +@property int IP; +// IP-LABEL: "!testLabel": "c:objc(cs)A(py)IP" +// IP: "availability": [ +// IP-NEXT: { +// IP-NEXT: "deprecated": { +// IP-NEXT: "major": 12, +// IP-NEXT: "minor": 0, +// IP-NEXT: "patch": 0 +// IP-NEXT: } +// IP-NEXT: "domain": "macos" +// IP-NEXT: "introduced": { +// IP-NEXT: "major": 9, +// IP-NEXT: "minor": 0, +// IP-NEXT: "patch": 0 +// IP-NEXT: } +// IP-NEXT: "obsoleted": { +// IP-NEXT: "major": 20, +// IP-NEXT: "minor": 0, +// IP-NEXT: "patch": 0 +// IP-NEXT: } +// IP-NEXT: } +// IP-NEXT: ] + +// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix MR +@property int moreRestrictive __attribute__((availability(macos, introduced=10.0, deprecated=11.0, obsoleted=19.0))); +// MR-LABEL: "!testLabel": "c:objc(cs)A(py)moreRestrictive" +// MR: "availability": [ +// MR-NEXT: { +// MR-NEXT: "deprecated": { +// MR-NEXT: "major": 11, +// MR-NEXT: "minor": 0, +// MR-NEXT: "patch": 0 +// MR-NEXT: } +// MR-NEXT: "domain": "macos" +// MR-NEXT: "introduced": { +// MR-NEXT: "major": 10, +// MR-NEXT: "minor": 0, +// MR-NEXT: "patch": 0 +// MR-NEXT: } +// MR-NEXT: "obsoleted": { +// MR-NEXT: "major": 19, +// MR-NEXT: "minor": 0, +// MR-NEXT: "patch": 0 +// MR-NEXT: } +// MR-NEXT: } +// MR-NEXT: ] + +@end + +// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix B +__attribute__((deprecated("B is deprecated"))) +@interface B +// B-LABEL: "!testLabel": "c:objc(cs)B" +// B: "availability": [ +// B-NEXT: { +// B-NEXT: "domain": "*" +// B-NEXT: "isUnconditionallyDeprecated": true +// B-NEXT: } +// B-NEXT: ] + +// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix BIP +@property int BIP; +// BIP-LABEL: "!testLabel": "c:objc(cs)B(py)BIP" +// BIP: "availability": [ +// BIP-NEXT: { +// BIP-NEXT: "domain": "*" +// BIP-NEXT: "isUnconditionallyDeprecated": true +// BIP-NEXT: } +// BIP-NEXT: ] +@end + +// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix C +__attribute__((availability(macos, unavailable))) +@interface C +// C-LABEL: "!testLabel": "c:objc(cs)C" +// C: "availability": [ +// C-NEXT: { +// C-NEXT: "domain": "macos" +// C-NEXT: "isUnconditionallyUnavailable": true +// C-NEXT: } +// C-NEXT: ] + +// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix CIP +@property int CIP; +// CIP-LABEL: "!testLabel": "c:objc(cs)C(py)CIP" +// CIP: "availability": [ +// CIP-NEXT: { +// CIP-NEXT: "domain": "macos" +// CIP-NEXT: "isUnconditionallyUnavailable": true +// CIP-NEXT: } +// CIP-NEXT: ] +@end + +// expected-no-diagnostics `````````` </details> https://github.com/llvm/llvm-project/pull/103040 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits