Skip to content

Commit 35e21b8

Browse files
committed
Refactor MigrateToNewIfLetSyntax Into a RefactoringProvider
Pull the MigrateToNewIfLetSyntax transform into SwiftRefactor as its first refactoring action. This action transforms all optional binding conditions in if statements into the new Swift 5.7 if-let shorthand syntax. Before: ```swift if let foo = foo { // ... } ``` After: ```swift if let foo { // ... }
1 parent 9b98573 commit 35e21b8

File tree

4 files changed

+139
-39
lines changed

4 files changed

+139
-39
lines changed

Examples/Package.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ let package = Package(
1010
products: [
1111
.executable(name: "AddOneToIntegerLiterals", targets: ["AddOneToIntegerLiterals"]),
1212
.executable(name: "CodeGenerationUsingSwiftSyntaxBuilder", targets: ["CodeGenerationUsingSwiftSyntaxBuilder"]),
13-
.executable(name: "MigrateToNewIfLetSyntax", targets: ["MigrateToNewIfLetSyntax"]),
1413
],
1514
dependencies: [
1615
.package(path: "../"),
@@ -33,14 +32,5 @@ let package = Package(
3332
path: ".",
3433
exclude: ["README.md", "AddOneToIntegerLiterals.swift", "MigrateToNewIfLetSyntax.swift"]
3534
),
36-
.executableTarget(
37-
name: "MigrateToNewIfLetSyntax",
38-
dependencies: [
39-
.product(name: "SwiftParser", package: "swift-syntax"),
40-
.product(name: "SwiftSyntax", package: "swift-syntax"),
41-
],
42-
path: ".",
43-
exclude: ["README.md", "CodeGenerationUsingSwiftSyntaxBuilder.swift", "AddOneToIntegerLiterals.swift"]
44-
),
4535
]
4636
)

Examples/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ Each example can be executed by navigating into this folder and running `swift r
44

55
- [AddOneToIntegerLiterals](AddOneToIntegerLiterals.swift): Command line tool to add 1 to every integer literal in a source file
66
- [CodeGenerationUsingSwiftSyntaxBuilder](CodeGenerationUsingSwiftSyntaxBuilder.swift): Code-generate a simple source file using SwiftSyntaxBuilder
7-
- [MigrateToNewIfLetSyntax](MigrateToNewIfLetSyntax.swift): Command line tool to transform optional bindings in `if` statements to the new shorthand syntax
87

98
## Some Example Usages
109

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,39 @@
1-
import Foundation
21
import SwiftSyntax
32
import SwiftParser
43

