13
13
import SwiftFormatCore
14
14
import SwiftSyntax
15
15
16
- /// Declarations at file scope should be declared `fileprivate`, not `private`.
16
+ /// Declarations at file scope with effective private access should be consistently declared as
17
+ /// either `fileprivate` or `private`, determined by configuration.
17
18
///
18
- /// Using `private` at file scope actually gives the declaration `fileprivate` visibility, so using
19
- /// `fileprivate` explicitly is a better indicator of intent .
19
+ /// Lint: If a file-scoped declaration has formal access opposite to the desired access level in the
20
+ /// formatter's configuration, a lint error is raised .
20
21
///
21
- /// Lint: If a file-scoped declaration has `private` visibility, a lint error is raised.
22
- ///
23
- /// Format: File-scoped declarations that have `private` visibility will have their visibility
24
- /// changed to `fileprivate`.
25
- public final class FileprivateAtFileScope : SyntaxFormatRule {
22
+ /// Format: File-scoped declarations that have formal access opposite to the desired access level in
23
+ /// the formatter's configuration will have their access level changed.
24
+ public final class FileScopedDeclarationPrivacy : SyntaxFormatRule {
26
25
public override func visit( _ node: SourceFileSyntax ) -> Syntax {
27
26
let newStatements = rewrittenCodeBlockItems ( node. statements)
28
27
return Syntax ( node. withStatements ( newStatements) )
29
28
}
30
29
31
30
/// Returns a list of code block items equivalent to the given list, but where any file-scoped
32
- /// declarations have had their `private` modifier replaced by `fileprivate` if present.
31
+ /// declarations with effective private access have had their formal access level rewritten, if
32
+ /// necessary, to be either `private` or `fileprivate`, as determined by the formatter
33
+ /// configuration.
33
34
///
34
35
/// - Parameter codeBlockItems: The list of code block items to rewrite.
35
36
/// - Returns: A new `CodeBlockItemListSyntax` that has possibly been rewritten.
@@ -100,7 +101,9 @@ public final class FileprivateAtFileScope: SyntaxFormatRule {
100
101
}
101
102
102
103
/// Returns a new `IfConfigDeclSyntax` equivalent to the given node, but where any file-scoped
103
- /// declarations have had their `private` modifier replaced by `fileprivate` if present.
104
+ /// declarations with effective private access have had their formal access level rewritten, if
105
+ /// necessary, to be either `private` or `fileprivate`, as determined by the formatter
106
+ /// configuration.
104
107
///
105
108
/// - Parameter ifConfigDecl: The `IfConfigDeclSyntax` to rewrite.
106
109
/// - Returns: A new `IfConfigDeclSyntax` that has possibly been rewritten.
@@ -119,8 +122,8 @@ public final class FileprivateAtFileScope: SyntaxFormatRule {
119
122
/// Returns a rewritten version of the given declaration if its modifier list contains `private`
120
123
/// that contains `fileprivate` instead.
121
124
///
122
- /// If the modifier list does not contain `private`, the original declaration is returned
123
- /// unchanged.
125
+ /// If the modifier list is not inconsistent with the configured access level, the original
126
+ /// declaration is returned unchanged.
124
127
///
125
128
/// - Parameters:
126
129
/// - decl: The declaration to possibly rewrite.
@@ -133,15 +136,30 @@ public final class FileprivateAtFileScope: SyntaxFormatRule {
133
136
modifiers: ModifierListSyntax ? ,
134
137
factory: ( ModifierListSyntax ? ) -> DeclType
135
138
) -> DeclType {
136
- guard let modifiers = modifiers, modifiers. has ( modifier: " private " ) else {
139
+ let invalidAccess : TokenKind
140
+ let validAccess : TokenKind
141
+ let diagnostic : Diagnostic . Message
142
+
143
+ switch context. configuration. fileScopedDeclarationPrivacy. accessLevel {
144
+ case . private:
145
+ invalidAccess = . fileprivateKeyword
146
+ validAccess = . privateKeyword
147
+ diagnostic = . replaceFileprivateWithPrivate
148
+ case . fileprivate:
149
+ invalidAccess = . privateKeyword
150
+ validAccess = . fileprivateKeyword
151
+ diagnostic = . replacePrivateWithFileprivate
152
+ }
153
+
154
+ guard let modifiers = modifiers, modifiers. has ( modifier: invalidAccess) else {
137
155
return decl
138
156
}
139
157
140
158
let newModifiers = modifiers. map { modifier -> DeclModifierSyntax in
141
159
let name = modifier. name
142
- if name. tokenKind == . privateKeyword {
143
- diagnose ( . replacePrivateWithFileprivate , on: name)
144
- return modifier. withName ( name. withKind ( . fileprivateKeyword ) )
160
+ if name. tokenKind == invalidAccess {
161
+ diagnose ( diagnostic , on: name)
162
+ return modifier. withName ( name. withKind ( validAccess ) )
145
163
}
146
164
return modifier
147
165
}
@@ -152,4 +170,7 @@ public final class FileprivateAtFileScope: SyntaxFormatRule {
152
170
extension Diagnostic . Message {
153
171
public static let replacePrivateWithFileprivate =
154
172
Diagnostic . Message ( . warning, " replace 'private' with 'fileprivate' on file-scoped declarations " )
173
+
174
+ public static let replaceFileprivateWithPrivate =
175
+ Diagnostic . Message ( . warning, " replace 'fileprivate' with 'private' on file-scoped declarations " )
155
176
}
0 commit comments