mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 06:36:06 +00:00
[clang][ExtractAPI] Compute inherited availability information (#103040)
Additionally this computes availability information for all platforms ahead of possibly introducing a flag to enable this behavior. rdar://123513706
This commit is contained in:
parent
a4525fcc8f
commit
026d963cb0
@ -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),
|
||||
|
@ -16,33 +16,104 @@
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
|
||||
namespace clang {
|
||||
namespace {
|
||||
|
||||
AvailabilityInfo AvailabilityInfo::createFromDecl(const Decl *Decl) {
|
||||
ASTContext &Context = Decl->getASTContext();
|
||||
StringRef PlatformName = Context.getTargetInfo().getPlatformName();
|
||||
AvailabilityInfo Availability;
|
||||
/// Represents the availability of a symbol across platforms.
|
||||
struct AvailabilitySet {
|
||||
bool UnconditionallyDeprecated = false;
|
||||
bool UnconditionallyUnavailable = false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::SmallVector<clang::AvailabilityInfo> Availabilities;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
return Availability;
|
||||
}
|
||||
|
||||
} // 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;
|
||||
// Walk DeclContexts upwards starting from D to find the combined availability
|
||||
// of the symbol.
|
||||
for (const auto *Ctx = D; 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);
|
||||
}
|
||||
|
||||
AvailabilityInfo Avail;
|
||||
Avail.UnconditionallyDeprecated = Availabilities.UnconditionallyDeprecated;
|
||||
Avail.UnconditionallyUnavailable = Availabilities.UnconditionallyUnavailable;
|
||||
return Avail;
|
||||
}
|
||||
|
||||
} // namespace clang
|
||||
|
@ -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.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));
|
||||
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;
|
||||
}
|
||||
|
||||
|
175
clang/test/ExtractAPI/inherited_availability.m
Normal file
175
clang/test/ExtractAPI/inherited_availability.m
Normal file
@ -0,0 +1,175 @@
|
||||
// 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
|
||||
|
||||
@interface D
|
||||
// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix DIP
|
||||
@property int DIP __attribute__((availability(macos, introduced=10.0, deprecated=11.0, obsoleted=19.0)));
|
||||
// DIP-LABEL: "!testLabel": "c:objc(cs)D(py)DIP"
|
||||
// DIP: "availability": [
|
||||
// DIP-NEXT: {
|
||||
// DIP-NEXT: "deprecated": {
|
||||
// DIP-NEXT: "major": 11,
|
||||
// DIP-NEXT: "minor": 0,
|
||||
// DIP-NEXT: "patch": 0
|
||||
// DIP-NEXT: }
|
||||
// DIP-NEXT: "domain": "macos"
|
||||
// DIP-NEXT: "introduced": {
|
||||
// DIP-NEXT: "major": 10,
|
||||
// DIP-NEXT: "minor": 0,
|
||||
// DIP-NEXT: "patch": 0
|
||||
// DIP-NEXT: }
|
||||
// DIP-NEXT: "obsoleted": {
|
||||
// DIP-NEXT: "major": 19,
|
||||
// DIP-NEXT: "minor": 0,
|
||||
// DIP-NEXT: "patch": 0
|
||||
// DIP-NEXT: }
|
||||
// DIP-NEXT: }
|
||||
// DIP-NEXT: ]
|
||||
@end
|
||||
|
||||
// expected-no-diagnostics
|
Loading…
x
Reference in New Issue
Block a user