@@ -14,6 +14,7 @@ struct StatementPositionRule: CorrectableRule {
1414 Example ( " } else if { " ) ,
1515 Example ( " } else { " ) ,
1616 Example ( " } catch { " ) ,
17+ Example ( " guard foo() else { return } " ) ,
1718 Example ( " \" }else{ \" " ) ,
1819 Example ( " struct A { let catchphrase: Int } \n let a = A( \n catchphrase: 0 \n ) " ) ,
1920 Example ( " struct A { let `catch`: Int } \n let a = A( \n `catch`: 0 \n ) " ) ,
@@ -23,11 +24,13 @@ struct StatementPositionRule: CorrectableRule {
2324 Example ( " ↓} else { " ) ,
2425 Example ( " ↓} \n catch { " ) ,
2526 Example ( " ↓} \n \t catch { " ) ,
27+ Example ( " guard foo()↓else { return } " ) ,
2628 ] ,
2729 corrections: [
2830 Example ( " ↓} \n else { " ) : Example ( " } else { " ) ,
2931 Example ( " ↓} \n else if { " ) : Example ( " } else if { " ) ,
3032 Example ( " ↓} \n catch { " ) : Example ( " } catch { " ) ,
33+ Example ( " guard foo()↓else { return } " ) : Example ( " guard foo() else { return } " ) ,
3134 ]
3235 )
3336
@@ -87,34 +90,75 @@ private extension StatementPositionRule {
8790 // followed by 'else' or 'catch' literals
8891 static let defaultPattern = " \\ }(?:[ \\ s \\ n \\ r]{2,}|[ \\ n \\ t \\ r]+)? \\ b(else|catch) \\ b "
8992
93+ // match a guard statement where `else` is glued to the condition without whitespace
94+ static let defaultGuardPattern = " ( \\ bguard \\ b[^ \\ n]* \\ S)(else \\ b) "
95+
96+ static let defaultGuardRegex = regex ( defaultGuardPattern)
97+
9098 func defaultValidate( file: SwiftLintFile ) -> [ StyleViolation ] {
91- defaultViolationRanges ( in: file, matching : Self . defaultPattern ) . compactMap { range in
99+ defaultViolationRanges ( in: file) . compactMap { range in
92100 StyleViolation ( ruleDescription: Self . description,
93101 severity: configuration. severity,
94102 location: Location ( file: file, characterOffset: range. location) )
95103 }
96104 }
97105
98- func defaultViolationRanges( in file: SwiftLintFile , matching pattern: String ) -> [ NSRange ] {
99- file. match ( pattern: pattern) . filter { _, syntaxKinds in
106+ func defaultViolationRanges( in file: SwiftLintFile ) -> [ NSRange ] {
107+ defaultBraceViolationRanges ( in: file) + defaultGuardViolationRanges( in: file)
108+ }
109+
110+ func defaultBraceViolationRanges( in file: SwiftLintFile ) -> [ NSRange ] {
111+ file. match ( pattern: Self . defaultPattern) . filter { _, syntaxKinds in
100112 syntaxKinds. starts ( with: [ . keyword] )
101113 } . compactMap ( \. 0 )
102114 }
103115
116+ func defaultGuardViolationRanges( in file: SwiftLintFile ) -> [ NSRange ] {
117+ defaultGuardMatches ( in: file) . map { $0. range ( at: 2 ) }
118+ }
119+
120+ func defaultGuardCorrectionRanges( in file: SwiftLintFile ) -> [ NSRange ] {
121+ defaultGuardMatches ( in: file) . map ( \. range)
122+ }
123+
124+ func defaultGuardMatches( in file: SwiftLintFile ) -> [ NSTextCheckingResult ] {
125+ let contents = file. stringView
126+ let syntaxMap = file. syntaxMap
127+
128+ return Self . defaultGuardRegex. matches ( in: file) . filter { match in
129+ guard let elseRange = contents. NSRangeToByteRange (
130+ start: match. range ( at: 2 ) . location,
131+ length: match. range ( at: 2 ) . length
132+ ) else {
133+ return false
134+ }
135+
136+ return syntaxMap. kinds ( inByteRange: elseRange) == [ . keyword]
137+ }
138+ }
139+
104140 func defaultCorrect( file: SwiftLintFile ) -> Int {
105- let violations = defaultViolationRanges ( in: file, matching: Self . defaultPattern)
106- let matches = file. ruleEnabled ( violatingRanges: violations, for: self )
107- if matches. isEmpty {
141+ let braceViolations = defaultBraceViolationRanges ( in: file)
142+ let guardViolations = defaultGuardCorrectionRanges ( in: file)
143+ let enabledBraceViolations = file. ruleEnabled ( violatingRanges: braceViolations, for: self )
144+ let enabledGuardViolations = file. ruleEnabled ( violatingRanges: guardViolations, for: self )
145+ if enabledBraceViolations. isEmpty, enabledGuardViolations. isEmpty {
108146 return 0
109147 }
110- let regularExpression = regex ( Self . defaultPattern )
148+
111149 var contents = file. contents
112- for range in matches. reversed ( ) {
113- contents = regularExpression. stringByReplacingMatches ( in: contents, options: [ ] , range: range,
114- withTemplate: " } $1 " )
150+ let braceRegex = regex ( Self . defaultPattern)
151+ for range in enabledBraceViolations. reversed ( ) {
152+ contents = braceRegex. stringByReplacingMatches ( in: contents, options: [ ] , range: range,
153+ withTemplate: " } $1 " )
154+ }
155+
156+ for range in enabledGuardViolations. reversed ( ) {
157+ contents = Self . defaultGuardRegex. stringByReplacingMatches ( in: contents, options: [ ] , range: range,
158+ withTemplate: " $1 $2 " )
115159 }
116160 file. write ( contents)
117- return matches . count
161+ return enabledBraceViolations . count + enabledGuardViolations . count
118162 }
119163}
120164
0 commit comments