Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2f15f66

Browse files
authored
Rewritemark rule using SwiftSyntax (realm#5408)
1 parentf4fa852 commit2f15f66

File tree

3 files changed

+220
-211
lines changed

3 files changed

+220
-211
lines changed

‎CHANGELOG.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,16 @@
2929
*`explicit_acl`
3030
*`identifier_name`
3131
*`let_var_whitespace`
32+
*`mark`
3233
*`multiline_literal_brackets`
3334
*`nimble_operator`
3435
*`opening_brace`
3536
*`void_return`
3637

3738
[SimplyDanny](https://github.com/SimplyDanny)
3839
[kishikawakatsumi](https://github.com/kishikawakatsumi)
39-
[Marcelo Fabri](https://github.com/marcelofabri)
40+
[Marcelo Fabri](https://github.com/marcelofabri)
41+
[swiftty](https://github.com/swiftty)
4042

4143
* Print invalid keys when configuration parsing fails.
4244
[SimplyDanny](https://github.com/SimplyDanny)
Lines changed: 102 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Foundation
2-
importSourceKittenFramework
2+
importSwiftSyntax
33

4+
@SwiftSyntaxRule(explicitRewriter:true)
45
structMarkRule:CorrectableRule{
56
varconfiguration=SeverityConfiguration<Self>(.warning)
67

@@ -9,239 +10,130 @@ struct MarkRule: CorrectableRule {
910
name:"Mark",
1011
description:"MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'",
1112
kind:.lint,
12-
nonTriggeringExamples:[
13-
Example("// MARK: good"),
14-
Example("// MARK: - good"),
15-
Example("// MARK: -"),
16-
Example("// BOOKMARK"),
17-
Example("//BOOKMARK"),
18-
Example("// BOOKMARKS"),
19-
issue1749Example
20-
],
21-
triggeringExamples:[
22-
Example("↓//MARK: bad"),
23-
Example("↓// MARK:bad"),
24-
Example("↓//MARK:bad"),
25-
Example("↓// MARK: bad"),
26-
Example("↓// MARK: bad"),
27-
Example("↓// MARK: -bad"),
28-
Example("↓// MARK:- bad"),
29-
Example("↓// MARK:-bad"),
30-
Example("↓//MARK: - bad"),
31-
Example("↓//MARK:- bad"),
32-
Example("↓//MARK: -bad"),
33-
Example("↓//MARK:-bad"),
34-
Example("↓//Mark: bad"),
35-
Example("↓// Mark: bad"),
36-
Example("↓// MARK bad"),
37-
Example("↓//MARK bad"),
38-
Example("↓// MARK - bad"),
39-
Example("↓//MARK : bad"),
40-
Example("↓// MARKL:"),
41-
Example("↓// MARKR"),
42-
Example("↓// MARKK -"),
43-
Example("↓/// MARK:"),
44-
Example("↓/// MARK bad"),
45-
issue1029Example
46-
],
47-
corrections:[
48-
Example("↓//MARK: comment"):Example("// MARK: comment"),
49-
Example("↓// MARK: comment"):Example("// MARK: comment"),
50-
Example("↓// MARK:comment"):Example("// MARK: comment"),
51-
Example("↓// MARK: comment"):Example("// MARK: comment"),
52-
Example("↓//MARK: - comment"):Example("// MARK: - comment"),
53-
Example("↓// MARK:- comment"):Example("// MARK: - comment"),
54-
Example("↓// MARK: -comment"):Example("// MARK: - comment"),
55-
Example("↓// MARK: - comment"):Example("// MARK: - comment"),
56-
Example("↓// Mark: comment"):Example("// MARK: comment"),
57-
Example("↓// Mark: - comment"):Example("// MARK: - comment"),
58-
Example("↓// MARK - comment"):Example("// MARK: - comment"),
59-
Example("↓// MARK : comment"):Example("// MARK: comment"),
60-
Example("↓// MARKL:"):Example("// MARK:"),
61-
Example("↓// MARKL: -"):Example("// MARK: -"),
62-
Example("↓// MARKK"):Example("// MARK:"),
63-
Example("↓// MARKK -"):Example("// MARK: -"),
64-
Example("↓/// MARK:"):Example("// MARK:"),
65-
Example("↓/// MARK comment"):Example("// MARK: comment"),
66-
issue1029Example: issue1029Correction,
67-
issue1749Example: issue1749Correction
68-
]
13+
nonTriggeringExamples:MarkRuleExamples.nonTriggeringExamples,
14+
triggeringExamples:MarkRuleExamples.triggeringExamples,
15+
corrections:MarkRuleExamples.corrections
6916
)
17+
}
7018

71-
privateletspaceStartPattern="(?:\(nonSpaceOrTwoOrMoreSpace)\(mark))"
72-
73-
privateletendNonSpacePattern="(?:\(mark)\(nonSpace))"
74-
privateletendTwoOrMoreSpacePattern="(?:\(mark)\(twoOrMoreSpace))"
75-
76-
privateletinvalidEndSpacesPattern="(?:\(mark)\(nonSpaceOrTwoOrMoreSpace))"
77-
78-
privatelettwoOrMoreSpacesAfterHyphenPattern="(?:\(mark) -\(twoOrMoreSpace))"
79-
privateletnonSpaceOrNewlineAfterHyphenPattern="(?:\(mark) -[^\n])"
80-
81-
privateletinvalidSpacesAfterHyphenPattern="(?:\(mark) -\(nonSpaceOrTwoOrMoreSpaceOrNewline))"
82-
83-
privateletinvalidLowercasePattern="(?:// ?[Mm]ark:)"
84-
85-
privateletmissingColonPattern="(?:// ?MARK[^:])"
86-
// The below patterns more specifically describe some of the above pattern's failure cases for correction.
87-
privateletoneOrMoreSpacesBeforeColonPattern="(?:// ?MARK +:)"
88-
privateletnonWhitespaceBeforeColonPattern="(?:// ?MARK\\S+:)"
89-
privateletnonWhitespaceNorColonBeforeSpacesPattern="(?:// ?MARK[^\\s:]* +)"
90-
privateletthreeSlashesInsteadOfTwo="/// MARK:?"
91-
92-
privatevarpattern:String{
93-
return[
94-
spaceStartPattern,
95-
invalidEndSpacesPattern,
96-
invalidSpacesAfterHyphenPattern,
97-
invalidLowercasePattern,
98-
missingColonPattern,
99-
threeSlashesInsteadOfTwo
100-
].joined(separator:"|")
19+
privateextensionMarkRule{
20+
finalclassVisitor:ViolationsSyntaxVisitor<ConfigurationType>{
21+
overridefunc visitPost(_ node:TokenSyntax){
22+
forresultin node.violationResults(){
23+
violations.append(result.position)
24+
}
25+
}
10126
}
10227

103-
func validate(file:SwiftLintFile)->[StyleViolation]{
104-
returnviolationRanges(in: file, matching: pattern).map{
105-
StyleViolation(ruleDescription:Self.description,
106-
severity: configuration.severity,
107-
location:Location(file: file, characterOffset: $0.location))
28+
finalclassRewriter:ViolationsSyntaxRewriter{
29+
overridefunc visit(_ token:TokenSyntax)->TokenSyntax{
30+
varpieces= token.leadingTrivia.pieces
31+
forresultin token.violationResults(){
32+
// caution: `correctionPositions` records the positions before the mutations.
33+
// https://github.com/realm/SwiftLint/pull/4297
34+
correctionPositions.append(result.position)
35+
result.correct(&pieces)
36+
}
37+
return super.visit(token.with(\.leadingTrivia,Trivia(pieces: pieces)))
10838
}
10939
}
40+
}
11041

111-
func correct(file:SwiftLintFile)->[Correction]{
112-
varresult=[Correction]()
42+
privatestructViolationResult{
43+
letposition:AbsolutePosition
44+
letcorrect:(inout[TriviaPiece])->Void
45+
}
11346

114-
result.append(contentsOf:correct(file: file,
115-
pattern: spaceStartPattern,
116-
replaceString:"// MARK:"))
47+
privateextensionTokenSyntax{
48+
privateenumMark{
49+
staticfunc lint(in text:String)->[()->String]{
50+
letrange=NSRange(text.startIndex..<text.endIndex, in: text)
51+
returnregex(badPattern).matches(in: text, options:[], range: range).compactMap{ matchin
52+
isIgnoredCases(text, range: range)?nil:{
53+
varcorrected=replace(text, range: match.range(at:2), to:"-")
54+
corrected=replace(corrected, range: match.range(at:1), to:"// MARK:")
55+
if !text.hasSuffix(""), corrected.hasSuffix(""){
56+
corrected.removeLast()
57+
}
58+
return corrected
59+
}
60+
}
61+
}
11762

118-
result.append(contentsOf:correct(file: file,
119-
pattern: endNonSpacePattern,
120-
replaceString:"// MARK:",
121-
keepLastChar:true))
63+
privatestaticfunc isIgnoredCases(_ text:String, range:NSRange)->Bool{
64+
regex(goodPattern).firstMatch(in: text, range: range)!=nil
65+
}
12266

123-
result.append(contentsOf:correct(file: file,
124-
pattern: endTwoOrMoreSpacePattern,
125-
replaceString:"// MARK:"))
67+
privatestaticletgoodPattern=[
68+
"^// MARK:\(oneOrMoreHyphen)\(anyText)$",
69+
"^// MARK:\(oneOrMoreHyphen) ?$",
70+
"^// MARK:\(nonSpaceOrHyphen)+ ?\(anyText)?$",
71+
"^// MARK:$",
12672

127-
result.append(contentsOf:correct(file: file,
128-
pattern: twoOrMoreSpacesAfterHyphenPattern,
129-
replaceString:"// MARK: -"))
73+
// comment start with `Mark ...` is ignored
74+
"^\(twoOrThreeSlashes) +[Mm]ark[^:]"
75+
].map(nonCapturingGroup).joined(separator:"|")
13076

131-
result.append(contentsOf:correct(file: file,
132-
pattern: nonSpaceOrNewlineAfterHyphenPattern,
133-
replaceString:"// MARK: -",
134-
keepLastChar:true))
77+
privatestaticletbadPattern=capturingGroup([
78+
"MARK[^\\s:]",
79+
"[Mm]ark",
80+
"MARK"
81+
].map(basePattern).joined(separator:"|"))+ capturingGroup(hyphenOrEmpty)
13582

136-
result.append(contentsOf:correct(file: file,
137-
pattern: oneOrMoreSpacesBeforeColonPattern,
138-
replaceString:"// MARK:",
139-
keepLastChar:false))
83+
privatestaticletanySpace=" *"
84+
privatestaticletnonSpaceOrTwoOrMoreSpace="(?: {2,})?"
14085

141-
result.append(contentsOf:correct(file: file,
142-
pattern: nonWhitespaceBeforeColonPattern,
143-
replaceString:"// MARK:",
144-
keepLastChar:false))
86+
privatestaticletanyText="(?:\\S.*)"
14587

146-
result.append(contentsOf:correct(file: file,
147-
pattern: nonWhitespaceNorColonBeforeSpacesPattern,
148-
replaceString:"// MARK:",
149-
keepLastChar:false))
88+
privatestaticletoneOrMoreHyphen="-+"
89+
privatestaticletnonSpaceOrHyphen="[^ -]"
15090

151-
result.append(contentsOf:correct(file: file,
152-
pattern: invalidLowercasePattern,
153-
replaceString:"// MARK:"))
91+
privatestaticlettwoOrThreeSlashes="///?"
92+
privatestaticletcolonOrEmpty=":?"
93+
privatestaticlethyphenOrEmpty="-? *"
15494

155-
result.append(contentsOf:correct(file: file,
156-
pattern: threeSlashesInsteadOfTwo,
157-
replaceString:"// MARK:"))
95+
privatestaticfunc nonCapturingGroup(_ pattern:String)->String{
96+
"(?:\(pattern))"
97+
}
15898

159-
return result.unique
160-
}
99+
privatestaticfunc capturingGroup(_ pattern:String)->String{
100+
"(\(pattern))"
101+
}
161102

162-
privatefunc correct(file:SwiftLintFile,
163-
pattern:String,
164-
replaceString:String,
165-
keepLastChar:Bool=false)->[Correction]{
166-
letviolations=violationRanges(in: file, matching: pattern)
167-
letmatches= file.ruleEnabled(violatingRanges: violations, for:self)
168-
if matches.isEmpty{return[]}
169-
170-
varnsstring= file.contents.bridge()
171-
letdescription=Self.description
172-
varcorrections=[Correction]()
173-
forvar rangein matches.reversed(){
174-
if keepLastChar{
175-
range.length-=1
103+
privatestaticfunc basePattern(_ pattern:String)->String{
104+
nonCapturingGroup("\(twoOrThreeSlashes)\(anySpace)\(pattern)\(anySpace)\(colonOrEmpty)\(anySpace)")
105+
}
106+
107+
privatestaticfunc replace(_ target:String, range nsrange:NSRange, to replaceString:String)->String{
108+
guard nsrange.length>0,let range=Range(nsrange, in: target)else{
109+
return target
176110
}
177-
letlocation=Location(file: file, characterOffset: range.location)
178-
nsstring= nsstring.replacingCharacters(in: range, with: replaceString).bridge()
179-
corrections.append(Correction(ruleDescription: description, location: location))
111+
return target.replacingCharacters(in: range, with: replaceString)
180112
}
181-
file.write(nsstring.bridge())
182-
return corrections
183113
}
184114

185-
privatefunc violationRanges(in file:SwiftLintFile, matching pattern:String)->[NSRange]{
186-
return file.rangesAndTokens(matching: pattern).filter{ matchRange, syntaxTokensin
187-
guard
188-
let syntaxToken= syntaxTokens.first,
189-
let syntaxKind= syntaxToken.kind,
190-
SyntaxKind.commentKinds.contains(syntaxKind),
191-
caselet tokenLocation=Location(file: file, byteOffset: syntaxToken.offset),
192-
caselet matchLocation=Location(file: file, characterOffset: matchRange.location),
193-
// Skip MARKs that are part of a multiline comment
194-
tokenLocation.line== matchLocation.line
195-
else{
196-
returnfalse
115+
func violationResults()->[ViolationResult]{
116+
varutf8Offset=0
117+
varresults:[ViolationResult]=[]
118+
119+
forindexin leadingTrivia.pieces.indices{
120+
letpiece= leadingTrivia.pieces[index]
121+
defer{ utf8Offset+= piece.sourceLength.utf8Length}
122+
123+
switch piece{
124+
case.lineComment(let comment),.docLineComment(let comment):
125+
forcorrectinMark.lint(in: comment){
126+
letposition= position.advanced(by: utf8Offset)
127+
results.append(ViolationResult(position: position){ piecesin
128+
pieces[index]=.lineComment(correct())
129+
})
130+
}
131+
132+
default:
133+
break
197134
}
198-
returntrue
199-
}.compactMap{ range, syntaxTokensin
200-
letbyteRange=ByteRange(location:syntaxTokens[0].offset, length:0)
201-
letidentifierRange= file.stringView.byteRangeToNSRange(byteRange)
202-
return identifierRange.map{NSUnionRange($0, range)}
203135
}
204-
}
205-
}
206136

207-
privateletissue1029Example=Example("""
208-
↓//MARK:- Top-Level bad mark
209-
↓//MARK:- Another bad mark
210-
struct MarkTest {}
211-
↓// MARK:- Bad mark
212-
extension MarkTest {}
213-
""")
214-
215-
privateletissue1029Correction=Example("""
216-
// MARK: - Top-Level bad mark
217-
// MARK: - Another bad mark
218-
struct MarkTest {}
219-
// MARK: - Bad mark
220-
extension MarkTest {}
221-
""")
222-
223-
// https://github.com/realm/SwiftLint/issues/1749
224-
// https://github.com/realm/SwiftLint/issues/3841
225-
privateletissue1749Example=Example(
226-
"""
227-
/*
228-
func test1() {
229-
}
230-
//MARK: mark
231-
func test2() {
137+
return results
232138
}
233-
*/
234-
"""
235-
)
236-
237-
// This example should not trigger changes
238-
privateletissue1749Correction= issue1749Example
239-
240-
// These need to be at the bottom of the file to work around https://bugs.swift.org/browse/SR-10486
241-
242-
privateletnonSpace="[^ ]"
243-
privatelettwoOrMoreSpace=" {2,}"
244-
privateletmark="MARK:"
245-
privateletnonSpaceOrTwoOrMoreSpace="(?:\(nonSpace)|\(twoOrMoreSpace))"
246-
247-
privateletnonSpaceOrTwoOrMoreSpaceOrNewline="(?:[^\n]|\(twoOrMoreSpace))"
139+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp