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

Commitf3fb172

Browse files
Rewriteextension_access_modifier rule with SwiftSyntax (realm#5310)
Co-authored-by: Danny Mösch <danny.moesch@icloud.com>
1 parent6d38558 commitf3fb172

File tree

2 files changed

+145
-72
lines changed

2 files changed

+145
-72
lines changed

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
* Rewrite the following rules with SwiftSyntax:
4141
*`explicit_acl`
42+
*`extension_access_modifier`
4243
*`identifier_name`
4344
*`let_var_whitespace`
4445
*`mark`
Lines changed: 144 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
importFoundation
2-
importSourceKittenFramework
1+
importSwiftLintCore
2+
importSwiftSyntax
33

4-
structExtensionAccessModifierRule:ASTRule,OptInRule{
4+
@SwiftSyntaxRule
5+
structExtensionAccessModifierRule:OptInRule{
56
varconfiguration=SeverityConfiguration<Self>(.warning)
67

78
staticletdescription=RuleDescription(
@@ -34,28 +35,68 @@ struct ExtensionAccessModifierRule: ASTRule, OptInRule {
3435
}
3536
"""),
3637
Example("""
38+
extension Foo {
39+
var bar: Int { return 1 }
40+
internal var baz: Int { return 1 }
41+
}
42+
"""),
43+
Example("""
44+
internal extension Foo {
45+
var bar: Int { return 1 }
46+
var baz: Int { return 1 }
47+
}
48+
"""),
49+
Example("""
3750
public extension Foo {
3851
var bar: Int { return 1 }
3952
var baz: Int { return 1 }
4053
}
4154
"""),
4255
Example("""
56+
public extension Foo {
57+
var bar: Int { return 1 }
58+
internal var baz: Int { return 1 }
59+
}
60+
"""),
61+
Example("""
4362
extension Foo {
44-
private bar: Int { return 1 }
45-
private baz: Int { return 1 }
63+
privatevarbar: Int { return 1 }
64+
privatevarbaz: Int { return 1 }
4665
}
4766
"""),
4867
Example("""
4968
extension Foo {
50-
open bar: Int { return 1 }
51-
open baz: Int { return 1 }
69+
openvarbar: Int { return 1 }
70+
openvarbaz: Int { return 1 }
5271
}
5372
"""),
5473
Example("""
5574
extension Foo {
5675
func setup() {}
5776
public func update() {}
5877
}
78+
"""),
79+
Example("""
80+
private extension Foo {
81+
private var bar: Int { return 1 }
82+
var baz: Int { return 1 }
83+
}
84+
"""),
85+
Example("""
86+
extension Foo {
87+
internal private(set) var bar: Int {
88+
get { Foo.shared.bar }
89+
set { Foo.shared.bar = newValue }
90+
}
91+
}
92+
"""),
93+
Example("""
94+
extension Foo {
95+
private(set) internal var bar: Int {
96+
get { Foo.shared.bar }
97+
set { Foo.shared.bar = newValue }
98+
}
99+
}
59100
""")
60101
],
61102
triggeringExamples:[
@@ -73,100 +114,131 @@ struct ExtensionAccessModifierRule: ASTRule, OptInRule {
73114
"""),
74115
Example("""
75116
public extension Foo {
76-
public ↓func bar() {}
77-
public ↓func baz() {}
117+
↓public func bar() {}
118+
↓public func baz() {}
119+
}
120+
"""),
121+
Example("""
122+
↓extension Foo {
123+
public var bar: Int {
124+
let value = 1
125+
return value
126+
}
127+
128+
public var baz: Int { return 1 }
129+
}
130+
"""),
131+
Example("""
132+
↓extension Array where Element: Equatable {
133+
public var unique: [Element] {
134+
var uniqueValues = [Element]()
135+
for item in self where !uniqueValues.contains(item) {
136+
uniqueValues.append(item)
137+
}
138+
return uniqueValues
139+
}
78140
}
79141
"""),
80142
Example("""
81143
↓extension Foo {
144+
#if DEBUG
82145
public var bar: Int {
83146
let value = 1
84147
return value
85148
}
149+
#endif
86150
87151
public var baz: Int { return 1 }
88152
}
153+
"""),
154+
Example("""
155+
public extension Foo {
156+
↓private func bar() {}
157+
↓private func baz() {}
158+
}
89159
""")
90160
]
91161
)
162+
}
92163

