11import SwiftLintCore
22import SwiftSyntax
33
4- @SwiftSyntaxRule ( explicitRewriter : true )
5- struct SuperfluousElseRule : OptInRule {
4+ @SwiftSyntaxRule
5+ struct SuperfluousElseRule : SwiftSyntaxCorrectableRule , OptInRule {
66var configuration = SeverityConfiguration < Self > ( . warning)
77
88static let description = RuleDescription (
@@ -58,49 +58,49 @@ struct SuperfluousElseRule: OptInRule {
5858] ,
5959 triggeringExamples: [
6060Example ( """
61- ↓ if i > 0 {
61+ if i > 0 {
6262 return 1
6363 // comment
64- } else {
64+ }↓ else {
6565 return 2
6666 }
6767""" ) ,
6868Example ( """
69- ↓ if i > 0 {
69+ if i > 0 {
7070 return 1
71- } else↓ if i < 12 {
71+ }↓ else if i < 12 {
7272 return 2
73- } else if i > 18 {
73+ }↓ else if i > 18 {
7474 return 3
7575 }
7676""" ) ,
7777Example ( """
78- ↓ if i > 0 {
79- ↓ if i < 12 {
78+ if i > 0 {
79+ if i < 12 {
8080 return 5
81- } else {
82- ↓ if i > 11 {
81+ }↓ else {
82+ if i > 11 {
8383 return 6
84- } else {
84+ }↓ else {
8585 return 7
8686 }
8787 }
88- } else↓ if i < 12 {
88+ }↓ else if i < 12 {
8989 return 2
90- } else↓ if i < 24 {
90+ }↓ else if i < 24 {
9191 return 8
92- } else {
92+ }↓ else {
9393 return 3
9494 }
9595""" )
9696] ,
9797 corrections: [
9898Example ( """
9999 func f() -> Int {
100- ↓ if i > 0 {
100+ if i > 0 {
101101 return 1
102102 // comment
103- } else {
103+ }↓ else {
104104 // another comment
105105 return 2
106106 // yet another comment
@@ -119,12 +119,12 @@ struct SuperfluousElseRule: OptInRule {
119119""" ) ,
120120Example ( """
121121 func f() -> Int {
122- ↓ if i > 0 {
122+ if i > 0 {
123123 return 1
124124 // comment
125- } else↓ if i < 10 {
125+ }↓ else if i < 10 {
126126 return 2
127- } else {
127+ }↓ else {
128128 return 3
129129 }
130130 }
@@ -143,10 +143,10 @@ struct SuperfluousElseRule: OptInRule {
143143Example ( """
144144 func f() -> Int {
145145
146- ↓ if i > 0 {
146+ if i > 0 {
147147 return 1
148148 // comment
149- } else if i < 10 {
149+ }↓ else if i < 10 {
150150 // another comment
151151 return 2
152152 }
@@ -166,9 +166,9 @@ struct SuperfluousElseRule: OptInRule {
166166""" ) ,
167167Example ( """
168168 {
169- ↓ if i > 0 {
169+ if i > 0 {
170170 return 1
171- } else {
171+ }↓ else {
172172 return 2
173173 }
174174 }()
@@ -182,43 +182,60 @@ struct SuperfluousElseRule: OptInRule {
182182""" )
183183]
184184)
185+
186+ func makeRewriter( file: SwiftLintFile ) -> ( some ViolationsSyntaxRewriter ) ? {
187+ Rewriter (
188+ configuration: configuration,
189+ file: file,
190+ disabledRegions: disabledRegions ( file: file)
191+ )
192+ }
185193}
186194
187195private extension SuperfluousElseRule {
188196final class Visitor : ViolationsSyntaxVisitor < ConfigurationType > {
189197override var skippableDeclarations : [ any DeclSyntaxProtocol . Type ] { [ ProtocolDeclSyntax . self] }
190198
191199override func visitPost( _ node: IfExprSyntax ) {
192- if node. violatesRule {
193- violations. append ( node . ifKeyword . positionAfterSkippingLeadingTrivia)
200+ if let elseKeyword = node. superfluousElse {
201+ violations. append ( elseKeyword . positionAfterSkippingLeadingTrivia)
194202}
195203}
196204}
197205
198206final class Rewriter : ViolationsSyntaxRewriter {
199- override func visit( _ node: CodeBlockItemListSyntax ) -> CodeBlockItemListSyntax {
200- super. visit ( restructure ( list: node) )
207+ init ( configuration: ConfigurationType ,
208+ file: SwiftLintFile ,
209+ disabledRegions: [ SourceRange ] ) {
210+ super. init ( locationConverter: file. locationConverter, disabledRegions: disabledRegions)
211+ let correctionPositions = Visitor ( configuration: configuration, file: file) . walk ( file: file) {
212+ $0. violations. map ( \. position)
213+ } . filter { !$0. isContainedIn ( regions: disabledRegions, locationConverter: locationConverter) }
214+ self . correctionPositions. append ( contentsOf: correctionPositions)
215+ }
216+
217+ override func visitAny( _ node: Syntax ) -> Syntax ? {
218+ correctionPositions. isEmpty? node: nil // Avoid skipping all `if` expressions in a code block.
201219}
202220
203- private func restructure ( list: CodeBlockItemListSyntax , offset : Int = 0 ) -> CodeBlockItemListSyntax {
221+ override func visit ( _ list: CodeBlockItemListSyntax ) -> CodeBlockItemListSyntax {
204222var newStatements = CodeBlockItemListSyntax ( )
205- var newOffset = 0
223+ var ifStmtRewritten = false
206224for item in list{
207225guard let ifStmt= item. item. as ( ExpressionStmtSyntax . self) ? . expression. as ( IfExprSyntax . self) ,
208- !ifStmt . isContainedIn ( regions : disabledRegions , locationConverter : locationConverter ) ,
209- ifStmt . violatesRule else {
226+ let elseKeyword = ifStmt . superfluousElse ,
227+ !elseKeyword . isContainedIn ( regions : disabledRegions , locationConverter : locationConverter ) else {
210228 newStatements. append ( item)
211229continue
212230}
213- newOffset= ifStmt. body. rightBrace. endPositionBeforeTrailingTrivia. utf8Offset
214- correctionPositions. append ( ifStmt. ifKeyword. positionAfterSkippingLeadingTrivia. advanced ( by: offset) )
231+ ifStmtRewritten= true
215232let ( newIfStm, removedItems) = modify ( ifStmt: ifStmt)
216233 newStatements. append (
217234CodeBlockItemSyntax ( item: CodeBlockItemSyntax . Item ( ExpressionStmtSyntax ( expression: newIfStm) ) )
218235)
219236 newStatements. append ( contentsOf: removedItems)
220237}
221- return newOffset > 0 ? restructure ( list : newStatements, offset : newOffset ) : newStatements
238+ return ifStmtRewritten ? visit ( newStatements) : super . visit ( newStatements)
222239}
223240
224241private func modify( ifStmt: IfExprSyntax ) -> ( newIfStmt: IfExprSyntax , removedItems: [ CodeBlockItemSyntax ] ) {
@@ -255,15 +272,17 @@ private extension SuperfluousElseRule {
255272}
256273
257274private extension IfExprSyntax {
258- var violatesRule : Bool {
275+ var superfluousElse : TokenSyntax ? {
259276if elseKeyword== nil {
260- return false
277+ return nil
278+ }
279+ if !lastStatementReturns( in: body) {
280+ return nil
261281}
262- let thenBodyReturns = lastStatementReturns ( in: body)
263- if thenBodyReturns, let parent= parent? . as ( IfExprSyntax . self) {
264- return parent. violatesRule
282+ if let parent= parent? . as ( IfExprSyntax . self) {
283+ return parent. superfluousElse!= nil ? elseKeyword: nil
265284}
266- return thenBodyReturns
285+ return elseKeyword
267286}
268287
269288private var returnsInAllBranches : Bool {