Skip to content

Commit 486e6ef

Browse files
authored
Merge pull request #39640 from xymus/check-api-availability-only
[Sema] Intro flag to limit availability checks to the API
2 parents 5830cea + a34953e commit 486e6ef

File tree

6 files changed

+241
-0
lines changed

6 files changed

+241
-0
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ namespace swift {
147147
/// Disable API availability checking.
148148
bool DisableAvailabilityChecking = false;
149149

150+
/// Only check the availability of the API, ignore function bodies.
151+
bool CheckAPIAvailabilityOnly = false;
152+
150153
/// Should conformance availability violations be diagnosed as errors?
151154
bool EnableConformanceAvailabilityErrors = false;
152155

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,10 @@ def disable_availability_checking : Flag<["-"],
465465
"disable-availability-checking">,
466466
HelpText<"Disable checking for potentially unavailable APIs">;
467467

468+
def check_api_availability_only : Flag<["-"],
469+
"check-api-availability-only">,
470+
HelpText<"Only check the availability of the APIs, ignore function bodies">;
471+
468472
def enable_conformance_availability_errors : Flag<["-"],
469473
"enable-conformance-availability-errors">,
470474
HelpText<"Diagnose conformance availability violations as errors">;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
473473

474474
Opts.DisableAvailabilityChecking |=
475475
Args.hasArg(OPT_disable_availability_checking);
476+
Opts.CheckAPIAvailabilityOnly |=
477+
Args.hasArg(OPT_check_api_availability_only);
476478

477479
if (auto A = Args.getLastArg(OPT_enable_conformance_availability_errors,
478480
OPT_disable_conformance_availability_errors)) {
@@ -915,6 +917,12 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args,
915917
Opts.DebugTimeExpressions |=
916918
Args.hasArg(OPT_debug_time_expression_type_checking);
917919

920+
// Checking availability of the API only relies on skipping non-inlinable
921+
// function bodies. Define it first so it can be overridden by the other
922+
// flags.
923+
if (Args.hasArg(OPT_check_api_availability_only))
924+
Opts.SkipFunctionBodies = FunctionBodySkipping::NonInlinable;
925+
918926
// Check for SkipFunctionBodies arguments in order from skipping less to
919927
// skipping more.
920928
if (Args.hasArg(

lib/Sema/TypeCheckAccess.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,6 +1640,20 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
16401640
// "name: TheType" form, we can get better results by diagnosing the TypeRepr.
16411641
UNINTERESTING(Var)
16421642

1643+
static bool shouldCheck(Decl *D) {
1644+
if (D && D->getASTContext().LangOpts.CheckAPIAvailabilityOnly) {
1645+
// Skip whole decl if not API-public.
1646+
if (auto valueDecl = dyn_cast<const ValueDecl>(D)) {
1647+
AccessScope scope =
1648+
valueDecl->getFormalAccessScope(/*useDC*/nullptr,
1649+
/*treatUsableFromInlineAsPublic*/true);
1650+
if (!scope.isPublic())
1651+
return false;
1652+
}
1653+
}
1654+
return true;
1655+
}
1656+
16431657
/// \see visitPatternBindingDecl
16441658
void checkNamedPattern(const NamedPattern *NP,
16451659
const llvm::DenseSet<const VarDecl *> &seenVars) {
@@ -1685,6 +1699,9 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
16851699
}
16861700

16871701
void visitPatternBindingDecl(PatternBindingDecl *PBD) {
1702+
if (!shouldCheck(PBD->getAnchoringVarDecl(0)))
1703+
return;
1704+
16881705
llvm::DenseSet<const VarDecl *> seenVars;
16891706
for (auto idx : range(PBD->getNumPatternEntries())) {
16901707
PBD->getPattern(idx)->forEachNode([&](const Pattern *P) {
@@ -1952,5 +1969,8 @@ void swift::checkAccessControl(Decl *D) {
19521969
if (where.isImplicit())
19531970
return;
19541971

1972+
if (!DeclAvailabilityChecker::shouldCheck(D))
1973+
return;
1974+
19551975
DeclAvailabilityChecker(where).visit(D);
19561976
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/// Derived from api-availability-only with the errors fixed to check that the
2+
/// generated module interface can be built.
3+
4+
// RUN: %empty-directory(%t)
5+
6+
// RUN: %swiftc_driver -emit-module %s -target x86_64-apple-macosx10.15 -emit-module-interface -emit-module-interface-path %t/main.swiftinterface -enable-library-evolution -Xfrontend -check-api-availability-only -verify-emitted-module-interface
7+
// RUN: %target-swift-frontend -typecheck-module-from-interface %t/main.swiftinterface
8+
9+
// REQUIRES: OS=macosx
10+
11+
@available(macOS 11.0, *)
12+
public protocol NewProto {}
13+
14+
@available(macOS 11.0, *)
15+
public func newFunc() {}
16+
17+
@available(macOS 11.0, *)
18+
public func apiFunc(s : NewProto) {
19+
let _: NewProto
20+
newFunc()
21+
}
22+
23+
@available(macOS 11.0, *)
24+
@usableFromInline func usableFromInline(s : NewProto) {
25+
let _: NewProto
26+
newFunc()
27+
}
28+
29+
@available(macOS 11.0, *)
30+
@inlinable func inlinable(s : NewProto) {
31+
let _: NewProto
32+
newFunc()
33+
}
34+
35+
@available(macOS 11.0, *)
36+
@_spi(SomeSPI) public func spiFunc(s : NewProto) {
37+
let _: NewProto
38+
newFunc()
39+
}
40+
41+
func internalFunc(s : NewProto) {
42+
let _: NewProto
43+
newFunc()
44+
}
45+
46+
private func privateFunc(s : NewProto) {
47+
let _: NewProto
48+
newFunc()
49+
}
50+
51+
fileprivate func fileprivateFunc(s : NewProto) {
52+
let _: NewProto
53+
newFunc()
54+
}
55+
56+
public struct Struct {
57+
internal var internalVar: NewProto
58+
private var privateVar: NewProto
59+
fileprivate var fileprivateVar: NewProto
60+
61+
@available(macOS 11.0, *)
62+
public typealias PubTA = NewProto
63+
private typealias PrivateTA = NewProto
64+
65+
@available(macOS 11.0, *)
66+
public func apiFunc(s : NewProto) {
67+
let _: NewProto
68+
newFunc()
69+
}
70+
71+
@available(macOS 11.0, *)
72+
@usableFromInline func usableFromInline(s : NewProto) {
73+
let _: NewProto
74+
newFunc()
75+
}
76+
77+
@available(macOS 11.0, *)
78+
@inlinable func inlinable(s : NewProto) {
79+
let _: NewProto
80+
newFunc()
81+
}
82+
83+
func internalFunc(s : NewProto) {
84+
let _: NewProto
85+
newFunc()
86+
}
87+
88+
private func privateFunc(s : NewProto) {
89+
let _: NewProto
90+
newFunc()
91+
}
92+
93+
fileprivate func fileprivateFunc(s : NewProto) {
94+
let _: NewProto
95+
newFunc()
96+
}
97+
}

test/Sema/api-availability-only.swift

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/// Test that -check-api-availability-only skips what is expected while checking
2+
/// the module API and SPI.
3+
4+
// RUN: %target-typecheck-verify-swift -module-name MyModule -target x86_64-apple-macosx10.15 -check-api-availability-only -enable-library-evolution
5+
6+
// REQUIRES: OS=macosx
7+
8+
@available(macOS 11.0, *)
9+
public protocol NewProto {}
10+
11+
@available(macOS 11.0, *)
12+
public func newFunc() {}
13+
14+
// expected-note @+1 {{add @available attribute to enclosing}}
15+
public func apiFunc(s : NewProto) { // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
16+
let _: NewProto
17+
newFunc()
18+
}
19+
20+
// expected-note @+1 {{add @available attribute to enclosing}}
21+
@usableFromInline func usableFromInline(s : NewProto) { // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
22+
let _: NewProto
23+
newFunc()
24+
}
25+
26+
// expected-note @+1 3 {{add @available attribute to enclosing}}
27+
@inlinable func inlinable(s : NewProto) { // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
28+
29+
// expected-note @+1 {{add 'if #available' version check}}
30+
let _: NewProto // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
31+
32+
// expected-note @+1 {{add 'if #available' version check}}
33+
newFunc() // expected-error {{'newFunc()' is only available in macOS 11.0 or newer}}
34+
}
35+
36+
// expected-note @+1 {{add @available attribute to enclosing}}
37+
@_spi(SomeSPI) public func spiFunc(s : NewProto) { // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
38+
let _: NewProto
39+
newFunc()
40+
}
41+
42+
func internalFunc(s : NewProto) {
43+
let _: NewProto
44+
newFunc()
45+
}
46+
47+
private func privateFunc(s : NewProto) {
48+
let _: NewProto
49+
newFunc()
50+
}
51+
52+
fileprivate func fileprivateFunc(s : NewProto) {
53+
let _: NewProto
54+
newFunc()
55+
}
56+
57+
// expected-note @+1 7 {{add @available attribute to enclosing struct}}
58+
public struct Struct {
59+
public var publicVar: NewProto // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
60+
internal var internalVar: NewProto
61+
private var privateVar: NewProto
62+
fileprivate var fileprivateVar: NewProto
63+
64+
// expected-note @+1 {{add @available attribute to enclosing}}
65+
public typealias PubTA = NewProto // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
66+
private typealias PrivateTA = NewProto
67+
68+
// expected-note @+1 {{add @available attribute to enclosing}}
69+
public func apiFunc(s : NewProto) { // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
70+
let _: NewProto
71+
newFunc()
72+
}
73+
74+
// expected-note @+1 {{add @available attribute to enclosing}}
75+
@usableFromInline func usableFromInline(s : NewProto) { // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
76+
let _: NewProto
77+
newFunc()
78+
}
79+
80+
// expected-note @+1 3 {{add @available attribute to enclosing}}
81+
@inlinable func inlinable(s : NewProto) { // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
82+
83+
// expected-note @+1 {{add 'if #available' version check}}
84+
let _: NewProto // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
85+
86+
// expected-note @+1 {{add 'if #available' version check}}
87+
newFunc() // expected-error {{'newFunc()' is only available in macOS 11.0 or newer}}
88+
}
89+
90+
func internalFunc(s : NewProto) {
91+
let _: NewProto
92+
newFunc()
93+
}
94+
95+
private func privateFunc(s : NewProto) {
96+
let _: NewProto
97+
newFunc()
98+
}
99+
100+
fileprivate func fileprivateFunc(s : NewProto) {
101+
let _: NewProto
102+
newFunc()
103+
}
104+
}
105+
106+
// expected-note @+1 {{add @available attribute to enclosing}}
107+
extension NewProto { // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
108+
public func foo() {}
109+
}

0 commit comments

Comments
 (0)