Files
SwiftFormat/Sources/Rules/RedundantNilInit.swift
2026-01-25 09:02:03 -08:00

107 lines
3.6 KiB
Swift

//
// RedundantNilInit.swift
// SwiftFormat
//
// Created by Nick Lockwood on 12/5/16.
// Copyright © 2024 Nick Lockwood. All rights reserved.
//
import Foundation
public extension FormatRule {
/// Remove or insert redundant `= nil` initialization for Optional properties
static let redundantNilInit = FormatRule(
help: "Remove/insert redundant `nil` default value (Optional vars are nil by default).",
options: ["nil-init"]
) { formatter in
let declarations = formatter.parseDeclarations()
declarations.forEachRecursiveDeclaration { declaration in
// Only process var declarations
guard declaration.keyword == "var" else { return }
let varIndex = declaration.keywordIndex
// Check modifiers don't include `lazy` or property wrappers
if declaration.modifiers.contains(where: {
$0 == "lazy" || ($0 != "@objc" && $0.isAttribute)
}) {
return
}
// Preserve as-is in result builders
if formatter.isInResultBuilder(at: varIndex) {
return
}
if let parentType = declaration.parentType {
// Check if this is in a Codable type
if parentType.conformances.contains(where: {
["Codable", "Decodable"].contains($0.conformance.string)
}) {
return
}
// Preserve the value if the struct has a synthesized memberwise init
// before Swift 5.2
if parentType.keyword == "struct",
formatter.index(of: .keyword("init"), after: parentType.openBraceIndex) == nil,
formatter.options.swiftVersion < "5.2"
{
return
}
}
guard let propertyDeclaration = formatter.parsePropertyDeclaration(atIntroducerIndex: varIndex),
let type = propertyDeclaration.type,
type.isOptionalType
else { return }
switch formatter.options.nilInit {
case .remove:
// Remove `= nil` if it exists
if let valueInfo = propertyDeclaration.value,
formatter.tokens[valueInfo.expressionRange] == [.identifier("nil")]
{
// Remove from the space before = to the end of the value
let startIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: valueInfo.assignmentIndex, if: { !$0.isSpaceOrCommentOrLinebreak }) ?? valueInfo.assignmentIndex - 1
formatter.removeTokens(in: startIndex + 1 ... valueInfo.expressionRange.upperBound)
}
case .insert:
// Insert `= nil` if it doesn't exist and this is a stored property
if propertyDeclaration.value == nil, declaration.isStoredProperty {
let tokens: [Token] = [.space(" "), .operator("=", .infix), .space(" "), .identifier("nil")]
formatter.insert(tokens, at: type.range.upperBound + 1)
}
}
}
} examples: {
"""
`--nil-init remove`
```diff
- var foo: Int? = nil
+ var foo: Int?
```
```diff
// doesn't apply to `let` properties
let foo: Int? = nil
```
```diff
// doesn't affect non-nil initialization
var foo: Int? = 0
```
`--nil-init insert`
```diff
- var foo: Int?
+ var foo: Int? = nil
```
"""
}
}