5-
/// MigrateToNewIfLetSyntax will visit each `if` statement in the syntax tree
6-
/// replacing all "old style" optional bindings by the new shorter syntax available
7-
/// since Swift 5.7.
4+
/// ``MigrateToNewIfLetSyntax`` will visit each if statement in the Syntax tree, and
5+
/// checks if the there is an if condition which is of the pre Swift 5.7 "if-let-style"
6+
/// and rewrites it to the new one.
87
///
9-
/// For example, it will turn:
10-
/// ```
8+
/// - Seealso: https://github.com/apple/swift-evolution/blob/main/proposals/0345-if-let-shorthand.md
9+
///
10+
/// ## Before
11+
///
12+
/// ```swift
1113
/// if let foo = foo {
12-
/// ...
14+
/// // ...
1315
/// }
1416
/// ```
15-
/// into:
16-
/// ```
17+
///
18+
/// ## After
19+
///
20+
/// ```swift
1721
/// if let foo {
18-
/// ...
22+
/// // ...
1923
/// }
20-
class MigrateToNewIfLetSyntax: SyntaxRewriter {
21-
// Visit all `if` statements.
22-
override func visit(_ node: IfStmtSyntax) -> StmtSyntax {
24+
public struct MigrateToNewIfLetSyntax: RefactoringProvider {
25+
public static func refactor(syntax node: IfStmtSyntax, in context: ()) -> StmtSyntax? {
2326
// Visit all conditions in the node.
2427
let newConditions = node.conditions.enumerated().map { (index, condition) in
2528
var conditionCopy = condition
2629
// Check if the condition is an optional binding ...
2730
if var binding = condition.condition.as(OptionalBindingConditionSyntax.self),
28-
// ... and has an initializer ...
29-
let initializer = binding.initializer,
31+
// ... that binds an identifier (and not a tuple) ...
32+
let bindingIdentifier = binding.pattern.as(IdentifierPatternSyntax.self),
33+
// ... and has an initializer that is also an identifier ...
34+
let initializerIdentifier = binding.initializer?.value.as(IdentifierExprSyntax.self),
3035
// ... and both sides of the assignment are the same identifiers.
31-
binding.pattern.withoutTrivia().description == initializer.value.withoutTrivia().description {
36+
bindingIdentifier.identifier.text == initializerIdentifier.identifier.text {
3237
// Remove the initializer ...
3338
binding.initializer = nil
3439
// ... and remove whitespace before the comma (in `if` statements with multiple conditions).
@@ -42,15 +47,3 @@ class MigrateToNewIfLetSyntax: SyntaxRewriter {
4247
return StmtSyntax(node.withConditions(ConditionElementListSyntax(newConditions)))
4348
}
4449
}
45-
46-
@main
47-
struct Main {
48-
static func main() throws {
49-
let file = CommandLine.arguments[1]
50-
let url = URL(fileURLWithPath: file)
51-
let source = try String(contentsOf: url, encoding: .utf8)
52-
let sourceFile = Parser.parse(source: source)
53-
let rewritten = MigrateToNewIfLetSyntax().visit(sourceFile)
54-
print(rewritten)
55-
}
56-
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
import SwiftRefactor
14+
import SwiftSyntax
15+
import SwiftSyntaxBuilder
16+
17+
import XCTest
18+
import _SwiftSyntaxTestSupport
19+
20+
final class MigrateToNewIfLetSyntaxTest: XCTestCase {
21+
func testRefactoring() throws {
22+
let baselineSyntax: StmtSyntax = """
23+
if let x = x {}
24+
"""
25+
26+
let expectedSyntax: StmtSyntax = """
27+
if let x {}
28+
"""
29+
30+
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
31+
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
32+
33+
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
34+
35+
AssertStringsEqualWithDiff(expected.description, refactored.description)
36+
}
37+
38+
func testIdempotence() throws {
39+
let baselineSyntax: StmtSyntax = """
40+
if let x = x {}
41+
"""
42+
43+
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
44+
45+
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
46+
let refactoredAgain = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
47+
48+
AssertStringsEqualWithDiff(refactored.description, refactoredAgain.description)
49+
}
50+
51+
func testMultiBinding() throws {
52+
let baselineSyntax: StmtSyntax = """
53+
if let x = x, var y = y, let z = z {}
54+
"""
55+
56+
let expectedSyntax: StmtSyntax = """
57+
if let x, var y, let z {}
58+
"""
59+
60+
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
61+
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
62+
63+
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
64+
65+
AssertStringsEqualWithDiff(expected.description, refactored.description)
66+
}
67+
68+
func testMixedBinding() throws {
69+
let baselineSyntax: StmtSyntax = """
70+
if let x = x, var y = x, let z = y.w {}
71+
"""
72+
73+
let expectedSyntax: StmtSyntax = """
74+
if let x, var y = x, let z = y.w {}
75+
"""
76+
77+
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
78+
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
79+
80+
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
81+
82+
AssertStringsEqualWithDiff(expected.description, refactored.description)
83+
}
84+
85+
func testConditions() throws {
86+
let baselineSyntax: StmtSyntax = """
87+
if let x = x + 1, x == x, !x {}
88+
"""
89+
90+
let expectedSyntax: StmtSyntax = """
91+
if let x = x + 1, x == x, !x {}
92+
"""
93+
94+
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
95+
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
96+
97+
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
98+
99+
AssertStringsEqualWithDiff(expected.description, refactored.description)
100+
}
101+
102+
func testWhitespaceNormalization() throws {
103+
let baselineSyntax: StmtSyntax = """
104+
if let x = x , let y = y {}
105+
"""
106+
107+
let expectedSyntax: StmtSyntax = """
108+
if let x, let y {}
109+
"""
110+
111+
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
112+
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
113+
114+
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
115+
116+
AssertStringsEqualWithDiff(expected.description, refactored.description)
117+
}
118+
}

0 commit comments

Comments
 (0)