93-
func validate(file:SwiftLintFile, kind:SwiftDeclarationKind,
94-
dictionary:SourceKittenDictionary)->[StyleViolation]{
95-
guard kind==.extension,let offset= dictionary.offset,
96-
dictionary.inheritedTypes.isEmpty
97-
else{
98-
return[]
99-
}
100-
101-
letdeclarations= dictionary.substructure
102-
.compactMap{ entry->(acl:AccessControlLevel, offset:ByteCount)?in
103-
guardlet kind= entry.declarationKind,
104-
kind!=.varLocal, kind!=.varParameter,
105-
let offset= entry.offsetelse{
106-
returnnil
107-
}
164+
privateextensionExtensionAccessModifierRule{
165+
privateenumACL:Hashable{
166+
case implicit
167+
case explicit(TokenKind)
108168

109-
return(acl: entry.accessibility??.internal, offset: offset)
169+
staticfunc from(tokenKind:TokenKind?)->ACL{
170+
switch tokenKind{
171+
casenil:
172+
return.implicit
173+
caselet value?:
174+
return.explicit(value)
110175
}
111-
112-
letdeclarationsACLs= declarations.map{ $0.acl}.unique
113-
letallowedACLs:Set<AccessControlLevel>=[.internal,.private,.open]
114-
guard declarationsACLs.count==1, !allowedACLs.contains(declarationsACLs[0])else{
115-
return[]
116176
}
117177

118-
letsyntaxTokens= file.syntaxMap.tokens
119-
letparts= syntaxTokens.partitioned{ offset<= $0.offset}
120-
iflet aclToken= parts.first.last, file.isACL(token: aclToken){
121-
returndeclarationsViolations(file: file, acl:declarationsACLs[0],
122-
declarationOffsets: declarations.map{ $0.offset},
123-
dictionary: dictionary)
178+
staticfunc isAllowed(_ acl:Self)->Bool{
179+
[
180+
.explicit(.keyword(.internal)),
181+
.explicit(.keyword(.private)),
182+
.explicit(.keyword(.open)),
183+
.implicit
184+
].contains(acl)
124185
}
125-
126-
return[
127-
StyleViolation(ruleDescription:Self.description,
128-
severity: configuration.severity,
129-
location:Location(file: file, byteOffset: offset))
130-
]
131186
}
132187

133-
privatefunc declarationsViolations(file:SwiftLintFile, acl:AccessControlLevel,
134-
declarationOffsets:[ByteCount],
135-
dictionary:SourceKittenDictionary)->[StyleViolation]{
136-
guardlet byteRange= dictionary.byteRange,
137-
caselet contents= file.stringView,
138-
let range= contents.byteRangeToNSRange(byteRange)else{
139-
return[]
140-
}
188+
finalclassVisitor:ViolationsSyntaxVisitor<ConfigurationType>{
189+
overridevarskippableDeclarations:[anyDeclSyntaxProtocol.Type]{.all}
141190

142-
// find all ACL tokens
143-
letallACLRanges= file.match(pattern: acl.description, with:[.attributeBuiltin], range: range).compactMap{
144-
contents.NSRangeToByteRange(start: $0.location, length: $0.length)
145-
}
191+
overridefunc visitPost(_ node:ExtensionDeclSyntax){
192+
guard node.inheritanceClause==nilelse{
193+
return
194+
}
195+
196+
varareAllACLsEqual=true
197+
varaclTokens=[(position: AbsolutePosition, acl: ACL)]()
146198

147-
letviolationOffsets= declarationOffsets.filter{ typeOffsetin
148-
// find the last ACL token before the type
149-
guardlet previousInternalByteRange=lastACLByteRange(before: typeOffset, in: allACLRanges)else{
150-
// didn't find a candidate token, so the ACL is implicit (not a violation)
151-
returnfalse
199+
fordeclin node.memberBlock.expandingIfConfigs(){
200+
letmodifiers= decl.asProtocol((anyWithModifiersSyntax).self)?.modifiers
201+
letaclToken= modifiers?.accessLevelModifier?.name
202+
letacl=ACL.from(tokenKind: aclToken?.tokenKind)
203+
if areAllACLsEqual, acl!= aclTokens.last?.acl, aclTokens.isNotEmpty{
204+
areAllACLsEqual=false
205+
}
206+
aclTokens.append((decl.positionAfterSkippingLeadingTrivia, acl))
152207
}
153208

154-
// the ACL token correspond to the type if there're only
155-
// attributeBuiltin (`final` for example) tokens between them
156-
letlength= typeOffset- previousInternalByteRange.location
157-
letrange=ByteRange(location: previousInternalByteRange.location, length: length)
158-
returnSet(file.syntaxMap.kinds(inByteRange: range))==[.attributeBuiltin]
159-
}
209+
guard areAllACLsEqual,let lastACL= aclTokens.lastelse{
210+
return
211+
}
212+
213+
letisAllowedACL=ACL.isAllowed(lastACL.acl)
214+
letextensionACL=ACL.from(tokenKind: node.modifiers.accessLevelModifier?.name.tokenKind)
160215

161-
return violationOffsets.map{
162-
StyleViolation(ruleDescription:Self.description,
163-
severity: configuration.severity,
164-
location:Location(file: file, byteOffset: $0))
216+
if extensionACL!=.implicit{
217+
if !isAllowedACL || lastACL.acl!= extensionACL, lastACL.acl!=.implicit{
218+
violations.append(contentsOf: aclTokens.map(\.position))
219+
}
220+
}elseif !isAllowedACL{
221+
violations.append(node.extensionKeyword.positionAfterSkippingLeadingTrivia)
222+
}
165223
}
166224
}
225+
}
167226

168-
privatefunc lastACLByteRange(before typeOffset:ByteCount, in ranges:[ByteRange])->ByteRange?{
169-
letfirstPartition= ranges.partitioned(by:{ $0.location> typeOffset}).first
170-
return firstPartition.last
227+
privateextensionMemberBlockSyntax{
228+
func expandingIfConfigs()->[DeclSyntax]{
229+
members.flatMap{ memberin
230+
iflet ifConfig= member.decl.as(IfConfigDeclSyntax.self){
231+
return ifConfig.clauses.flatMap{ clausein
232+
switch clause.elements{
233+
case.decls(let decls):
234+
return decls.map(\.decl)
235+
default:
236+
return[]
237+
}
238+
}
239+
}else{
240+
return[member.decl]
241+
}
242+
}
171243
}
172244
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp