Skip to content

Commit 4bdbb28

Browse files
allevatoahoppen
authored andcommitted
[UseShorthandTypeNames] Fix shorthand for optional attributed types.
Like with `some/any` type sugar, when we simplify an `Optional` whose type parameter is an attributed type, we need to wrap it in parentheses so that the `?` doesn't bind with the contained type. Fixes swiftlang#657.
1 parent 949b0d0 commit 4bdbb28

File tree

2 files changed

+52
-9
lines changed

2 files changed

+52
-9
lines changed

Sources/SwiftFormat/Rules/UseShorthandTypeNames.swift

+12-9
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,12 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
238238
) -> TypeSyntax {
239239
var wrappedType = wrappedType
240240

241-
// Function types and some-or-any types must be wrapped in parentheses before using shorthand
242-
// optional syntax, otherwise the "?" will bind incorrectly (in the function case it binds to
243-
// only the result, and in the some-or-any case it only binds to the child protocol). Attach the
244-
// leading trivia to the left-paren that we're adding in these cases.
241+
// Certain types must be wrapped in parentheses before using shorthand optional syntax to avoid
242+
// the "?" from binding incorrectly when re-parsed. Attach the leading trivia to the left-paren
243+
// that we're adding in these cases.
245244
switch Syntax(wrappedType).as(SyntaxEnum.self) {
245+
case .attributedType(let attributedType):
246+
wrappedType = parenthesizedType(attributedType, leadingTrivia: leadingTrivia)
246247
case .functionType(let functionType):
247248
wrappedType = parenthesizedType(functionType, leadingTrivia: leadingTrivia)
248249
case .someOrAnyType(let someOrAnyType):
@@ -319,12 +320,11 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
319320
) -> OptionalChainingExprSyntax? {
320321
guard var wrappedTypeExpr = expressionRepresentation(of: wrappedType) else { return nil }
321322

322-
// Function types and some-or-any types must be wrapped in parentheses before using shorthand
323-
// optional syntax, otherwise the "?" will bind incorrectly (in the function case it binds to
324-
// only the result, and in the some-or-any case it only binds to the child protocol). Attach the
325-
// leading trivia to the left-paren that we're adding in these cases.
323+
// Certain types must be wrapped in parentheses before using shorthand optional syntax to avoid
324+
// the "?" from binding incorrectly when re-parsed. Attach the leading trivia to the left-paren
325+
// that we're adding in these cases.
326326
switch Syntax(wrappedType).as(SyntaxEnum.self) {
327-
case .functionType, .someOrAnyType:
327+
case .attributedType, .functionType, .someOrAnyType:
328328
wrappedTypeExpr = parenthesizedExpr(wrappedTypeExpr, leadingTrivia: leadingTrivia)
329329
default:
330330
// Otherwise, the argument type can safely become an optional by simply appending a "?". If
@@ -448,6 +448,9 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
448448
case .someOrAnyType(let someOrAnyType):
449449
return ExprSyntax(TypeExprSyntax(type: someOrAnyType))
450450

451+
case .attributedType(let attributedType):
452+
return ExprSyntax(TypeExprSyntax(type: attributedType))
453+
451454
default:
452455
return nil
453456
}

Tests/SwiftFormatTests/Rules/UseShorthandTypeNamesTests.swift

+40
Original file line numberDiff line numberDiff line change
@@ -692,4 +692,44 @@ final class UseShorthandTypeNamesTests: LintOrFormatRuleTestCase {
692692
]
693693
)
694694
}
695+
696+
func testAttributedTypesInOptionalsAreParenthesized() {
697+
// If we need to insert parentheses, verify that we do, but also verify that we don't insert
698+
// them unnecessarily.
699+
assertFormatting(
700+
UseShorthandTypeNames.self,
701+
input: """
702+
var x: 1️⃣Optional<consuming P> = S()
703+
var y: 2️⃣Optional<@Sendable (Int) -> Void> = S()
704+
var z = [3️⃣Optional<consuming P>]([S()])
705+
var a = [4️⃣Optional<@Sendable (Int) -> Void>]([S()])
706+
707+
var x: 5️⃣Optional<(consuming P)> = S()
708+
var y: 6️⃣Optional<(@Sendable (Int) -> Void)> = S()
709+
var z = [7️⃣Optional<(consuming P)>]([S()])
710+
var a = [8️⃣Optional<(@Sendable (Int) -> Void)>]([S()])
711+
""",
712+
expected: """
713+
var x: (consuming P)? = S()
714+
var y: (@Sendable (Int) -> Void)? = S()
715+
var z = [(consuming P)?]([S()])
716+
var a = [(@Sendable (Int) -> Void)?]([S()])
717+
718+
var x: (consuming P)? = S()
719+
var y: (@Sendable (Int) -> Void)? = S()
720+
var z = [(consuming P)?]([S()])
721+
var a = [(@Sendable (Int) -> Void)?]([S()])
722+
""",
723+
findings: [
724+
FindingSpec("1️⃣", message: "use shorthand syntax for this 'Optional' type"),
725+
FindingSpec("2️⃣", message: "use shorthand syntax for this 'Optional' type"),
726+
FindingSpec("3️⃣", message: "use shorthand syntax for this 'Optional' type"),
727+
FindingSpec("4️⃣", message: "use shorthand syntax for this 'Optional' type"),
728+
FindingSpec("5️⃣", message: "use shorthand syntax for this 'Optional' type"),
729+
FindingSpec("6️⃣", message: "use shorthand syntax for this 'Optional' type"),
730+
FindingSpec("7️⃣", message: "use shorthand syntax for this 'Optional' type"),
731+
FindingSpec("8️⃣", message: "use shorthand syntax for this 'Optional' type"),
732+
]
733+
)
734+
}
695735
}

0 commit comments

Comments
 (0)