Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@

### Bug Fixes

* Fix false positive in `void_function_in_ternary` rule when a ternary expression
appears inside an `if` or `switch` expression used as an implicit return value.
[WZBbiao](https://github.com/WZBbiao)
[#5611](https://github.com/realm/SwiftLint/issues/5611)

* Ensure that disable commands work for `redundant_nil_coalescing` rule.
[SimplyDanny](https://github.com/SimplyDanny)
[#6465](https://github.com/realm/SwiftLint/issues/6465)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@ struct VoidFunctionInTernaryConditionRule: Rule {
a &<<= b ? c() : d()
a &-= b ? c() : d()
"""),
Example("""
func makeValue() -> MyStruct {
if condition {
flag ? MyStruct(value: 0) : MyStruct(value: 1)
} else {
MyStruct(value: 2)
}
}
"""),
Example("""
func computeSize(for section: Int) -> CGSize {
switch section {
case 0: isEditing ? CGSize(width: 150, height: 20) : CGSize(width: 100, height: 20)
default: .zero
}
}
"""),
],
triggeringExamples: [
Example("success ↓? askQuestion() : exit()"),
Expand Down Expand Up @@ -182,6 +199,15 @@ private extension ExprListSyntax {

private extension CodeBlockItemSyntax {
var isImplicitReturn: Bool {
isClosureImplicitReturn || isFunctionImplicitReturn ||
isVariableImplicitReturn || isSubscriptImplicitReturn ||
isAccessorImplicitReturn || isIfExprOrSwitchExprImplicitReturn
}

/// Like `isImplicitReturn` but without checking `isIfExprOrSwitchExprImplicitReturn`.
/// Used inside `isIfExprOrSwitchExprImplicitReturn` to avoid recursive calls that can
/// trigger thread-safety issues when multiple rules traverse the syntax tree concurrently.
var isImplicitReturnExcludingIfSwitchExpr: Bool {
isClosureImplicitReturn || isFunctionImplicitReturn ||
isVariableImplicitReturn || isSubscriptImplicitReturn ||
isAccessorImplicitReturn
Expand Down Expand Up @@ -231,6 +257,40 @@ private extension CodeBlockItemSyntax {

return parent.children(viewMode: .sourceAccurate).count == 1
}

/// Returns `true` if this code block item is the sole expression in a branch of an `if` or
/// `switch` expression that is itself used as an implicit return value. This prevents false
/// positives when a ternary that returns a value appears inside an `if`/`switch` expression
/// branch (Swift 5.9+) where the result of the branch is used as the enclosing expression's
/// value.
var isIfExprOrSwitchExprImplicitReturn: Bool {
guard let parent = parent?.as(CodeBlockItemListSyntax.self),
parent.children(viewMode: .sourceAccurate).count == 1 else {
return false
}

// Check if inside an if expression branch (body or else body).
// Chain: CodeBlockItemListSyntax -> CodeBlockSyntax -> IfExprSyntax
// Note: IfExprSyntax used as a statement is wrapped in ExpressionStmtSyntax inside the CodeBlockItemSyntax.
if let ifExpr = parent.parent?.parent?.as(IfExprSyntax.self),
let ifCodeBlockItem = ifExpr.parent?.as(ExpressionStmtSyntax.self)?.parent?.as(CodeBlockItemSyntax.self) {
// Use isImplicitReturnExcludingIfSwitchExpr instead of isImplicitReturn to avoid
// recursive calls that traverse shared syntax nodes concurrently during parallel rule execution.
return ifCodeBlockItem.isImplicitReturnExcludingIfSwitchExpr
}

// Check if inside a switch expression case body.
// Chain: CodeBlockItemListSyntax -> SwitchCaseSyntax -> SwitchCaseListSyntax -> SwitchExprSyntax
// Note: SwitchExprSyntax used as a statement is wrapped in ExpressionStmtSyntax inside the CodeBlockItemSyntax.
if let switchExpr = parent.parent?.parent?.parent?.as(SwitchExprSyntax.self),
let switchCodeBlockItem = switchExpr.parent?.as(ExpressionStmtSyntax.self)?.parent?.as(CodeBlockItemSyntax.self) {
// Use isImplicitReturnExcludingIfSwitchExpr instead of isImplicitReturn to avoid
// recursive calls that traverse shared syntax nodes concurrently during parallel rule execution.
return switchCodeBlockItem.isImplicitReturnExcludingIfSwitchExpr
}

return false
}
}

private extension FunctionSignatureSyntax {
Expand Down