Make unneeded_break_in_switch auto correctable (#5188)

This commit is contained in:
Kotaro Suto
2023-10-03 01:31:10 -07:00
committed by GitHub
parent 591154091e
commit 5d95b0adaf
2 changed files with 106 additions and 5 deletions
+3
View File
@@ -17,6 +17,9 @@
[gibachan](https://github.com/gibachan)
[#5233](https://github.com/realm/SwiftLint/pull/5233)
* Make `unneeded_break_in_switch` auto correctable.
[KS1019](https://github.com/KS1019/)
#### Bug Fixes
* Fix false positive in `unused_import` rule that triggered on
@@ -12,7 +12,7 @@ private func embedInSwitch(
""", file: file, line: line)
}
struct UnneededBreakInSwitchRule: SwiftSyntaxRule, ConfigurationProviderRule {
struct UnneededBreakInSwitchRule: SwiftSyntaxCorrectableRule, ConfigurationProviderRule {
var configuration = SeverityConfiguration<Self>(.warning)
static let description = RuleDescription(
@@ -45,23 +45,121 @@ struct UnneededBreakInSwitchRule: SwiftSyntaxRule, ConfigurationProviderRule {
embedInSwitch("something()\n ↓break // comment"),
embedInSwitch("something()\n ↓break", case: "default"),
embedInSwitch("something()\n ↓break", case: "case .foo, .foo2 where condition")
],
corrections: [
embedInSwitch("something()\n ↓break")
: embedInSwitch("something()"),
embedInSwitch("something()\n ↓break // line comment")
: embedInSwitch("something()\n // line comment"),
embedInSwitch("""
something()
↓break
/*
block comment
*/
""")
: embedInSwitch("""
something()
/*
block comment
*/
"""),
embedInSwitch("something()\n ↓break /// doc line comment")
: embedInSwitch("something()\n /// doc line comment"),
embedInSwitch("""
something()
↓break
///
/// doc block comment
///
""")
: embedInSwitch("""
something()
///
/// doc block comment
///
"""),
embedInSwitch("something()\n ↓break", case: "default")
: embedInSwitch("something()", case: "default"),
embedInSwitch("something()\n ↓break", case: "case .foo, .foo2 where condition")
: embedInSwitch("something()", case: "case .foo, .foo2 where condition")
]
)
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
UnneededBreakInSwitchRuleVisitor(viewMode: .sourceAccurate)
}
func makeRewriter(file: SwiftLintCore.SwiftLintFile) -> SwiftLintCore.ViolationsSyntaxRewriter? {
UnneededBreakInSwitchRewriter(
locationConverter: file.locationConverter,
disabledRegions: disabledRegions(file: file)
)
}
}
private final class UnneededBreakInSwitchRuleVisitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: SwitchCaseSyntax) {
guard node.statements.count > 1,
let statement = node.statements.last,
let breakStatement = statement.item.as(BreakStmtSyntax.self),
breakStatement.label == nil else {
guard let statement = node.unneededBreak else {
return
}
violations.append(statement.item.positionAfterSkippingLeadingTrivia)
}
}
private final class UnneededBreakInSwitchRewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
private(set) var correctionPositions: [SwiftSyntax.AbsolutePosition] = []
let locationConverter: SourceLocationConverter
let disabledRegions: [SourceRange]
init(locationConverter: SourceLocationConverter, disabledRegions: [SourceRange]) {
self.locationConverter = locationConverter
self.disabledRegions = disabledRegions
}
override func visit(_ node: SwitchCaseSyntax) -> SwitchCaseSyntax {
let stmts = CodeBlockItemListSyntax(node.statements.dropLast())
guard let breakStatement = node.unneededBreak, let secondLast = stmts.last,
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
return super.visit(node)
}
correctionPositions.append(breakStatement.item.positionAfterSkippingLeadingTrivia)
let trivia = breakStatement.item.leadingTrivia + breakStatement.item.trailingTrivia
let newNode = node
.with(\.statements, stmts)
.with(\.statements.trailingTrivia, secondLast.item.trailingTrivia + trivia)
.trimmed { !$0.isComment }
.formatted()
.as(SwitchCaseSyntax.self)!
return super.visit(newNode)
}
}
private extension SwitchCaseSyntax {
var unneededBreak: CodeBlockItemSyntax? {
guard statements.count > 1,
let breakStatement = statements.last?.item.as(BreakStmtSyntax.self),
breakStatement.label == nil else {
return nil
}
return statements.last
}
}
private extension TriviaPiece {
var isComment: Bool {
switch self {
case .lineComment, .blockComment, .docLineComment, .docBlockComment:
return true
default:
return false
}
}
}