From 118e7c3cdab3c5a15bed33655563e0fb702007d7 Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Thu, 28 May 2020 16:53:51 -0700 Subject: [PATCH] [SymbolGraph] Inherit availability from parent contexts Also be a little smarter about encountering duplicate `@available` attributes on the same declaration. rdar://63570830 --- lib/SymbolGraphGen/AvailabilityMixin.cpp | 187 ++++++++++++++++++ lib/SymbolGraphGen/AvailabilityMixin.h | 78 ++++++++ lib/SymbolGraphGen/CMakeLists.txt | 1 + lib/SymbolGraphGen/Symbol.cpp | 152 +++++++------- lib/SymbolGraphGen/Symbol.h | 6 +- .../{Availability.swift => Basic.swift} | 8 +- .../Duplicated/DeprecatedFilled.swift | 25 +++ .../Duplicated/DeprecatedReplaced.swift | 38 ++++ .../Duplicated/IntroducedFilled.swift | 23 +++ .../Duplicated/IntroducedReplaced.swift | 23 +++ .../Duplicated/MessageLastWins.swift | 25 +++ .../Duplicated/ObsoletedFilled.swift | 25 +++ .../Duplicated/ObsoletedReplaced.swift | 21 ++ .../Inherited/DeprecatedFilled.swift | 41 ++++ .../Inherited/DeprecatedReplaced.swift | 98 +++++++++ .../Inherited/IntroducedFilled.swift | 39 ++++ .../Inherited/IntroducedReplaced.swift | 73 +++++++ .../Inherited/MessageFilled.swift | 38 ++++ .../Inherited/MessageReplaced.swift | 40 ++++ .../Inherited/ObsoletedFilled.swift | 40 ++++ .../Inherited/ObsoletedReplaced.swift | 98 +++++++++ .../Inherited/RenamedFilled.swift | 25 +++ .../Inherited/RenamedNoVersion.swift | 16 ++ .../Inherited/RenamedReplaced.swift | 42 ++++ .../Inherited/UnconditionallyDeprecated.swift | 19 ++ .../UnconditionallyDeprecated.swift | 8 +- 26 files changed, 1099 insertions(+), 90 deletions(-) create mode 100644 lib/SymbolGraphGen/AvailabilityMixin.cpp create mode 100644 lib/SymbolGraphGen/AvailabilityMixin.h rename test/SymbolGraph/Symbols/Mixins/Availability/{Availability.swift => Basic.swift} (65%) create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/DeprecatedFilled.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/DeprecatedReplaced.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/IntroducedFilled.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/IntroducedReplaced.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/MessageLastWins.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/ObsoletedFilled.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/ObsoletedReplaced.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/DeprecatedFilled.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/DeprecatedReplaced.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/IntroducedFilled.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/IntroducedReplaced.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/MessageFilled.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/MessageReplaced.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedFilled.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedReplaced.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedFilled.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedNoVersion.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedReplaced.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/Inherited/UnconditionallyDeprecated.swift diff --git a/lib/SymbolGraphGen/AvailabilityMixin.cpp b/lib/SymbolGraphGen/AvailabilityMixin.cpp new file mode 100644 index 0000000000000..3ac0ecaba3d05 --- /dev/null +++ b/lib/SymbolGraphGen/AvailabilityMixin.cpp @@ -0,0 +1,187 @@ +//===--- 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" + +using namespace swift; +using namespace symbolgraphgen; + +namespace { +StringRef getDomain(const AvailableAttr &AvAttr) { + switch (AvAttr.getPlatformAgnosticAvailability()) { + // SPM- and Swift-specific availability. + case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: + return { "SwiftPM" }; + case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: + case PlatformAgnosticAvailabilityKind::UnavailableInSwift: + return { "Swift" }; + // Although these are in the agnostic kinds, they are actually a signal + // that there is either platform-specific or completely platform-agnostic. + // They'll be handled below. + case PlatformAgnosticAvailabilityKind::Deprecated: + case PlatformAgnosticAvailabilityKind::Unavailable: + case PlatformAgnosticAvailabilityKind::None: + break; + } + + // Platform-specific availability. + switch (AvAttr.Platform) { + case swift::PlatformKind::iOS: + return { "iOS" }; + case swift::PlatformKind::macCatalyst: + return { "macCatalyst" }; + case swift::PlatformKind::OSX: + return { "macOS" }; + case swift::PlatformKind::tvOS: + return { "tvOS" }; + case swift::PlatformKind::watchOS: + return { "watchOS" }; + case swift::PlatformKind::iOSApplicationExtension: + return { "iOSAppExtension" }; + case swift::PlatformKind::macCatalystApplicationExtension: + return { "macCatalystAppExtension" }; + case swift::PlatformKind::OSXApplicationExtension: + return { "macOSAppExtension" }; + case swift::PlatformKind::tvOSApplicationExtension: + return { "tvOSAppExtension" }; + case swift::PlatformKind::watchOSApplicationExtension: + return { "watchOSAppExtension" }; + case swift::PlatformKind::none: + return { "*" }; + } + llvm_unreachable("invalid platform kind"); +} +} // end anonymous namespace + +Availability::Availability(const AvailableAttr &AvAttr) + : Domain(getDomain(AvAttr)), + Introduced(AvAttr.Introduced), + Deprecated(AvAttr.Deprecated), + Obsoleted(AvAttr.Obsoleted), + Message(AvAttr.Message), + Renamed(AvAttr.Rename), + IsUnconditionallyDeprecated(AvAttr.isUnconditionallyDeprecated()) { + 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 `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 supercede 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; +} + +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); + } + }); // end availability object +} + +bool Availability::empty() const { + return !Introduced.hasValue() && + !Deprecated.hasValue() && + !Obsoleted.hasValue() && + !IsUnconditionallyDeprecated; +} diff --git a/lib/SymbolGraphGen/AvailabilityMixin.h b/lib/SymbolGraphGen/AvailabilityMixin.h new file mode 100644 index 0000000000000..34fc2bc34b2c2 --- /dev/null +++ b/lib/SymbolGraphGen/AvailabilityMixin.h @@ -0,0 +1,78 @@ +//===--- AvailabilityMixin.h - 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_AVAILABILITYMIXIN_H +#define SWIFT_SYMBOLGRAPHGEN_AVAILABILITYMIXIN_H + +#include "swift/AST/Attr.h" +#include "swift/AST/Module.h" +#include "swift/Basic/LLVM.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" + +namespace swift { +namespace symbolgraphgen { + +/// A mixin representing a symbol's effective availability in its module. +struct Availability { + /// The domain to which the availability applies, such as + /// an operating system or Swift itself. + StringRef Domain; + + /// The domain version at which a symbol was introduced if defined. + Optional Introduced; + + /// The domain version at which a symbol was deprecated if defined. + Optional Deprecated; + + /// The domain version at which a symbol was obsoleted if defined. + Optional Obsoleted; + + /// An optional message regarding a symbol's availability. + StringRef Message; + + /// The informal spelling of a new replacement symbol if defined. + StringRef Renamed; + + /// If \c true, is unconditionally deprecated in this \c Domain. + bool IsUnconditionallyDeprecated; + + Availability(const AvailableAttr &AvAttr); + + /// Update this availability from a duplicate @available + /// attribute with the same platform on the same declaration. + /// + /// e.g. + /// @available(macOS, deprecated: 10.15) + /// @available(macOS, deprecated: 10.12) + /// func foo() {} + /// + /// Updates the first availability using the second's information. + void updateFromDuplicate(const Availability &Other); + + /// Update this availability from a parent context's availability. + void updateFromParent(const Availability &Parent); + + /// Returns true if this availability item doesn't have + /// any introduced version, deprecated version, obsoleted version, + /// or uncondtional deprecation status. + /// + /// \note \c message and \c renamed are not considered. + bool empty() const; + + void serialize(llvm::json::OStream &OS) const; +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_AVAILABILITYMIXIN_H diff --git a/lib/SymbolGraphGen/CMakeLists.txt b/lib/SymbolGraphGen/CMakeLists.txt index 64f34c90254b7..57159b83dc59b 100644 --- a/lib/SymbolGraphGen/CMakeLists.txt +++ b/lib/SymbolGraphGen/CMakeLists.txt @@ -1,4 +1,5 @@ add_swift_host_library(swiftSymbolGraphGen STATIC + AvailabilityMixin.cpp DeclarationFragmentPrinter.cpp Edge.cpp JSON.cpp diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index 2e4c12fc3766f..94d3c5bdb2801 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -16,6 +16,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/USRGeneration.h" #include "swift/Basic/SourceManager.h" +#include "AvailabilityMixin.h" #include "JSON.h" #include "Symbol.h" #include "SymbolGraph.h" @@ -325,98 +326,89 @@ void Symbol::serializeLocationMixin(llvm::json::OStream &OS) const { }); } -llvm::Optional -Symbol::getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, - PlatformKind Kind) const { - switch (AgnosticKind) { - // SPM- and Swift-specific availability. - case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: - return { "SwiftPM" }; - case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: - case PlatformAgnosticAvailabilityKind::UnavailableInSwift: - return { "Swift" }; - // Although these are in the agnostic kinds, they are actually a signal - // that there is either platform-specific or completely platform-agnostic. - // They'll be handled below. - case PlatformAgnosticAvailabilityKind::Deprecated: - case PlatformAgnosticAvailabilityKind::Unavailable: - case PlatformAgnosticAvailabilityKind::None: - break; +namespace { +/// Get the availabilities for each domain on a declaration without walking +/// up the parent hierarchy. +/// +/// \param D The declaration whose availabilities the method will collect. +/// \param Availabilities The domain -> availability map that will be updated. +/// \param IsParent If \c true\c, will update or fill availabilities for a given +/// domain with different "inheriting" rules rather than filling from +/// duplicate \c \@available attributes on the same declaration. +void getAvailabilities(const Decl *D, + llvm::StringMap &Availabilities, + bool IsParent) { + // DeclAttributes is a linked list in reverse order from where they + // appeared in the source. Let's re-reverse them. + SmallVector AvAttrs; + for (const auto *Attr : D->getAttrs()) { + if (const auto *AvAttr = dyn_cast(Attr)) { + AvAttrs.push_back(AvAttr); + } } + std::reverse(AvAttrs.begin(), AvAttrs.end()); - // Platform-specific availability. - switch (Kind) { - case swift::PlatformKind::iOS: - return { "iOS" }; - case swift::PlatformKind::macCatalyst: - return { "macCatalyst" }; - case swift::PlatformKind::OSX: - return { "macOS" }; - case swift::PlatformKind::tvOS: - return { "tvOS" }; - case swift::PlatformKind::watchOS: - return { "watchOS" }; - case swift::PlatformKind::iOSApplicationExtension: - return { "iOSAppExtension" }; - case swift::PlatformKind::macCatalystApplicationExtension: - return { "macCatalystAppExtension" }; - case swift::PlatformKind::OSXApplicationExtension: - return { "macOSAppExtension" }; - case swift::PlatformKind::tvOSApplicationExtension: - return { "tvOSAppExtension" }; - case swift::PlatformKind::watchOSApplicationExtension: - return { "watchOSAppExtension" }; - // Platform-agnostic availability, such as "unconditionally deprecated" - // or "unconditionally obsoleted". - case swift::PlatformKind::none: - return None; + // Now go through them in source order. + for (auto *AvAttr : AvAttrs) { + Availability NewAvailability(*AvAttr); + if (NewAvailability.empty()) { + continue; + } + auto ExistingAvailability = Availabilities.find(NewAvailability.Domain); + if (ExistingAvailability != Availabilities.end()) { + // There are different rules for filling in missing components + // or replacing existing components from a parent's @available + // attribute compared to duplicate @available attributes on the + // same declaration. + // See the respective methods below for an explanation for the + // replacement/filling rules. + if (IsParent) { + ExistingAvailability->getValue().updateFromParent(NewAvailability); + } else { + ExistingAvailability->getValue().updateFromDuplicate(NewAvailability); + } + } else { + // There are no availabilities for this domain yet, so either + // inherit the parent's in its entirety or set it from this declaration. + Availabilities.insert(std::make_pair(NewAvailability.Domain, + NewAvailability)); + } } - llvm_unreachable("invalid platform kind"); } -void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { - SmallVector Availabilities; - for (const auto *Attr : VD->getAttrs()) { - if (const auto *AvAttr = dyn_cast(Attr)) { - Availabilities.push_back(AvAttr); +/// Get the availabilities of a declaration, considering all of its +/// parent context's except for the module. +void getInheritedAvailabilities(const Decl *D, +llvm::StringMap &Availabilities) { + getAvailabilities(D, Availabilities, /*IsParent*/false); + + auto CurrentContext = D->getDeclContext(); + while (CurrentContext) { + if (const auto *Parent = CurrentContext->getAsDecl()) { + if (isa(Parent)) { + return; + } + getAvailabilities(Parent, Availabilities, /*IsParent*/true); } + CurrentContext = CurrentContext->getParent(); } +} + +} // end anonymous namespace + +void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { + llvm::StringMap Availabilities; + getInheritedAvailabilities(VD, Availabilities); + if (Availabilities.empty()) { return; } - OS.attributeArray("availability", [&](){ - for (const auto *AvAttr : Availabilities) { - OS.object([&](){ - auto Domain = getDomain(AvAttr->getPlatformAgnosticAvailability(), - AvAttr->Platform); - if (Domain) { - OS.attribute("domain", *Domain); - } - if (AvAttr->Introduced) { - AttributeRAII Introduced("introduced", OS); - symbolgraphgen::serialize(*AvAttr->Introduced, OS); - } - if (AvAttr->Deprecated) { - AttributeRAII Deprecated("deprecated", OS); - symbolgraphgen::serialize(*AvAttr->Deprecated, OS); - } - if (AvAttr->Obsoleted) { - AttributeRAII Obsoleted("obsoleted", OS); - symbolgraphgen::serialize(*AvAttr->Obsoleted, OS); - } - if (!AvAttr->Message.empty()) { - OS.attribute("message", AvAttr->Message); - } - if (!AvAttr->Rename.empty()) { - OS.attribute("renamed", AvAttr->Rename); - } - if (AvAttr->isUnconditionallyDeprecated()) { - OS.attribute("isUnconditionallyDeprecated", true); - } - }); // end availability object + OS.attributeArray("availability", [&]{ + for (const auto &Availability : Availabilities) { + Availability.getValue().serialize(OS); } - }); // end availability: [] + }); } void Symbol::serialize(llvm::json::OStream &OS) const { diff --git a/lib/SymbolGraphGen/Symbol.h b/lib/SymbolGraphGen/Symbol.h index 4e5987e5b0274..2be6765157b8c 100644 --- a/lib/SymbolGraphGen/Symbol.h +++ b/lib/SymbolGraphGen/Symbol.h @@ -21,7 +21,7 @@ namespace swift { namespace symbolgraphgen { -struct AvailabilityDomain; +struct Availability; struct SymbolGraphASTWalker; struct SymbolGraph; @@ -68,10 +68,6 @@ class Symbol { void serializeLocationMixin(llvm::json::OStream &OS) const; - llvm::Optional - getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, - PlatformKind Kind) const; - void serializeAvailabilityMixin(llvm::json::OStream &OS) const; public: diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Basic.swift similarity index 65% rename from test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift rename to test/SymbolGraph/Symbols/Mixins/Availability/Basic.swift index 32bee2c712005..7f7eb9eefc714 100644 --- a/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Basic.swift @@ -1,7 +1,9 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift %s -module-name Availability -emit-module -emit-module-path %t/ -// RUN: %target-swift-symbolgraph-extract -module-name Availability -I %t -pretty-print -output-dir %t -// RUN: %FileCheck %s --input-file %t/Availability.symbols.json +// RUN: %target-build-swift %s -module-name Basic -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Basic -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/Basic.symbols.json + +// REQUIRES: OS=macosx @available(macOS, introduced: 10.9, deprecated: 10.10, obsoleted: 10.11, message: "Everyone makes mistakes", renamed: "S2") public struct S {} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/DeprecatedFilled.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/DeprecatedFilled.swift new file mode 100644 index 0000000000000..d588617bc6ca8 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/DeprecatedFilled.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DeprecatedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DeprecatedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/DeprecatedFilled.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS, introduced: 10.0) +@available(macOS, deprecated: 10.1) +public func foo() {} + +// CHECK-LABEL: "precise": "s:16DeprecatedFilled3fooyyF", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 10, +// CHECK-NEXT: "minor": 0 +// CHECK-NEXT: }, +// CHECK-NEXT: "deprecated": { +// CHECK-NEXT: "major": 10, +// CHECK-NEXT: "minor": 1 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/DeprecatedReplaced.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/DeprecatedReplaced.swift new file mode 100644 index 0000000000000..aed37f934592c --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/DeprecatedReplaced.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DeprecatedReplaced -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DeprecatedReplaced -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/DeprecatedReplaced.symbols.json --check-prefix=DEPRECATED +// RUN: %FileCheck %s --input-file %t/DeprecatedReplaced.symbols.json --check-prefix=NOTDEPRECATED + +// REQUIRES: OS=macosx + +@available(macOS, deprecated: 7.0) +@available(macOS, deprecated: 11.0) +public func foo() {} + +// Last `deprecated` wins. +// DEPRECATED-LABEL: "precise": "s:18DeprecatedReplaced3fooyyF", +// DEPRECATED: "availability": [ +// DEPRECATED-NEXT: { +// DEPRECATED-NEXT: "domain": "macOS", +// DEPRECATED-NEXT: "deprecated": { +// DEPRECATED-NEXT: "major": 11, +// DEPRECATED-NEXT: "minor": 0 +// DEPRECATED-NEXT: } +// DEPRECATED-NEXT: } +// DEPRECATED-NEXT: ] + +@available(macOS, deprecated: 10.0) +@available(macOS, introduced: 10.0) +public func noLongerDeprecated() {} + +// NOTDEPRECATED: "precise": "s:18DeprecatedReplaced08noLongerA0yyF", +// NOTDEPRECATED: "availability": [ +// NOTDEPRECATED-NEXT: { +// NOTDEPRECATED-NEXT: "domain": "macOS", +// NOTDEPRECATED-NEXT: "introduced": { +// NOTDEPRECATED-NEXT: "major": 10, +// NOTDEPRECATED-NEXT: "minor": 0 +// NOTDEPRECATED-NEXT: } +// NOTDEPRECATED-NEXT: } +// NOTDEPRECATED-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/IntroducedFilled.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/IntroducedFilled.swift new file mode 100644 index 0000000000000..9c6d7cd2d2538 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/IntroducedFilled.swift @@ -0,0 +1,23 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name IntroducedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name IntroducedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/IntroducedFilled.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS, deprecated: 10.0) +@available(macOS, introduced: 10.0) +public func foo() {} + +// This effectively erases the deprecation. + +// CHECK: "precise": "s:16IntroducedFilled3fooyyF" +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 10, +// CHECK-NEXT: "minor": 0 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/IntroducedReplaced.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/IntroducedReplaced.swift new file mode 100644 index 0000000000000..4aaad721995c6 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/IntroducedReplaced.swift @@ -0,0 +1,23 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name IntroducedReplaced -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name IntroducedReplaced -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/IntroducedReplaced.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS, introduced: 10.12) +@available(macOS, introduced: 10.11) +@available(macOS, introduced: 10.10) +public func foo() {} + +// Highest `introduced` wins. +// CHECK-LABEL: "precise": "s:18IntroducedReplaced3fooyyF", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 10, +// CHECK-NEXT: "minor": 12 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/MessageLastWins.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/MessageLastWins.swift new file mode 100644 index 0000000000000..157ea906da57d --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/MessageLastWins.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name MessageLastWins -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name MessageLastWins -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/MessageLastWins.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS, deprecated, message: "first") +@available(macOS, deprecated, message: "second") +@available(iOS, deprecated, message: "iOS") +public func foo() {} + +// CHECK-LABEL: "precise": "s:15MessageLastWins3fooyyF", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "message": "second", +// CHECK-NEXT: "isUnconditionallyDeprecated": true +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "iOS", +// CHECK-NEXT: "message": "iOS", +// CHECK-NEXT: "isUnconditionallyDeprecated": true +// CHECK-NEXT: } +// CHECK-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/ObsoletedFilled.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/ObsoletedFilled.swift new file mode 100644 index 0000000000000..d53a3b70d3396 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/ObsoletedFilled.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ObsoletedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ObsoletedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/ObsoletedFilled.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS, introduced: 10.0) +@available(macOS, obsoleted: 10.999) +public func foo() {} + +// CHECK-LABEL: "precise": "s:15ObsoletedFilled3fooyyF", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 10, +// CHECK-NEXT: "minor": 0 +// CHECK-NEXT: }, +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 10, +// CHECK-NEXT: "minor": 999 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/ObsoletedReplaced.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/ObsoletedReplaced.swift new file mode 100644 index 0000000000000..d092882998e42 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/ObsoletedReplaced.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ObsoletedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ObsoletedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/ObsoletedFilled.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS, obsoleted: 10.999) +@available(macOS, obsoleted: 10.888) +public func foo() {} + +// CHECK: "precise": "s:15ObsoletedFilled3fooyyF" +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 10, +// CHECK-NEXT: "minor": 888 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/DeprecatedFilled.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/DeprecatedFilled.swift new file mode 100644 index 0000000000000..2ffebb9152889 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/DeprecatedFilled.swift @@ -0,0 +1,41 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DeprecatedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DeprecatedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/DeprecatedFilled.symbols.json +// RUN: %FileCheck %s --input-file %t/DeprecatedFilled.symbols.json --check-prefix=TRANSITIVE + +// REQUIRES: OS=macosx + +@available(macOS, deprecated: 10.15) +public struct S { + public func foo() {} +} + +// CHECK-LABEL: "precise": "s:16DeprecatedFilled1SV3fooyyF", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "deprecated": { +// CHECK-NEXT: "major": 10, +// CHECK-NEXT: "minor": 15 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] + +@available(macOS, deprecated: 10.15) +public struct Outer { + public struct Inner { + // TRANSITIVE-LABEL: "precise": "s:16DeprecatedFilled5OuterV5InnerV3fooyyF" + // TRANSITIVE: "availability": [ + // TRANSITIVE-NEXT: { + // TRANSITIVE-NEXT: "domain": "macOS", + // TRANSITIVE-NEXT: "deprecated": { + // TRANSITIVE-NEXT: "major": 10, + // TRANSITIVE-NEXT: "minor": 15 + // TRANSITIVE-NEXT: } + // TRANSITIVE-NEXT: } + // TRANSITIVE-NEXT: ] + public func foo() {} + } +} + diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/DeprecatedReplaced.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/DeprecatedReplaced.swift new file mode 100644 index 0000000000000..0c63463dcda8c --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/DeprecatedReplaced.swift @@ -0,0 +1,98 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DeprecatedReplaced -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DeprecatedReplaced -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/DeprecatedReplaced.symbols.json --check-prefix=LESSTHAN +// RUN: %FileCheck %s --input-file %t/DeprecatedReplaced.symbols.json --check-prefix=GREATERTHAN +// RUN: %FileCheck %s --input-file %t/DeprecatedReplaced.symbols.json --check-prefix=EQUAL +// RUN: %FileCheck %s --input-file %t/DeprecatedReplaced.symbols.json --check-prefix=TRANSITIVELESSTHAN +// RUN: %FileCheck %s --input-file %t/DeprecatedReplaced.symbols.json --check-prefix=TRANSITIVEGREATERTHAN +// RUN: %FileCheck %s --input-file %t/DeprecatedReplaced.symbols.json --check-prefix=TRANSITIVEEQUAL + +// REQUIRES: OS=macosx + +@available(macOS, deprecated: 10.15) +public struct S { +// LESSTHAN-LABEL: "precise": "s:18DeprecatedReplaced1SV8lessThanyyF", +// LESSTHAN: "availability": [ +// LESSTHAN-NEXT: { +// LESSTHAN-NEXT: "domain": "macOS", +// LESSTHAN-NEXT: "deprecated": { +// LESSTHAN-NEXT: "major": 10, +// LESSTHAN-NEXT: "minor": 10 +// LESSTHAN-NEXT: } +// LESSTHAN-NEXT: } +// LESSTHAN-NEXT: ] + @available(macOS, deprecated: 10.10) + public func lessThan() {} + +// GREATERTHAN-LABEL: "precise": "s:18DeprecatedReplaced1SV11greaterThanyyF", +// GREATERTHAN: "availability": [ +// GREATERTHAN-NEXT: { +// GREATERTHAN-NEXT: "domain": "macOS", +// GREATERTHAN-NEXT: "deprecated": { +// GREATERTHAN-NEXT: "major": 10, +// GREATERTHAN-NEXT: "minor": 15 +// GREATERTHAN-NEXT: } +// GREATERTHAN-NEXT: } +// GREATERTHAN-NEXT: ] + @available(macOS, deprecated: 10.16) + public func greaterThan() {} + +// EQUAL-LABEL: "precise": "s:18DeprecatedReplaced1SV5equalyyF", +// EQUAL: "availability": [ +// EQUAL-NEXT: { +// EQUAL-NEXT: "domain": "macOS", +// EQUAL-NEXT: "deprecated": { +// EQUAL-NEXT: "major": 10, +// EQUAL-NEXT: "minor": 15 +// EQUAL-NEXT: } +// EQUAL-NEXT: } +// EQUAL-NEXT: ] + @available(macOS, deprecated: 10.15) + public func equal() {} +} + +@available(macOS, deprecated: 10.15) +public struct Outer { + public struct Inner { + // TRANSITIVELESSTHAN-LABEL: "precise": "s:18DeprecatedReplaced5OuterV5InnerV8lessThanyyF" + // TRANSITIVELESSTHAN: "availability": [ + // TRANSITIVELESSTHAN-NEXT: { + // TRANSITIVELESSTHAN-NEXT: "domain": "macOS", + // TRANSITIVELESSTHAN-NEXT: "deprecated": { + // TRANSITIVELESSTHAN-NEXT: "major": 10, + // TRANSITIVELESSTHAN-NEXT: "minor": 10 + // TRANSITIVELESSTHAN-NEXT: } + // TRANSITIVELESSTHAN-NEXT: } + // TRANSITIVELESSTHAN-NEXT: ] + @available(macOS, deprecated: 10.10) + public func lessThan() {} + + // TRANSITIVEGREATERTHAN-LABEL: "precise": "s:18DeprecatedReplaced5OuterV5InnerV11greaterThanyyF", + // TRANSITIVEGREATERTHAN: "availability": [ + // TRANSITIVEGREATERTHAN-NEXT: { + // TRANSITIVEGREATERTHAN-NEXT: "domain": "macOS", + // TRANSITIVEGREATERTHAN-NEXT: "deprecated": { + // TRANSITIVEGREATERTHAN-NEXT: "major": 10, + // TRANSITIVEGREATERTHAN-NEXT: "minor": 15 + // TRANSITIVEGREATERTHAN-NEXT: } + // TRANSITIVEGREATERTHAN-NEXT: } + // TRANSITIVEGREATERTHAN-NEXT: ] + @available(macOS, deprecated: 10.16) + public func greaterThan() {} + + // TRANSITIVEEQUAL-LABEL:"precise": "s:18DeprecatedReplaced5OuterV5InnerV5equalyyF" + // TRANSITIVEEQUAL: "availability": [ + // TRANSITIVEEQUAL-NEXT: { + // TRANSITIVEEQUAL-NEXT: "domain": "macOS", + // TRANSITIVEEQUAL-NEXT: "deprecated": { + // TRANSITIVEEQUAL-NEXT: "major": 10, + // TRANSITIVEEQUAL-NEXT: "minor": 15 + // TRANSITIVEEQUAL-NEXT: } + // TRANSITIVEEQUAL-NEXT: } + // TRANSITIVEEQUAL-NEXT: ] + @available(macOS, deprecated: 10.15) + public func equal() {} + } +} + diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/IntroducedFilled.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/IntroducedFilled.swift new file mode 100644 index 0000000000000..bee360c3235f2 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/IntroducedFilled.swift @@ -0,0 +1,39 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name IntroducedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name IntroducedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/IntroducedFilled.symbols.json +// RUN: %FileCheck %s --input-file %t/IntroducedFilled.symbols.json --check-prefix=TRANSITIVE + +// REQUIRES: OS=macosx + +@available(macOS, introduced: 10.15) +public struct S { + // CHECK-LABEL: "precise": "s:16IntroducedFilled1SV3fooyyF", + // CHECK: "availability": [ + // CHECK-NEXT: { + // CHECK-NEXT: "domain": "macOS", + // CHECK-NEXT: "introduced": { + // CHECK-NEXT: "major": 10, + // CHECK-NEXT: "minor": 15 + // CHECK-NEXT: } + // CHECK-NEXT: } + // CHECK-NEXT: ] + public func foo() {} +} + +@available(macOS, introduced: 10.15) +public struct Outer { + public struct Inner { + // TRANSITIVE-LABEL: "precise": "s:16IntroducedFilled5OuterV5InnerV3fooyyF" + // TRANSITIVE: "availability": [ + // TRANSITIVE-NEXT: { + // TRANSITIVE-NEXT: "domain": "macOS", + // TRANSITIVE-NEXT: "introduced": { + // TRANSITIVE-NEXT: "major": 10, + // TRANSITIVE-NEXT: "minor": 15 + // TRANSITIVE-NEXT: } + // TRANSITIVE-NEXT: } + // TRANSITIVE-NEXT: ] + public func foo() {} + } +} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/IntroducedReplaced.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/IntroducedReplaced.swift new file mode 100644 index 0000000000000..79bbf24ca5169 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/IntroducedReplaced.swift @@ -0,0 +1,73 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name IntroducedReplaced -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name IntroducedReplaced -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/IntroducedReplaced.symbols.json --check-prefix=GREATERTHAN +// RUN: %FileCheck %s --input-file %t/IntroducedReplaced.symbols.json --check-prefix=EQUAL +// RUN: %FileCheck %s --input-file %t/IntroducedReplaced.symbols.json --check-prefix=TRANSITIVEGREATERTHAN +// RUN: %FileCheck %s --input-file %t/IntroducedReplaced.symbols.json --check-prefix=TRANSITIVEEQUAL + +// REQUIRES: OS=macosx + +@available(macOS, introduced: 10.15) +public struct S { + // Not possible: declarations cannot be more available than an enclosing scope + // @available(macOS, introduced: 10.10) + // public func lessThan() {} + + // GREATERTHAN-LABEL: "precise": "s:18IntroducedReplaced1SV11greaterThanyyF" + // GREATERTHAN: "availability": [ + // GREATERTHAN-NEXT: { + // GREATERTHAN-NEXT: "domain": "macOS", + // GREATERTHAN-NEXT: "introduced": { + // GREATERTHAN-NEXT: "major": 10, + // GREATERTHAN-NEXT: "minor": 16 + // GREATERTHAN-NEXT: } + // GREATERTHAN-NEXT: } + // GREATERTHAN-NEXT: ] + @available(macOS, introduced: 10.16) + public func greaterThan() {} + + // EQUAL-LABEL: "precise": "s:18IntroducedReplaced1SV5equalyyF", + // EQUAL: "availability": [ + // EQUAL-NEXT: { + // EQUAL-NEXT: "domain": "macOS", + // EQUAL-NEXT: "introduced": { + // EQUAL-NEXT: "major": 10, + // EQUAL-NEXT: "minor": 15 + // EQUAL-NEXT: } + // EQUAL-NEXT: } + // EQUAL-NEXT: ] + @available(macOS, introduced: 10.15) + public func equal() {} +} + +@available(macOS, introduced: 10.15) +public struct Outer { + public struct Inner { + // TRANSITIVEGREATERTHAN-LABEL: "precise": "s:18IntroducedReplaced5OuterV5InnerV11greaterThanyyF" + // TRANSITIVEGREATERTHAN: "availability": [ + // TRANSITIVEGREATERTHAN-NEXT: { + // TRANSITIVEGREATERTHAN-NEXT: "domain": "macOS", + // TRANSITIVEGREATERTHAN-NEXT: "introduced": { + // TRANSITIVEGREATERTHAN-NEXT: "major": 10, + // TRANSITIVEGREATERTHAN-NEXT: "minor": 16 + // TRANSITIVEGREATERTHAN-NEXT: } + // TRANSITIVEGREATERTHAN-NEXT: } + // TRANSITIVEGREATERTHAN-NEXT: ] + @available(macOS, introduced: 10.16) + public func greaterThan() {} + + // TRANSITIVEEQUAL-LABEL: precise": "s:18IntroducedReplaced5OuterV5InnerV5equalyyF" + // TRANSITIVEEQUAL: "availability": [ + // TRANSITIVEEQUAL-NEXT: { + // TRANSITIVEEQUAL-NEXT: "domain": "macOS", + // TRANSITIVEEQUAL-NEXT: "introduced": { + // TRANSITIVEEQUAL-NEXT: "major": 10, + // TRANSITIVEEQUAL-NEXT: "minor": 15 + // TRANSITIVEEQUAL-NEXT: } + // TRANSITIVEEQUAL-NEXT: } + // TRANSITIVEEQUAL-NEXT: ] + @available(macOS, introduced: 10.15) + public func equal() {} + } +} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/MessageFilled.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/MessageFilled.swift new file mode 100644 index 0000000000000..db311d67391d3 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/MessageFilled.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name MessageFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name MessageFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/MessageFilled.symbols.json +// RUN: %FileCheck %s --input-file %t/MessageFilled.symbols.json --check-prefix=TRANSITIVE + +// REQUIRES: OS=macosx + +@available(macOS, deprecated, message: "S message") +public struct S { + @available(macOS, deprecated) + public func foo() {} +} + +// A child never inherits its parent's availability message because it +// may not make sense 100% of the time. + +// CHECK-LABEL: "precise": "s:13MessageFilled1SV3fooyyF", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "isUnconditionallyDeprecated": true +// CHECK-NEXT: } + +@available(macOS, deprecated, message: "Outer message") +public struct Outer { + public struct Inner { + // TRANSITIVE-LABEL: "precise": "s:13MessageFilled5OuterV5InnerV3fooyyF" + // TRANSITIVE: "availability": [ + // TRANSITIVE-NEXT: { + // TRANSITIVE-NEXT: "domain": "macOS", + // TRANSITIVE-NEXT: "isUnconditionallyDeprecated": true + // TRANSITIVE-NEXT: } + @available(macOS, deprecated) + public func foo() {} + } +} + diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/MessageReplaced.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/MessageReplaced.swift new file mode 100644 index 0000000000000..b59480c8e2ae8 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/MessageReplaced.swift @@ -0,0 +1,40 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name MessageFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name MessageFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/MessageFilled.symbols.json +// RUN: %FileCheck %s --input-file %t/MessageFilled.symbols.json --check-prefix=TRANSITIVE + +// REQUIRES: OS=macosx + +@available(macOS, deprecated, message: "S message") +public struct S { + // A child never inherits its parent's availability message because it + // may not make sense 100% of the time. + + // CHECK-LABEL: "precise": "s:13MessageFilled1SV3fooyyF" + // CHECK: "availability": [ + // CHECK-NEXT: { + // CHECK-NEXT: "domain": "macOS", + // CHECK-NEXT: "message": "foo() message", + // CHECK-NEXT: "isUnconditionallyDeprecated": true + // CHECK-NEXT: } + @available(macOS, deprecated, message: "foo() message") + public func foo() {} +} + + +@available(macOS, deprecated, message: "Outer message") +public struct Outer { + public struct Inner { + // TRANSITIVE-LABEL: "precise": "s:13MessageFilled5OuterV5InnerV3fooyyF" + // TRANSITIVE: "availability": [ + // TRANSITIVE-NEXT: { + // TRANSITIVE-NEXT: "domain": "macOS", + // TRANSITIVE-NEXT: "message": "Inner.foo() message", + // TRANSITIVE-NEXT: "isUnconditionallyDeprecated": true + // TRANSITIVE-NEXT: } + // TRANSITIVE-NEXT: ] + @available(macOS, deprecated, message: "Inner.foo() message") + public func foo() {} + } +} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedFilled.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedFilled.swift new file mode 100644 index 0000000000000..d50718b4c34fe --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedFilled.swift @@ -0,0 +1,40 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name IntroducedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name IntroducedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/IntroducedFilled.symbols.json +// RUN: %FileCheck %s --input-file %t/IntroducedFilled.symbols.json --check-prefix=TRANSITIVE + +// REQUIRES: OS=macosx + +@available(macOS, obsoleted: 10.15) +public struct S { + public func foo() {} +} + +// CHECK-LABEL: "precise": "s:16IntroducedFilled1SV3fooyyF", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 10, +// CHECK-NEXT: "minor": 15 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] + +@available(macOS, obsoleted: 10.15) +public struct Outer { + public struct Inner { + // TRANSITIVE-LABEL: "precise": "s:16IntroducedFilled5OuterV5InnerV3fooyyF" + // TRANSITIVE: "availability": [ + // TRANSITIVE-NEXT: { + // TRANSITIVE-NEXT: "domain": "macOS", + // TRANSITIVE-NEXT: "obsoleted": { + // TRANSITIVE-NEXT: "major": 10, + // TRANSITIVE-NEXT: "minor": 15 + // TRANSITIVE-NEXT: } + // TRANSITIVE-NEXT: } + // TRANSITIVE-NEXT: ] + public func foo() {} + } +} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedReplaced.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedReplaced.swift new file mode 100644 index 0000000000000..7ac2ebe996032 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/ObsoletedReplaced.swift @@ -0,0 +1,98 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ObsoletedReplaced -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ObsoletedReplaced -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/ObsoletedReplaced.symbols.json --check-prefix=LESSTHAN +// RUN: %FileCheck %s --input-file %t/ObsoletedReplaced.symbols.json --check-prefix=GREATERTHAN +// RUN: %FileCheck %s --input-file %t/ObsoletedReplaced.symbols.json --check-prefix=EQUAL +// RUN: %FileCheck %s --input-file %t/ObsoletedReplaced.symbols.json --check-prefix=TRANSITIVELESSTHAN +// RUN: %FileCheck %s --input-file %t/ObsoletedReplaced.symbols.json --check-prefix=TRANSITIVEGREATERTHAN +// RUN: %FileCheck %s --input-file %t/ObsoletedReplaced.symbols.json --check-prefix=TRANSITIVEEQUAL + +// REQUIRES: OS=macosx + +@available(macOS, obsoleted: 10.15) +public struct S { + // LESSTHAN-LABEL: "precise": "s:17ObsoletedReplaced1SV8lessThanyyF", + // LESSTHAN: "availability": [ + // LESSTHAN-NEXT: { + // LESSTHAN-NEXT: "domain": "macOS", + // LESSTHAN-NEXT: "obsoleted": { + // LESSTHAN-NEXT: "major": 10, + // LESSTHAN-NEXT: "minor": 10 + // LESSTHAN-NEXT: } + // LESSTHAN-NEXT: } + // LESSTHAN-NEXT: ] + @available(macOS, obsoleted: 10.10) + public func lessThan() {} + + // GREATERTHAN-LABEL: "precise": "s:17ObsoletedReplaced1SV11greaterThanyyF", + // GREATERTHAN: "availability": [ + // GREATERTHAN-NEXT: { + // GREATERTHAN-NEXT: "domain": "macOS", + // GREATERTHAN-NEXT: "obsoleted": { + // GREATERTHAN-NEXT: "major": 10, + // GREATERTHAN-NEXT: "minor": 15 + // GREATERTHAN-NEXT: } + // GREATERTHAN-NEXT: } + // GREATERTHAN-NEXT: ] + @available(macOS, obsoleted: 10.16) + public func greaterThan() {} + + // EQUAL-LABEL: "precise": "s:17ObsoletedReplaced1SV5equalyyF", + // EQUAL: "availability": [ + // EQUAL-NEXT: { + // EQUAL-NEXT: "domain": "macOS", + // EQUAL-NEXT: "obsoleted": { + // EQUAL-NEXT: "major": 10, + // EQUAL-NEXT: "minor": 15 + // EQUAL-NEXT: } + // EQUAL-NEXT: } + // EQUAL-NEXT: ] + @available(macOS, obsoleted: 10.15) + public func equal() {} +} + +@available(macOS, obsoleted: 10.15) +public struct Outer { + public struct Inner { + // TRANSITIVELESSTHAN-LABEL: "precise": "s:17ObsoletedReplaced5OuterV5InnerV8lessThanyyF" + // TRANSITIVELESSTHAN: "availability": [ + // TRANSITIVELESSTHAN-NEXT: { + // TRANSITIVELESSTHAN-NEXT: "domain": "macOS", + // TRANSITIVELESSTHAN-NEXT: "obsoleted": { + // TRANSITIVELESSTHAN-NEXT: "major": 10, + // TRANSITIVELESSTHAN-NEXT: "minor": 10 + // TRANSITIVELESSTHAN-NEXT: } + // TRANSITIVELESSTHAN-NEXT: } + // TRANSITIVELESSTHAN-NEXT: ] + @available(macOS, obsoleted: 10.10) + public func lessThan() {} + + // TRANSITIVEGREATERTHAN-LABEL:"precise": "s:17ObsoletedReplaced5OuterV5InnerV11greaterThanyyF" + // TRANSITIVEGREATERTHAN: "availability": [ + // TRANSITIVEGREATERTHAN-NEXT: { + // TRANSITIVEGREATERTHAN-NEXT: "domain": "macOS", + // TRANSITIVEGREATERTHAN-NEXT: "obsoleted": { + // TRANSITIVEGREATERTHAN-NEXT: "major": 10, + // TRANSITIVEGREATERTHAN-NEXT: "minor": 15 + // TRANSITIVEGREATERTHAN-NEXT: } + // TRANSITIVEGREATERTHAN-NEXT: } + // TRANSITIVEGREATERTHAN-NEXT: ] + @available(macOS, obsoleted: 10.16) + public func greaterThan() {} + + // TRANSITIVEEQUAL-LABEL:"precise": "s:17ObsoletedReplaced5OuterV5InnerV5equalyyF" + // TRANSITIVEEQUAL: "availability": [ + // TRANSITIVEEQUAL-NEXT: { + // TRANSITIVEEQUAL-NEXT: "domain": "macOS", + // TRANSITIVEEQUAL-NEXT: "obsoleted": { + // TRANSITIVEEQUAL-NEXT: "major": 10, + // TRANSITIVEEQUAL-NEXT: "minor": 15 + // TRANSITIVEEQUAL-NEXT: } + // TRANSITIVEEQUAL-NEXT: } + // TRANSITIVEEQUAL-NEXT: ] + @available(macOS, obsoleted: 10.15) + public func equal() {} + } +} + diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedFilled.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedFilled.swift new file mode 100644 index 0000000000000..801a027679268 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedFilled.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name RenamedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name RenamedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/RenamedFilled.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS, deprecated, renamed: "S.bar") +public struct S { + public func foo() {} +} + +@available(macOS, deprecated, renamed: "Other") +public struct Outer { + public struct Inner { + public func foo() {} + } +} + +// A parent's `renamed` field should never replace a child's. +// It wouldn't make sense for a parent and child to both be renamed to the same thing. + +// This will definitely be on the parents, so this is enough to check that it wasn't +// applied to the child. +// CHECK-COUNT-2: "availability": diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedNoVersion.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedNoVersion.swift new file mode 100644 index 0000000000000..89559c435cb8e --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedNoVersion.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name RenamedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name RenamedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/RenamedFilled.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS, renamed: "S.bar") +public struct S { + public func foo() {} +} + +// There is no version information or unconditional deprecation here, +// so we will not expect to see an empty availability mix-in with +// just the `renamed` field, even though there is a domain listed. +// CHECK-NOT: "availability": diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedReplaced.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedReplaced.swift new file mode 100644 index 0000000000000..99d9fa15227bd --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/RenamedReplaced.swift @@ -0,0 +1,42 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name RenamedReplaced -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name RenamedReplaced -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/RenamedReplaced.symbols.json +// RUN: %FileCheck %s --input-file %t/RenamedReplaced.symbols.json --check-prefix=TRANSITIVE + +// REQUIRES: OS=macosx + +@available(macOS, deprecated, renamed: "S.bar") +public struct S { + // A parent's `renamed` field should never replace a child's. + // It wouldn't make sense for a parent and child to both be renamed to the same thing. + + // CHECK-LABEL: "precise": "s:15RenamedReplaced1SV3fooyyF" + // CHECK: "availability": [ + // CHECK-NEXT: { + // CHECK-NEXT: "domain": "macOS", + // CHECK-NEXT: "renamed": "foo.bar", + // CHECK-NEXT: "isUnconditionallyDeprecated": true + // CHECK-NEXT: } + // CHECK-NEXT: ] + @available(macOS, deprecated, renamed: "foo.bar") + public func foo() {} +} + + +@available(macOS, deprecated, renamed: "Other") +public struct Outer { + public struct Inner { + // TRANSITIVE-LABEL: "precise": "s:15RenamedReplaced5OuterV5InnerV3fooyyF" + // TRANSITIVE: "availability": [ + // TRANSITIVE-NEXT: { + // TRANSITIVE-NEXT: "domain": "macOS", + // TRANSITIVE-NEXT: "renamed": "bar", + // TRANSITIVE-NEXT: "isUnconditionallyDeprecated": true + // TRANSITIVE-NEXT: } + // TRANSITIVE-NEXT: ] + @available(macOS, deprecated, renamed: "bar") + public func foo() {} + } +} + diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/UnconditionallyDeprecated.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/UnconditionallyDeprecated.swift new file mode 100644 index 0000000000000..ee45ab7f9faf0 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Inherited/UnconditionallyDeprecated.swift @@ -0,0 +1,19 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DeprecatedFilled -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DeprecatedFilled -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/DeprecatedFilled.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS, deprecated) +public struct S { + public func foo() {} +} + +// CHECK-LABEL: "precise": "s:16DeprecatedFilled1SV3fooyyF", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "isUnconditionallyDeprecated": true +// CHECK-NEXT: } +// CHECK-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift index 899beff3e4eec..a973a0a205207 100644 --- a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift +++ b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift @@ -6,5 +6,9 @@ @available(*, deprecated) public struct UnconditionallyDeprecated {} -// CHECK-NOT: domain -// CHECK: "isUnconditionallyDeprecated": true +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "*", +// CHECK-NEXT: "isUnconditionallyDeprecated": true +// CHECK-NEXT: } +// CHECK-NEXT: ]