@@ -71,40 +71,35 @@ public final class NoAccessLevelOnExtensionDeclaration: SyntaxFormatRule {
71
71
72
72
// Adds given keyword to all members in declaration block
73
73
private func addMemberAccessKeyword(
74
- _ keyword : DeclModifierSyntax ,
74
+ _ modifier : DeclModifierSyntax ,
75
75
toMembersIn memberBlock: MemberBlockSyntax
76
76
) -> ( MemberBlockItemListSyntax , [ Finding . Note ] ) {
77
77
var newMembers : [ MemberBlockItemSyntax ] = [ ]
78
78
var notes : [ Finding . Note ] = [ ]
79
79
80
- var formattedKeyword = keyword
81
- formattedKeyword. leadingTrivia = [ ]
82
-
83
80
for memberItem in memberBlock. members {
84
- let member = memberItem. decl
81
+ let decl = memberItem. decl
85
82
guard
86
- let modifiers = member. asProtocol ( WithModifiersSyntax . self) ? . modifiers,
87
- // addModifier relocates trivia for any token(s) displaced by the new modifier.
88
- let newDecl = addModifier ( declaration: member, modifierKeyword: formattedKeyword)
89
- . as ( DeclSyntax . self)
83
+ let modifiers = decl. asProtocol ( WithModifiersSyntax . self) ? . modifiers,
84
+ modifiers. accessLevelModifier == nil
90
85
else {
91
86
newMembers. append ( memberItem)
92
87
continue
93
88
}
94
89
90
+ // Create a note associated with each declaration that needs to have an access level modifier
91
+ // added to it.
92
+ notes. append ( Finding . Note (
93
+ message: . addModifierToExtensionMember( keyword: modifier. name. text) ,
94
+ location:
95
+ Finding . Location ( decl. startLocation ( converter: context. sourceLocationConverter) )
96
+ ) )
97
+
95
98
var newItem = memberItem
96
- newItem. decl = newDecl
99
+ newItem. decl = applyingAccessModifierIfNone ( modifier , to : decl )
97
100
newMembers. append ( newItem)
98
-
99
- // If it already had an explicit access modifier, don't leave a note.
100
- if modifiers. accessLevelModifier == nil {
101
- notes. append ( Finding . Note (
102
- message: . addModifierToExtensionMember( keyword: formattedKeyword. name. text) ,
103
- location:
104
- Finding . Location ( member. startLocation ( converter: context. sourceLocationConverter) )
105
- ) )
106
- }
107
101
}
102
+
108
103
return ( MemberBlockItemListSyntax ( newMembers) , notes)
109
104
}
110
105
}
@@ -129,3 +124,69 @@ extension Finding.Message {
129
124
" add ' \( keyword) ' access modifier to this declaration "
130
125
}
131
126
}
127
+
128
+ /// Adds `modifier` to `decl` if it doesn't already have an explicit access level modifier and
129
+ /// returns the new declaration.
130
+ ///
131
+ /// If `decl` already has an access level modifier, it is returned unchanged.
132
+ private func applyingAccessModifierIfNone(
133
+ _ modifier: DeclModifierSyntax ,
134
+ to decl: DeclSyntax
135
+ ) -> DeclSyntax {
136
+ switch Syntax ( decl) . as ( SyntaxEnum . self) {
137
+ case . actorDecl( let actorDecl) :
138
+ return applyingAccessModifierIfNone ( modifier, to: actorDecl, declKeywordKeyPath: \. actorKeyword)
139
+ case . classDecl( let classDecl) :
140
+ return applyingAccessModifierIfNone ( modifier, to: classDecl, declKeywordKeyPath: \. classKeyword)
141
+ case . enumDecl( let enumDecl) :
142
+ return applyingAccessModifierIfNone ( modifier, to: enumDecl, declKeywordKeyPath: \. enumKeyword)
143
+ case . initializerDecl( let initDecl) :
144
+ return applyingAccessModifierIfNone ( modifier, to: initDecl, declKeywordKeyPath: \. initKeyword)
145
+ case . functionDecl( let funcDecl) :
146
+ return applyingAccessModifierIfNone ( modifier, to: funcDecl, declKeywordKeyPath: \. funcKeyword)
147
+ case . structDecl( let structDecl) :
148
+ return applyingAccessModifierIfNone (
149
+ modifier, to: structDecl, declKeywordKeyPath: \. structKeyword)
150
+ case . subscriptDecl( let subscriptDecl) :
151
+ return applyingAccessModifierIfNone (
152
+ modifier, to: subscriptDecl, declKeywordKeyPath: \. subscriptKeyword)
153
+ case . typeAliasDecl( let typeAliasDecl) :
154
+ return applyingAccessModifierIfNone (
155
+ modifier, to: typeAliasDecl, declKeywordKeyPath: \. typealiasKeyword)
156
+ case . variableDecl( let varDecl) :
157
+ return applyingAccessModifierIfNone (
158
+ modifier, to: varDecl, declKeywordKeyPath: \. bindingSpecifier)
159
+ default :
160
+ return decl
161
+ }
162
+ }
163
+
164
+ private func applyingAccessModifierIfNone< Decl: DeclSyntaxProtocol & WithModifiersSyntax > (
165
+ _ modifier: DeclModifierSyntax ,
166
+ to decl: Decl ,
167
+ declKeywordKeyPath: WritableKeyPath < Decl , TokenSyntax >
168
+ ) -> DeclSyntax {
169
+ // If there's already an access modifier among the modifier list, bail out.
170
+ guard decl. modifiers. accessLevelModifier == nil else { return DeclSyntax ( decl) }
171
+
172
+ var result = decl
173
+ var modifier = modifier
174
+ modifier. trailingTrivia = [ . spaces( 1 ) ]
175
+
176
+ guard var firstModifier = decl. modifiers. first else {
177
+ // If there are no modifiers at all, add the one being requested, moving the leading trivia
178
+ // from the decl keyword to that modifier (to preserve leading comments, newlines, etc.).
179
+ modifier. leadingTrivia = decl [ keyPath: declKeywordKeyPath] . leadingTrivia
180
+ result [ keyPath: declKeywordKeyPath] . leadingTrivia = [ ]
181
+ result. modifiers = . init( [ modifier] )
182
+ return DeclSyntax ( result)
183
+ }
184
+
185
+ // Otherwise, insert the modifier at the front of the modifier list, moving the (original) first
186
+ // modifier's leading trivia to the new one (to preserve leading comments, newlines, etc.).
187
+ modifier. leadingTrivia = firstModifier. leadingTrivia
188
+ firstModifier. leadingTrivia = [ ]
189
+ result. modifiers [ result. modifiers. startIndex] = firstModifier
190
+ result. modifiers. insert ( modifier, at: result. modifiers. startIndex)
191
+ return DeclSyntax ( result)
192
+ }
0 commit comments