Skip to content

Commit bf52ff0

Browse files
committed
[Migrator] Conservative and Minimal @objc inference workflows
Based on recommendations in SE-0160, there are two migration workflows: - Conservative: Maintain @objc visibility that was inferred in Swift 3 by adding @objc to all declarations that were implicitily visible to the Objective-C runtime. This is invoked in the migrator by adding the -migrate-keep-objc-visibility flag. - Minimal: Only declarations that must be visible to Objective-C based on their uses (or in cases like dynamic vars) are migrated. rdar://problem/31876357
1 parent cd48479 commit bf52ff0

23 files changed

+105
-53
lines changed

include/swift/Migrator/FixitFilter.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ struct FixitFilter {
102102
Info.ID == diag::deprecated_any_composition.ID ||
103103
Info.ID == diag::deprecated_operator_body.ID ||
104104
Info.ID == diag::unbound_generic_parameter_explicit_fix.ID ||
105-
Info.ID == diag::objc_inference_swift3_addobjc.ID)
105+
Info.ID == diag::objc_inference_swift3_addobjc.ID ||
106+
Info.ID == diag::objc_inference_swift3_dynamic.ID ||
107+
Info.ID == diag::override_swift3_objc_inference.ID ||
108+
Info.ID == diag::objc_inference_swift3_objc_derived.ID)
106109
return true;
107110

108111
return false;

