mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
201 lines
6.6 KiB
C++
201 lines
6.6 KiB
C++
//===--- AvailabilityMixin.cpp - Symbol Graph Symbol Availability ---------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AvailabilityMixin.h"
|
|
#include "JSON.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
|
|
using namespace swift;
|
|
using namespace symbolgraphgen;
|
|
|
|
namespace {
|
|
StringRef getDomain(const SemanticAvailableAttr &AvAttr) {
|
|
// FIXME: [avalailability] Move the definition of these strings into
|
|
// AvailabilityDomain so that new domains are handled automatically.
|
|
|
|
if (AvAttr.getDomain().isPackageDescription())
|
|
return { "SwiftPM" };
|
|
|
|
if (AvAttr.getDomain().isSwiftLanguage())
|
|
return { "Swift" };
|
|
|
|
// Platform-specific availability.
|
|
switch (AvAttr.getPlatform()) {
|
|
case swift::PlatformKind::iOS:
|
|
return { "iOS" };
|
|
case swift::PlatformKind::macCatalyst:
|
|
return { "macCatalyst" };
|
|
case swift::PlatformKind::macOS:
|
|
return { "macOS" };
|
|
case swift::PlatformKind::tvOS:
|
|
return { "tvOS" };
|
|
case swift::PlatformKind::watchOS:
|
|
return { "watchOS" };
|
|
case swift::PlatformKind::visionOS:
|
|
return { "visionOS" };
|
|
case swift::PlatformKind::iOSApplicationExtension:
|
|
return { "iOSAppExtension" };
|
|
case swift::PlatformKind::macCatalystApplicationExtension:
|
|
return { "macCatalystAppExtension" };
|
|
case swift::PlatformKind::macOSApplicationExtension:
|
|
return { "macOSAppExtension" };
|
|
case swift::PlatformKind::tvOSApplicationExtension:
|
|
return { "tvOSAppExtension" };
|
|
case swift::PlatformKind::watchOSApplicationExtension:
|
|
return { "watchOSAppExtension" };
|
|
case swift::PlatformKind::visionOSApplicationExtension:
|
|
return { "visionOSAppExtension" };
|
|
case swift::PlatformKind::FreeBSD:
|
|
return { "FreeBSD" };
|
|
case swift::PlatformKind::OpenBSD:
|
|
return { "OpenBSD" };
|
|
case swift::PlatformKind::Windows:
|
|
return { "Windows" };
|
|
case swift::PlatformKind::none:
|
|
return { "*" };
|
|
}
|
|
llvm_unreachable("invalid platform kind");
|
|
}
|
|
} // end anonymous namespace
|
|
|
|
Availability::Availability(const SemanticAvailableAttr &AvAttr)
|
|
: Domain(getDomain(AvAttr)), Introduced(AvAttr.getIntroduced()),
|
|
Deprecated(AvAttr.getDeprecated()), Obsoleted(AvAttr.getObsoleted()),
|
|
Message(AvAttr.getMessage()), Renamed(AvAttr.getRename()),
|
|
IsUnconditionallyDeprecated(AvAttr.isUnconditionallyDeprecated()),
|
|
IsUnconditionallyUnavailable(AvAttr.isUnconditionallyUnavailable()) {
|
|
assert(!Domain.empty());
|
|
}
|
|
|
|
void
|
|
Availability::updateFromDuplicate(const Availability &Other) {
|
|
assert(Domain == Other.Domain);
|
|
|
|
// The highest `introduced` version always wins
|
|
// regardless of the order in which they appeared in the source.
|
|
if (!Introduced) {
|
|
Introduced = Other.Introduced;
|
|
} else if (Other.Introduced && *Other.Introduced > *Introduced) {
|
|
Introduced = Other.Introduced;
|
|
}
|
|
|
|
// The `deprecated` version that appears last in the source always wins,
|
|
// allowing even to erase a previous deprecated.
|
|
Deprecated = Other.Deprecated;
|
|
|
|
// Same for `deprecated` with no version.
|
|
IsUnconditionallyDeprecated = Other.IsUnconditionallyDeprecated;
|
|
|
|
// Same for `unavailable` with no version.
|
|
IsUnconditionallyUnavailable = Other.IsUnconditionallyUnavailable;
|
|
|
|
// Same for `obsoleted`.
|
|
Obsoleted = Other.Obsoleted;
|
|
|
|
// The `message` that appears last in the source always wins.
|
|
Message = Other.Message;
|
|
|
|
// Same for `renamed`.
|
|
Renamed = Other.Renamed;
|
|
}
|
|
|
|
void
|
|
Availability::updateFromParent(const Availability &Parent) {
|
|
assert(Domain == Parent.Domain);
|
|
|
|
// Allow filling, but never replace a child's existing `introduced`
|
|
// availability because it can never be less available than the parent anyway.
|
|
//
|
|
// e.g. you cannot write this:
|
|
// @available(macos, introduced: 10.15)
|
|
// struct S {
|
|
// @available(macos, introduced: 10.14)
|
|
// func foo() {}
|
|
// }
|
|
//
|
|
// So the child's `introduced` availability will always
|
|
// be >= the parent's.
|
|
if (!Introduced) {
|
|
Introduced = Parent.Introduced;
|
|
}
|
|
|
|
// Allow filling from the parent.
|
|
// For replacement, we will consider a parent's
|
|
// earlier deprecation to supersede a child's later deprecation.
|
|
if (!Deprecated) {
|
|
Deprecated = Parent.Deprecated;
|
|
} else if (Parent.Deprecated && *Parent.Deprecated < *Deprecated) {
|
|
Deprecated = Parent.Deprecated;
|
|
}
|
|
|
|
// The above regarding `deprecated` also will apply to `obsoleted`.
|
|
if (!Obsoleted) {
|
|
Obsoleted = Parent.Obsoleted;
|
|
} else if (Parent.Obsoleted && *Parent.Obsoleted < *Obsoleted) {
|
|
Obsoleted = Parent.Obsoleted;
|
|
}
|
|
|
|
// Never replace or fill a child's `message` with a parent's because
|
|
// there may be context at the parent that doesn't apply at the child,
|
|
// i.e. it might not always make sense.
|
|
|
|
// Never replace or fill a child's `renamed` field because it
|
|
// doesn't make sense. Just because a parent is renamed doesn't
|
|
// mean its child is renamed to the same thing.
|
|
|
|
// If a parent is unconditionally deprecated, then so are all
|
|
// of its children.
|
|
IsUnconditionallyDeprecated |= Parent.IsUnconditionallyDeprecated;
|
|
|
|
// If a parent is unconditionally unavailable, then so are all
|
|
// of its children.
|
|
IsUnconditionallyUnavailable |= Parent.IsUnconditionallyUnavailable;
|
|
}
|
|
|
|
void Availability::serialize(llvm::json::OStream &OS) const {
|
|
OS.object([&](){
|
|
OS.attribute("domain", Domain);
|
|
if (Introduced) {
|
|
AttributeRAII IntroducedAttribute("introduced", OS);
|
|
symbolgraphgen::serialize(*Introduced, OS);
|
|
}
|
|
if (Deprecated) {
|
|
AttributeRAII DeprecatedAttribute("deprecated", OS);
|
|
symbolgraphgen::serialize(*Deprecated, OS);
|
|
}
|
|
if (Obsoleted) {
|
|
AttributeRAII ObsoletedAttribute("obsoleted", OS);
|
|
symbolgraphgen::serialize(*Obsoleted, OS);
|
|
}
|
|
if (!Message.empty()) {
|
|
OS.attribute("message", Message);
|
|
}
|
|
if (!Renamed.empty()) {
|
|
OS.attribute("renamed", Renamed);
|
|
}
|
|
if (IsUnconditionallyDeprecated) {
|
|
OS.attribute("isUnconditionallyDeprecated", true);
|
|
}
|
|
if (IsUnconditionallyUnavailable) {
|
|
OS.attribute("isUnconditionallyUnavailable", true);
|
|
}
|
|
}); // end availability object
|
|
}
|
|
|
|
bool Availability::empty() const {
|
|
return !Introduced.has_value() &&
|
|
!Deprecated.has_value() &&
|
|
!Obsoleted.has_value() &&
|
|
!IsUnconditionallyDeprecated &&
|
|
!IsUnconditionallyUnavailable;
|
|
}
|