mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
Make control_statement rule substitution correctable
This commit is contained in:
@@ -71,6 +71,8 @@
|
||||
|
||||
* Add GitHub Actions Logging reporter (`github-actions-logging`).
|
||||
[Norio Nomura](https://github.com/norio-nomura)
|
||||
* Make `control_statement` rule substitution correctable.
|
||||
[MaxHaertwig](https://github.com/maxhaertwig)
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
|
||||
@@ -2953,7 +2953,7 @@ resourceString.range(of: rule.regex, options: .regularExpression) != nil
|
||||
|
||||
Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
|
||||
--- | --- | --- | --- | --- | ---
|
||||
`control_statement` | Enabled | No | style | No | 3.0.0
|
||||
`control_statement` | Enabled | Yes | style | No | 3.0.0
|
||||
|
||||
`if`, `for`, `guard`, `switch`, `while`, and `catch` statements shouldn't unnecessarily wrap their conditionals or arguments in parentheses.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import SourceKittenFramework
|
||||
|
||||
public struct ControlStatementRule: ConfigurationProviderRule, AutomaticTestableRule {
|
||||
public struct ControlStatementRule: ConfigurationProviderRule, AutomaticTestableRule, SubstitutionCorrectableRule {
|
||||
public var configuration = SeverityConfiguration(.warning)
|
||||
|
||||
public init() {}
|
||||
@@ -53,10 +53,39 @@ public struct ControlStatementRule: ConfigurationProviderRule, AutomaticTestable
|
||||
"↓switch (foo) {\n",
|
||||
"do {\n} ↓catch(let error as NSError) {\n}",
|
||||
"↓if (max(a, b) < c) {\n"
|
||||
],
|
||||
corrections: [
|
||||
"↓if (condition) {\n": "if condition {\n",
|
||||
"↓if(condition) {\n": "if condition {\n",
|
||||
"↓if (condition == endIndex) {\n": "if condition == endIndex {\n",
|
||||
"↓if ((a || b) && (c || d)) {\n": "if (a || b) && (c || d) {\n",
|
||||
"↓if ((min...max).contains(value)) {\n": "if (min...max).contains(value) {\n",
|
||||
"↓for (item in collection) {\n": "for item in collection {\n",
|
||||
"↓for (var index = 0; index < 42; index++) {\n": "for var index = 0; index < 42; index++ {\n",
|
||||
"↓for(item in collection) {\n": "for item in collection {\n",
|
||||
"↓for(var index = 0; index < 42; index++) {\n": "for var index = 0; index < 42; index++ {\n",
|
||||
"↓guard (condition) else {\n": "guard condition else {\n",
|
||||
"↓while (condition) {\n": "while condition {\n",
|
||||
"↓while(condition) {\n": "while condition {\n",
|
||||
"} ↓while (condition) {\n": "} while condition {\n",
|
||||
"} ↓while(condition) {\n": "} while condition {\n",
|
||||
"do { ; } ↓while(condition) {\n": "do { ; } while condition {\n",
|
||||
"do { ; } ↓while (condition) {\n": "do { ; } while condition {\n",
|
||||
"↓switch (foo) {\n": "switch foo {\n",
|
||||
"do {\n} ↓catch(let error as NSError) {\n}": "do {\n} catch let error as NSError {\n}",
|
||||
"↓if (max(a, b) < c) {\n": "if max(a, b) < c {\n",
|
||||
]
|
||||
)
|
||||
|
||||
public func validate(file: SwiftLintFile) -> [StyleViolation] {
|
||||
return violationRanges(in: file).map { match -> StyleViolation in
|
||||
return StyleViolation(ruleDescription: type(of: self).description,
|
||||
severity: configuration.severity,
|
||||
location: Location(file: file, characterOffset: match.location))
|
||||
}
|
||||
}
|
||||
|
||||
public func violationRanges(in file: SwiftLintFile) -> [NSRange] {
|
||||
let statements = ["if", "for", "guard", "switch", "while", "catch"]
|
||||
let statementPatterns: [String] = statements.map { statement -> String in
|
||||
let isGuard = statement == "guard"
|
||||
@@ -65,13 +94,14 @@ public struct ControlStatementRule: ConfigurationProviderRule, AutomaticTestable
|
||||
let clausePattern = isSwitch ? "[^,{]*" : "[^{]*"
|
||||
return "\(statement)\\s*\\(\(clausePattern)\\)\\s*\(elsePattern)\\{"
|
||||
}
|
||||
return statementPatterns.flatMap { pattern -> [StyleViolation] in
|
||||
return statementPatterns.flatMap { pattern -> [NSRange] in
|
||||
return file.match(pattern: pattern)
|
||||
.filter { match, syntaxKinds -> Bool in
|
||||
let matchString = file.contents.substring(from: match.location, length: match.length)
|
||||
return !isFalsePositive(matchString, syntaxKind: syntaxKinds.first)
|
||||
}
|
||||
.filter { match, _ -> Bool in
|
||||
.map { $0.0 }
|
||||
.filter { match -> Bool in
|
||||
let contents = file.contents.bridge()
|
||||
guard let byteOffset = contents.NSRangeToByteRange(start: match.location, length: 1)?.location,
|
||||
let outerKind = file.structureDictionary.kinds(forByteOffset: byteOffset).last else {
|
||||
@@ -80,15 +110,25 @@ public struct ControlStatementRule: ConfigurationProviderRule, AutomaticTestable
|
||||
|
||||
return SwiftExpressionKind(rawValue: outerKind.kind) != .call
|
||||
}
|
||||
.map { match, _ -> StyleViolation in
|
||||
return StyleViolation(ruleDescription: type(of: self).description,
|
||||
severity: configuration.severity,
|
||||
location: Location(file: file, characterOffset: match.location))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func isFalsePositive(_ content: String, syntaxKind: SyntaxKind?) -> Bool {
|
||||
public func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String) {
|
||||
var violationString = file.contents.bridge().substring(with: violationRange)
|
||||
if violationString.contains("(") && violationString.contains(")") {
|
||||
if let openingIndex = violationString.firstIndex(of: "(") {
|
||||
let replacement = violationString[violationString.index(before: openingIndex)] == " " ? "" : " "
|
||||
violationString.replaceSubrange(openingIndex...openingIndex, with: replacement)
|
||||
}
|
||||
if let closingIndex = violationString.lastIndex(of: ")" as Character) {
|
||||
let replacement = violationString[violationString.index(after: closingIndex)] == " " ? "" : " "
|
||||
violationString.replaceSubrange(closingIndex...closingIndex, with: replacement)
|
||||
}
|
||||
}
|
||||
return (violationRange, violationString)
|
||||
}
|
||||
|
||||
private func isFalsePositive(_ content: String, syntaxKind: SyntaxKind?) -> Bool {
|
||||
if syntaxKind != .keyword {
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user