Files

114 lines
4.1 KiB
Swift

//
// IsEmpty.swift
// SwiftFormat
//
// Created by Nick Lockwood on 12/15/18.
// Copyright © 2024 Nick Lockwood. All rights reserved.
//
import Foundation
public extension FormatRule {
/// Replace count == 0 with isEmpty
static let isEmpty = FormatRule(
help: "Prefer `isEmpty` over comparing `count` against zero.",
disabledByDefault: true
) { formatter in
formatter.forEach(.identifier("count")) { i, _ in
guard let dotIndex = formatter.index(of: .nonSpaceOrLinebreak, before: i, if: {
$0.isOperator(".")
}), let opIndex = formatter.index(of: .nonSpaceOrLinebreak, after: i, if: {
$0.isOperator
}), let endIndex = formatter.index(of: .nonSpaceOrLinebreak, after: opIndex, if: {
$0 == .number("0", .integer)
}) else {
return
}
var isOptional = false
var index = dotIndex
var wasIdentifier = false
loop: while true {
guard let prev = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: index) else {
break
}
switch formatter.tokens[prev] {
case .operator("!", _), .operator(".", _):
break // Ignored
case .operator("?", _):
if formatter.tokens[prev - 1].isSpace {
break loop
}
isOptional = true
case let .operator(op, .infix):
guard ["||", "&&", ":"].contains(op) else {
return
}
break loop
case .keyword, .delimiter, .startOfScope:
break loop
case .identifier:
if wasIdentifier {
break loop
}
wasIdentifier = true
index = prev
continue
case .endOfScope:
guard !wasIdentifier, let start = formatter.index(of: .startOfScope, before: prev) else {
break loop
}
wasIdentifier = false
index = start
continue
default:
break
}
wasIdentifier = false
index = prev
}
let isEmpty: Bool
switch formatter.tokens[opIndex] {
case .operator("==", .infix): isEmpty = true
case .operator("!=", .infix), .operator(">", .infix): isEmpty = false
default: return
}
if isEmpty {
if isOptional {
formatter.replaceTokens(in: i ... endIndex, with: [
.identifier("isEmpty"), .space(" "), .operator("==", .infix), .space(" "), .identifier("true"),
])
} else {
formatter.replaceTokens(in: i ... endIndex, with: .identifier("isEmpty"))
}
} else {
if isOptional {
formatter.replaceTokens(in: i ... endIndex, with: [
.identifier("isEmpty"), .space(" "), .operator("!=", .infix), .space(" "), .identifier("true"),
])
} else {
formatter.replaceTokens(in: i ... endIndex, with: .identifier("isEmpty"))
formatter.insert(.operator("!", .prefix), at: index)
}
}
}
} examples: {
"""
```diff
- if foo.count == 0 {
+ if foo.isEmpty {
- if foo.count > 0 {
+ if !foo.isEmpty {
- if foo?.count == 0 {
+ if foo?.isEmpty == true {
```
***NOTE:*** In rare cases, the `isEmpty` rule may insert an `isEmpty` call for
a type that doesn't implement that property, breaking the program. For this
reason, the rule is disabled by default, and must be manually enabled via the
`--enable isEmpty` option.
"""
}
}