Skip to content

Commit 118e7c3

Browse files
committed
[SymbolGraph] Inherit availability from parent contexts
Also be a little smarter about encountering duplicate `@available` attributes on the same declaration. rdar://63570830
1 parent 6fa4874 commit 118e7c3

26 files changed

+1099
-90
lines changed
+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
//===--- AvailabilityMixin.cpp - Symbol Graph Symbol Availability ---------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "AvailabilityMixin.h"
14+
#include "JSON.h"
15+
16+
using namespace swift;
17+
using namespace symbolgraphgen;
18+
19+
namespace {
20+
StringRef getDomain(const AvailableAttr &AvAttr) {
21+
switch (AvAttr.getPlatformAgnosticAvailability()) {
22+
// SPM- and Swift-specific availability.
23+
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
24+
return { "SwiftPM" };
25+
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
26+
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
27+
return { "Swift" };
28+
// Although these are in the agnostic kinds, they are actually a signal
29+
// that there is either platform-specific or completely platform-agnostic.
30+
// They'll be handled below.
31+
case PlatformAgnosticAvailabilityKind::Deprecated:
32+
case PlatformAgnosticAvailabilityKind::Unavailable:
33+
case PlatformAgnosticAvailabilityKind::None:
34+
break;
35+
}
36+
37+
// Platform-specific availability.
38+
switch (AvAttr.Platform) {
39+
case swift::PlatformKind::iOS:
40+
return { "iOS" };
41+
case swift::PlatformKind::macCatalyst:
42+
return { "macCatalyst" };
43+
case swift::PlatformKind::OSX:
44+
return { "macOS" };
45+
case swift::PlatformKind::tvOS:
46+
return { "tvOS" };
47+
case swift::PlatformKind::watchOS:
48+
return { "watchOS" };
49+
case swift::PlatformKind::iOSApplicationExtension:
50+
return { "iOSAppExtension" };
51+
case swift::PlatformKind::macCatalystApplicationExtension:
52+
return { "macCatalystAppExtension" };
53+
case swift::PlatformKind::OSXApplicationExtension:
54+
return { "macOSAppExtension" };
55+
case swift::PlatformKind::tvOSApplicationExtension:
56+
return { "tvOSAppExtension" };
57+
case swift::PlatformKind::watchOSApplicationExtension:
58+
return { "watchOSAppExtension" };
59+
case swift::PlatformKind::none:
60+
return { "*" };
61+
}
62+
llvm_unreachable("invalid platform kind");
63+
}
64+
} // end anonymous namespace
65+
66+
Availability::Availability(const AvailableAttr &AvAttr)
67+
: Domain(getDomain(AvAttr)),
68+
Introduced(AvAttr.Introduced),
69+
Deprecated(AvAttr.Deprecated),
70+
Obsoleted(AvAttr.Obsoleted),
71+
Message(AvAttr.Message),
72+
Renamed(AvAttr.Rename),
73+
IsUnconditionallyDeprecated(AvAttr.isUnconditionallyDeprecated()) {
74+
assert(!Domain.empty());
75+
}
76+
77+
void
78+
Availability::updateFromDuplicate(const Availability &Other) {
79+
assert(Domain == Other.Domain);
80+
81+
// The highest `introduced` version always wins
82+
// regardless of the order in which they appeared in the source.
83+
if (!Introduced) {
84+
Introduced = Other.Introduced;
85+
} else if (Other.Introduced && *Other.Introduced > *Introduced) {
86+
Introduced = Other.Introduced;
87+
}
88+
89+
// The `deprecated` version that appears last in the source always wins,
90+
// allowing even to erase a previous deprecated.
91+
Deprecated = Other.Deprecated;
92+
93+
// Same for `deprecated` with no version.
94+
IsUnconditionallyDeprecated = Other.IsUnconditionallyDeprecated;
95+
96+
// Same for `obsoleted`.
97+
Obsoleted = Other.Obsoleted;
98+
99+
// The `message` that appears last in the source always wins.
100+
Message = Other.Message;
101+
102+
// Same for `renamed`.
103+
Renamed = Other.Renamed;
104+
}
105+
106+
void
107+
Availability::updateFromParent(const Availability &Parent) {
108+
assert(Domain == Parent.Domain);
109+
110+
// Allow filling, but never replace a child's existing `introduced`
111+
// availability because it can never be less available than the parent anyway.
112+
//
113+
// e.g. you cannot write this:
114+
// @available(macos, introduced: 10.15)
115+
// struct S {
116+
// @available(macos, introduced: 10.14)
117+
// func foo() {}
118+
// }
119+
//
120+
// So the child's `introduced` availability will always
121+
// be >= the parent's.
122+
if (!Introduced) {
123+
Introduced = Parent.Introduced;
124+
}
125+
126+
// Allow filling from the parent.
127+
// For replacement, we will consider a parent's
128+
// earlier deprecation to supercede a child's later deprecation.
129+
if (!Deprecated) {
130+
Deprecated = Parent.Deprecated;
131+
} else if (Parent.Deprecated && *Parent.Deprecated < *Deprecated) {
132+
Deprecated = Parent.Deprecated;
133+
}
134+
135+
// The above regarding `deprecated` also will apply to `obsoleted`.
136+
if (!Obsoleted) {
137+
Obsoleted = Parent.Obsoleted;
138+
} else if (Parent.Obsoleted && *Parent.Obsoleted < *Obsoleted) {
139+
Obsoleted = Parent.Obsoleted;
140+
}
141+
142+
// Never replace or fill a child's `message` with a parent's because
143+
// there may be context at the parent that doesn't apply at the child,
144+
// i.e. it might not always make sense.
145+
146+
// Never replace or fill a child's `renamed` field because it
147+
// doesn't make sense. Just because a parent is renamed doesn't
148+
// mean its child is renamed to the same thing.
149+
150+
// If a parent is unconditionally deprecated, then so are all
151+
// of its children.
152+
IsUnconditionallyDeprecated |= Parent.IsUnconditionallyDeprecated;
153+
}
154+
155+
void Availability::serialize(llvm::json::OStream &OS) const {
156+
OS.object([&](){
157+
OS.attribute("domain", Domain);
158+
if (Introduced) {
159+
AttributeRAII IntroducedAttribute("introduced", OS);
160+
symbolgraphgen::serialize(*Introduced, OS);
161+
}
162+
if (Deprecated) {
163+
AttributeRAII DeprecatedAttribute("deprecated", OS);
164+
symbolgraphgen::serialize(*Deprecated, OS);
165+
}
166+
if (Obsoleted) {
167+
AttributeRAII ObsoletedAttribute("obsoleted", OS);
168+
symbolgraphgen::serialize(*Obsoleted, OS);
169+
}
170+
if (!Message.empty()) {
171+
OS.attribute("message", Message);
172+
}
173+
if (!Renamed.empty()) {
174+
OS.attribute("renamed", Renamed);
175+
}
176+
if (IsUnconditionallyDeprecated) {
177+
OS.attribute("isUnconditionallyDeprecated", true);
178+
}
179+
}); // end availability object
180+
}
181+
182+
bool Availability::empty() const {
183+
return !Introduced.hasValue() &&
184+
!Deprecated.hasValue() &&
185+
!Obsoleted.hasValue() &&
186+
!IsUnconditionallyDeprecated;
187+
}
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//===--- AvailabilityMixin.h - Symbol Graph Symbol Availability -----------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_SYMBOLGRAPHGEN_AVAILABILITYMIXIN_H
14+
#define SWIFT_SYMBOLGRAPHGEN_AVAILABILITYMIXIN_H
15+
16+
#include "swift/AST/Attr.h"
17+
#include "swift/AST/Module.h"
18+
#include "swift/Basic/LLVM.h"
19+
#include "llvm/Support/JSON.h"
20+
#include "llvm/Support/VersionTuple.h"
21+
22+
namespace swift {
23+
namespace symbolgraphgen {
24+
25+
/// A mixin representing a symbol's effective availability in its module.
26+
struct Availability {
27+
/// The domain to which the availability applies, such as
28+
/// an operating system or Swift itself.
29+
StringRef Domain;
30+
31+
/// The domain version at which a symbol was introduced if defined.
32+
Optional<llvm::VersionTuple> Introduced;
33+
34+
/// The domain version at which a symbol was deprecated if defined.
35+
Optional<llvm::VersionTuple> Deprecated;
36+
37+
/// The domain version at which a symbol was obsoleted if defined.
38+
Optional<llvm::VersionTuple> Obsoleted;
39+
40+
/// An optional message regarding a symbol's availability.
41+
StringRef Message;
42+
43+
/// The informal spelling of a new replacement symbol if defined.
44+
StringRef Renamed;
45+
46+
/// If \c true, is unconditionally deprecated in this \c Domain.
47+
bool IsUnconditionallyDeprecated;
48+
49+
Availability(const AvailableAttr &AvAttr);
50+
51+
/// Update this availability from a duplicate @available
52+
/// attribute with the same platform on the same declaration.
53+
///
54+
/// e.g.
55+
/// @available(macOS, deprecated: 10.15)
56+
/// @available(macOS, deprecated: 10.12)
57+
/// func foo() {}
58+
///
59+
/// Updates the first availability using the second's information.
60+
void updateFromDuplicate(const Availability &Other);
61+
62+
/// Update this availability from a parent context's availability.
63+
void updateFromParent(const Availability &Parent);
64+
65+
/// Returns true if this availability item doesn't have
66+
/// any introduced version, deprecated version, obsoleted version,
67+
/// or uncondtional deprecation status.
68+
///
69+
/// \note \c message and \c renamed are not considered.
70+
bool empty() const;
71+
72+
void serialize(llvm::json::OStream &OS) const;
73+
};
74+
75+
} // end namespace symbolgraphgen
76+
} // end namespace swift
77+
78+
#endif // SWIFT_SYMBOLGRAPHGEN_AVAILABILITYMIXIN_H

lib/SymbolGraphGen/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_swift_host_library(swiftSymbolGraphGen STATIC
2+
AvailabilityMixin.cpp
23
DeclarationFragmentPrinter.cpp
34
Edge.cpp
45
JSON.cpp

0 commit comments

Comments
 (0)