Skip to content

Commit cc14185

Browse files
authored
Merge pull request #2622 from Igor-Palaguta/TriggerBooleanNimbleOperators
Trigger boolean nimble operators
2 parents bfebdce + e611831 commit cc14185

File tree

3 files changed

+80
-25
lines changed

3 files changed

+80
-25
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
[Ben Staveley-Taylor](https://github.com/BenStaveleyTaylor)
3939
[#2620](https://github.com/realm/SwiftLint/issues/2620)
4040

41+
* `nimble_operator` now warns about `beTrue()` and `beFalse()`.
42+
[Igor-Palaguta](https://github.com/Igor-Palaguta)
43+
[#2613](https://github.com/realm/SwiftLint/issues/2613)
44+
4145
#### Bug Fixes
4246

4347
* Fix `explicit_type_interface` when used in statements.

Rules.md

+14
Original file line numberDiff line numberDiff line change
@@ -12786,6 +12786,10 @@ expect(x) === x
1278612786
expect(10) == 10
1278712787
```
1278812788
12789+
```swift
12790+
expect(success) == true
12791+
```
12792+
1278912793
```swift
1279012794
expect(object.asyncFunction()).toEventually(equal(1))
1279112795
@@ -12852,6 +12856,16 @@ foo.method {
1285212856
1285312857
```
1285412858
12859+
```swift
12860+
↓expect(success).to(beTrue())
12861+
12862+
```
12863+
12864+
```swift
12865+
↓expect(success).to(beFalse())
12866+
12867+
```
12868+
1285512869
```swift
1285612870
expect(10) > 2
1285712871
↓expect(10).to(beGreaterThan(2))

Source/SwiftLintFramework/Rules/Idiomatic/NimbleOperatorRule.swift

+62-25
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, Correcta
2020
"expect(10) <= 10\n",
2121
"expect(x) === x",
2222
"expect(10) == 10",
23+
"expect(success) == true",
2324
"expect(object.asyncFunction()).toEventually(equal(1))\n",
2425
"expect(actual).to(haveCount(expected))\n",
2526
"""
@@ -39,6 +40,8 @@ public struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, Correcta
3940
"↓expect(10).to(beLessThan(11))\n",
4041
"↓expect(10).to(beLessThanOrEqualTo(10))\n",
4142
"↓expect(x).to(beIdenticalTo(x))\n",
43+
"↓expect(success).to(beTrue())\n",
44+
"↓expect(success).to(beFalse())\n",
4245
"expect(10) > 2\n ↓expect(10).to(beGreaterThan(2))\n"
4346
],
4447
corrections: [
@@ -53,20 +56,39 @@ public struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, Correcta
5356
"↓expect(10).to(beLessThan(11))\n": "expect(10) < 11\n",
5457
"↓expect(10).to(beLessThanOrEqualTo(10))\n": "expect(10) <= 10\n",
5558
"↓expect(x).to(beIdenticalTo(x))\n": "expect(x) === x\n",
59+
"↓expect(success).to(beTrue())\n": "expect(success) == true\n",
60+
"↓expect(success).to(beFalse())\n": "expect(success) == false\n",
61+
"↓expect(success).toNot(beFalse())\n": "expect(success) != false\n",
62+
"↓expect(success).toNot(beTrue())\n": "expect(success) != true\n",
5663
"expect(10) > 2\n ↓expect(10).to(beGreaterThan(2))\n": "expect(10) > 2\n expect(10) > 2\n"
5764
]
5865
)
5966

60-
fileprivate typealias Operators = (to: String?, toNot: String?)
6167
fileprivate typealias MatcherFunction = String
6268

63-
private let operatorsMapping: [MatcherFunction: Operators] = [
64-
"equal": (to: "==", toNot: "!="),
65-
"beIdenticalTo": (to: "===", toNot: "!=="),
66-
"beGreaterThan": (to: ">", toNot: nil),
67-
"beGreaterThanOrEqualTo": (to: ">=", toNot: nil),
68-
"beLessThan": (to: "<", toNot: nil),
69-
"beLessThanOrEqualTo": (to: "<=", toNot: nil)
69+
fileprivate enum Arity {
70+
case nullary(analogueValue: String)
71+
case withArguments
72+
73+
var hasArguments: Bool {
74+
guard case .withArguments = self else {
75+
return false
76+
}
77+
return true
78+
}
79+
}
80+
81+
fileprivate typealias PredicateDescription = (to: String?, toNot: String?, arity: Arity)
82+
83+
private let predicatesMapping: [MatcherFunction: PredicateDescription] = [
84+
"equal": (to: "==", toNot: "!=", .withArguments),
85+
"beIdenticalTo": (to: "===", toNot: "!==", .withArguments),
86+
"beGreaterThan": (to: ">", toNot: nil, .withArguments),
87+
"beGreaterThanOrEqualTo": (to: ">=", toNot: nil, .withArguments),
88+
"beLessThan": (to: "<", toNot: nil, .withArguments),
89+
"beLessThanOrEqualTo": (to: "<=", toNot: nil, .withArguments),
90+
"beTrue": (to: "==", toNot: "!=", .nullary(analogueValue: "true")),
91+
"beFalse": (to: "==", toNot: "!=", .nullary(analogueValue: "false"))
7092
]
7193

7294
public func validate(file: File) -> [StyleViolation] {
@@ -79,11 +101,17 @@ public struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, Correcta
79101
}
80102

81103
private func violationMatchesRanges(in file: File) -> [NSRange] {
82-
let operatorNames = operatorsMapping.keys
83-
let operatorsPattern = "(" + operatorNames.joined(separator: "|") + ")"
104+
let operandPattern = "(.(?!expect\\())+?"
105+
106+
let operatorsPattern = "(" + predicatesMapping.map { name, predicateDescription in
107+
let argumentsPattern = predicateDescription.arity.hasArguments
108+
? operandPattern
109+
: ""
110+
111+
return "\(name)\\(\(argumentsPattern)\\)"
112+
}.joined(separator: "|") + ")"
84113

85-
let variablePattern = "(.(?!expect\\())+?"
86-
let pattern = "expect\\(\(variablePattern)\\)\\.to(Not)?\\(\(operatorsPattern)\\(\(variablePattern)\\)\\)"
114+
let pattern = "expect\\(\(operandPattern)\\)\\.to(Not)?\\(\(operatorsPattern)\\)"
87115

88116
let excludingKinds = SyntaxKind.commentKinds
89117

@@ -117,7 +145,7 @@ public struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, Correcta
117145
var contents = file.contents
118146

119147
for range in matches.sorted(by: { $0.location > $1.location }) {
120-
for (functionName, operatorCorrections) in operatorsMapping {
148+
for (functionName, operatorCorrections) in predicatesMapping {
121149
guard let correctedString = contents.replace(function: functionName,
122150
with: operatorCorrections,
123151
in: range)
@@ -141,26 +169,35 @@ public struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, Correcta
141169
private extension String {
142170
/// Returns corrected string if the correction is possible, otherwise returns nil.
143171
func replace(function name: NimbleOperatorRule.MatcherFunction,
144-
with operators: NimbleOperatorRule.Operators,
172+
with predicateDescription: NimbleOperatorRule.PredicateDescription,
145173
in range: NSRange) -> String? {
146174
let anything = "\\s*(.*?)\\s*"
147175

148-
let toPattern = ("expect\\(\(anything)\\)\\.to\\(\(name)\\(\(anything)\\)\\)", operators.to)
149-
let toNotPattern = ("expect\\(\(anything)\\)\\.toNot\\(\(name)\\(\(anything)\\)\\)", operators.toNot)
150-
151-
var correctedString: String?
176+
let toPattern = ("expect\\(\(anything)\\)\\.to\\(\(name)\\(\(anything)\\)\\)", predicateDescription.to)
177+
let toNotPattern = ("expect\\(\(anything)\\)\\.toNot\\(\(name)\\(\(anything)\\)\\)", predicateDescription.toNot)
152178

153179
for case let (pattern, operatorString?) in [toPattern, toNotPattern] {
154180
let expression = regex(pattern)
155-
if !expression.matches(in: self, options: [], range: range).isEmpty {
156-
correctedString = expression.stringByReplacingMatches(in: self,
157-
options: [],
158-
range: range,
159-
withTemplate: "expect($1) \(operatorString) $2")
160-
break
181+
guard !expression.matches(in: self, options: [], range: range).isEmpty else {
182+
continue
183+
}
184+
185+
let valueReplacementPattern: String
186+
switch predicateDescription.arity {
187+
case .nullary(let analogueValue):
188+
valueReplacementPattern = analogueValue
189+
case .withArguments:
190+
valueReplacementPattern = "$2"
161191
}
192+
193+
let replacementPattern = "expect($1) \(operatorString) \(valueReplacementPattern)"
194+
195+
return expression.stringByReplacingMatches(in: self,
196+
options: [],
197+
range: range,
198+
withTemplate: replacementPattern)
162199
}
163200

164-
return correctedString
201+
return nil
165202
}
166203
}

0 commit comments

Comments
 (0)