Skip to content

Refactor ConvertToTernaryExpr#3252

Open
a7maad-ayman wants to merge 1 commit intoswiftlang:mainfrom
a7maad-ayman:convert-to-ternary-expression
Open

Refactor ConvertToTernaryExpr#3252
a7maad-ayman wants to merge 1 commit intoswiftlang:mainfrom
a7maad-ayman:convert-to-ternary-expression

Conversation

@a7maad-ayman
Copy link

@a7maad-ayman a7maad-ayman commented Jan 21, 2026

Refactor ConvertToTernaryExpr

Implements a new Swift refactoring that converts if-else statements with assignments into ternary expressions.

What it does

Converts this:

let result: Type
if condition {
  result = trueValue
} else {
  result = falseValue
}

Into this:

let result: Type = condition ? trueValue : falseValue

Supported patterns

  • Variable declaration + if-else assignment
  • Standalone if-else assignment
  • Tuple assignments: (a, b) = condition ? (1, 2) : (3, 4)
  • Simple assignments: x = condition ? value1 : value2

Contributes to #2424

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for starting this, @a7maad-ayman. I left some initial comments from my first scan through the PR. Once those are resolved, I’ll have another more detailed look at it.

/// - Optionally, the variable is declared immediately before the if statement
public struct ConvertToTernaryExpression: SyntaxRefactoringProvider {

public static func refactor(syntax: CodeBlockItemListSyntax, in context: Void) throws -> CodeBlockItemListSyntax {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you run swift-format on your changes. Instructions should be in CONTRIBUTING.md

let items = Array(codeBlock)
guard !items.isEmpty else { return nil }

/// Variable declaration followed by if statement.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be simpler to find the if expression to extract first and then just look up if the previous statement is a variable declaration of that name? I feel like that could unify this branch and the one below a little.

Comment on lines +235 to +246
private static func extractCondition(from ifExpr: IfExprSyntax) -> ExprSyntax? {
guard let firstCondition = ifExpr.conditions.first else {
return nil
}

guard case .expression(let condition) = firstCondition.condition else {
return nil
}

return condition
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the entire code in analyzePattern would read easier if these checks would just be inline. This one, for example could just be a single

guard case .expression(let condition) = ifExpr.conditions.first.condition else {
  return nil
}

Similar for most of the other checks

}

private static func extractCondition(from ifExpr: IfExprSyntax) -> ExprSyntax? {
guard let firstCondition = ifExpr.conditions.first else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only apply this transformation if there’s only a single condition. You can use .only for that.

Comment on lines +273 to +274
guard codeBlock.statements.count == 1,
let statement = codeBlock.statements.first
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be .only

let expectedElementCount = 3
let assignmentOperatorIndex = 1

guard elements.count == expectedElementCount,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s just inline 3 here. The variable name doesn’t provide much value. Same for assignmentOperatorIndex.

Comment on lines +339 to +340
if expr.as(TernaryExprSyntax.self) != nil { return true }
if expr.as(ClosureExprSyntax.self) != nil { return true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these too complex for a ternary?

// MARK: - Alternative API for single if statement refactoring
extension ConvertToTernaryExpression {

public static func refactor(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT this doesn’t satisfy any requirement in SyntaxRefactoringProvider. Since most clients will enter the refactoring action through that protocol, this method is practically unreachable. We should be able to perform this refactoring using the main refactor entry point.

}

// MARK: - Builders
private static func withoutTrivia<T: SyntaxProtocol>(_ node: T) -> T {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have .trimmed, which does exactly this.


func testNamedTupleAssignment() throws {
let baseline: CodeBlockItemListSyntax = """
let coordinates: (x: Int, y: Int)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep the type annotation here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants