mirror of
https://github.com/realm/SwiftLint.git
synced 2026-05-07 20:12:49 +00:00
Make unneeded_break_in_switch auto correctable (#5188)
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user