include/swift/Migrator/MigratorOptions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace swift {
2222
struct MigratorOptions {
2323
/// Add `@objc` to declarations that would've been implicitly
2424
/// visible to the Objective-C runtime in Swift 3.
25-
bool AddObjC = false;
25+
bool KeepObjcVisibility = false;
2626

2727
/// Skip the migration phase that repeatedly asks for fix-its from the
2828
/// compiler and applies them. This is generally for debugging.

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,10 @@ def update_code : Flag<["-"], "update-code">,
420420
HelpText<"Update Swift code">,
421421
Flags<[FrontendOption, HelpHidden, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
422422

423+
def migrate_keep_objc_visibility: Flag<["-"], "migrate-keep-objc-visibility">,
424+
Flags<[FrontendOption, NoInteractiveOption]>,
425+
HelpText<"When migrating, add '@objc' to declarations that would've been implicitly visible in Swift 3">;
426+
423427
def disable_migrator_fixits: Flag<["-"], "disable-migrator-fixits">,
424428
Flags<[FrontendOption, NoInteractiveOption]>,
425429
HelpText<"Disable the Migrator phase which automatically applies fix-its">;

lib/Driver/ToolChains.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,10 @@ ToolChain::constructInvocation(const CompileJobAction &job,
399399
Arguments.push_back(LoadedModuleTracePath.c_str());
400400
}
401401

402+
if (context.Args.hasArg(options::OPT_migrate_keep_objc_visibility)) {
403+
Arguments.push_back("-migrate-keep-objc-visibility");
404+
}
405+
402406
const std::string &FixitsPath =
403407
context.Output.getAdditionalOutputForType(types::TY_Remapping);
404408
if (!FixitsPath.empty()) {

lib/Frontend/CompilerInvocation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1578,7 +1578,7 @@ bool ParseMigratorArgs(MigratorOptions &Opts, llvm::Triple &Triple,
15781578
DiagnosticEngine &Diags) {
15791579
using namespace options;
15801580

1581-
Opts.AddObjC |= Args.hasArg(OPT_warn_swift3_objc_inference);
1581+
Opts.KeepObjcVisibility |= Args.hasArg(OPT_migrate_keep_objc_visibility);
15821582
Opts.DumpUsr = Args.hasArg(OPT_dump_usr);
15831583

15841584
if (Args.hasArg(OPT_disable_migrator_fixits)) {

lib/Migrator/Migrator.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,20 @@ Migrator::performAFixItMigration() {
9696
Invocation.clearInputs();
9797
Invocation.getLangOptions().EffectiveLanguageVersion = { 4, 0, 0 };
9898

99-
const auto OrigPrimaryInput =
100-
StartInvocation.getFrontendOptions().PrimaryInput.getValue();
99+
// The default subset of @objc fix-its, referred to as "minimal" migration
100+
// in SE-0160, adds @objc to things that clearly must be visible to the
101+
// Objective-C runtime. These are compiler error fix-its.
102+
Invocation.getLangOptions().WarnSwift3ObjCInference = true;
103+
Invocation.getLangOptions().EnableSwift3ObjCInference = false;
104+
105+
// However, if the user selects the workflow to keep the behavior of Swift 3's
106+
// implicit Objective-C visibility, force Swift 3 @objc inference to be on.
107+
// Coupled with the -warn-swift3-objc-inference flag above, we'll get warning
108+
// fix-its from the compiler.
109+
if (getMigratorOptions().KeepObjcVisibility) {
110+
Invocation.getLangOptions().EnableSwift3ObjCInference = true;
111+
}
112+
101113
const auto &OrigFrontendOpts = StartInvocation.getFrontendOptions();
102114

103115
auto InputBuffers = OrigFrontendOpts.InputBuffers;

test/Driver/driver_migration.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// RUN: %swiftc_driver -update-code -migrate-keep-objc-visibility -### %s 2>&1 | %FileCheck -check-prefix=CHECK-KEEP-OBJC %s
2+
3+
// CHECK-KEEP-OBJC: -migrate-keep-objc-visibility
4+
// CHECK-KEEP-OBJC: -emit-remap-file-path {{.*}}.remap
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Foundation
2+
3+
class MyClass : NSObject {
4+
var property : NSObject? = nil
5+
dynamic var dynamicProperty: Int { return 2 }
6+
func foo() {}
7+
func baz() {}
8+
}
9+
10+
extension MyClass {
11+
func bar() {}
12+
}
13+
14+
class MySubClass : MyClass {
15+
override func foo() {}
16+
override func bar() {}
17+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Foundation
2+
3+
class MyClass : NSObject {
4+
var propertyUsedInKeyPath : NSObject? = nil
5+
dynamic var dynamicVarUsedInSelector : Int { return 2 }
6+
func overridden() {}
7+
func usedViaAnyObject() {}
8+
func unused() {}
9+
}
10+
11+
extension MyClass {
12+
func inExtensionAndOverridden() {}
13+
}
14+
15+
class MySubClass : MyClass {
16+
override func overridden() {}
17+
override func inExtensionAndOverridden() {}
18+
}
19+
20+
func test(object: AnyObject, mine: MyClass) {
21+
_ = #selector(MyClass.overridden)
22+
_ = #selector(getter: MyClass.dynamicVarUsedInSelector)
23+
_ = #keyPath(MyClass.propertyUsedInKeyPath)
24+
_ = object.usedViaAnyObject?()
25+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -migrate-keep-objc-visibility -primary-file %S/Inputs/conservative_objc_inference.swift -emit-migrated-file-path %t/migrated_conservative_objc_inference.swift -emit-remap-file-path %t/migrated_conservative_objc_inference.swift.remap -o /dev/null
2+
// RUN: %FileCheck %s < %t/migrated_conservative_objc_inference.swift
3+
// REQUIRES: objc_interop
4+
5+
// CHECK: @objc var property : NSObject? = nil
6+
// CHECK: @objc dynamic var dynamicProperty: Int { return 2 }
7+
// CHECK: @objc func foo() {}
8+
// CHECK: @objc func baz() {}
9+
// CHECK: @objc func bar() {}

test/Migrator/member.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// REQUIRES: objc_interop
2-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/member.swift.result -emit-remap-file-path %t/member.swift.remap
2+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/member.swift.result -emit-remap-file-path %t/member.swift.remap -o /dev/null
33
// RUN: diff -u %S/member.swift.expected %t/member.swift.result
44

55
import Bar

test/Migrator/member.swift.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// REQUIRES: objc_interop
2-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/member.swift.result -emit-remap-file-path %t/member.swift.remap
2+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/member.swift.result -emit-remap-file-path %t/member.swift.remap -o /dev/null
33
// RUN: diff -u %S/member.swift.expected %t/member.swift.result
44

55
import Bar
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -primary-file %S/Inputs/minimal_objc_inference.swift -emit-migrated-file-path %t/migrated_minimal_objc_inference.swift -emit-remap-file-path %t/migrated_minimal_objc_inference.swift.remap -o /dev/null
2+
// RUN: %FileCheck %s < %t/migrated_minimal_objc_inference.swift
3+
// REQUIRES: objc_interop
4+
5+
// CHECK-NOT: @objc var propertyUsedInKeyPath: NSObject? = nil
6+
// CHECK: @objc dynamic var dynamicVarUsedInSelector : Int { return 2 }
7+
// CHECK-NOT: @objc func overridden() {}
8+
// CHECK-NOT: @objc func usedViaAnyObject() {}
9+
// CHECK-NOT: @objc func inExtensionAndOverridden() {}
10+
// CHECK-NOT: @objc func unused() {}
11+

test/Migrator/null_migration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/migrated_null_migration.swift -emit-remap-file-path %t/null_migration.remap
1+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/migrated_null_migration.swift -emit-remap-file-path %t/null_migration.remap -o /dev/null
22
// RUN: diff -u %s %t/migrated_null_migration.swift
33

44
// This file tests that, if all migration passes are no-op,

test/Migrator/objc_inference.swift

Lines changed: 0 additions & 37 deletions
This file was deleted.

test/Migrator/property.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// REQUIRES: objc_interop
2-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/property.swift.result -disable-migrator-fixits
2+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/property.swift.result -disable-migrator-fixits -o /dev/null
33
// RUN: diff -u %S/property.swift.expected %t/property.swift.result
44

55
import Bar

test/Migrator/property.swift.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// REQUIRES: objc_interop
2-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/property.swift.result -disable-migrator-fixits
2+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/property.swift.result -disable-migrator-fixits -o /dev/null
33
// RUN: diff -u %S/property.swift.expected %t/property.swift.result
44

55
import Bar

test/Migrator/rename-init.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// REQUIRES: objc_interop
2-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/rename-init.swift.result -disable-migrator-fixits
2+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/rename-init.swift.result -disable-migrator-fixits -o /dev/null
33
// RUN: diff -u %S/rename-init.swift.expected %t/rename-init.swift.result
44

55
import Bar

test/Migrator/rename-init.swift.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// REQUIRES: objc_interop
2-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/rename-init.swift.result -disable-migrator-fixits
2+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/rename-init.swift.result -disable-migrator-fixits -o /dev/null
33
// RUN: diff -u %S/rename-init.swift.expected %t/rename-init.swift.result
44

55
import Bar

test/Migrator/rename.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// REQUIRES: objc_interop
2-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/rename.swift.result -emit-remap-file-path %t/rename.swift.remap
2+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/rename.swift.result -emit-remap-file-path %t/rename.swift.remap -o /dev/null
33
// RUN: diff -u %S/rename.swift.expected %t/rename.swift.result
44

55
import Bar

test/Migrator/rename.swift.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// REQUIRES: objc_interop
2-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/rename.swift.result -emit-remap-file-path %t/rename.swift.remap
2+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -api-diff-data-file %S/API.json -emit-migrated-file-path %t/rename.swift.result -emit-remap-file-path %t/rename.swift.remap -o /dev/null
33
// RUN: diff -u %S/rename.swift.expected %t/rename.swift.result
44

55
import Bar

test/Migrator/wrap_optional.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// REQUIRES: objc_interop
22
// RUN: rm -rf %t.mod && mkdir -p %t.mod
33
// RUN: %target-swift-frontend -emit-module -o %t.mod/cities.swiftmodule %S/Inputs/cities.swift -module-name Cities -parse-as-library
4-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -disable-migrator-fixits -primary-file %s -I %t.mod -api-diff-data-file %S/API.json -emit-migrated-file-path %t/wrap_optional.swift.result -o %t/wrap_optional.swift.remap
4+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -disable-migrator-fixits -primary-file %s -I %t.mod -api-diff-data-file %S/API.json -emit-migrated-file-path %t/wrap_optional.swift.result -o %t/wrap_optional.swift.remap -o /dev/null
55
// RUN: diff -u %S/wrap_optional.swift.expected %t/wrap_optional.swift.result
66

77
import Cities

test/Migrator/wrap_optional.swift.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// REQUIRES: objc_interop
22
// RUN: rm -rf %t.mod && mkdir -p %t.mod
33
// RUN: %target-swift-frontend -emit-module -o %t.mod/cities.swiftmodule %S/Inputs/cities.swift -module-name Cities -parse-as-library
4-
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -disable-migrator-fixits -primary-file %s -I %t.mod -api-diff-data-file %S/API.json -emit-migrated-file-path %t/wrap_optional.swift.result -o %t/wrap_optional.swift.remap
4+
// RUN: rm -rf %t && mkdir -p %t && %target-swift-frontend -c -update-code -disable-migrator-fixits -primary-file %s -I %t.mod -api-diff-data-file %S/API.json -emit-migrated-file-path %t/wrap_optional.swift.result -o %t/wrap_optional.swift.remap -o /dev/null
55
// RUN: diff -u %S/wrap_optional.swift.expected %t/wrap_optional.swift.result
66

77
import Cities

0 commit comments

Comments
 (0)