117 lines
3.8 KiB
Swift
117 lines
3.8 KiB
Swift
import SwiftSyntax
|
|
|
|
struct PrefixedTopLevelConstantRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
|
var configuration = PrefixedConstantRuleConfiguration(onlyPrivateMembers: false)
|
|
|
|
init() {}
|
|
|
|
static let description = RuleDescription(
|
|
identifier: "prefixed_toplevel_constant",
|
|
name: "Prefixed Top-Level Constant",
|
|
description: "Top-level constants should be prefixed by `k`",
|
|
kind: .style,
|
|
nonTriggeringExamples: [
|
|
Example("private let kFoo = 20.0"),
|
|
Example("public let kFoo = false"),
|
|
Example("internal let kFoo = \"Foo\""),
|
|
Example("let kFoo = true"),
|
|
Example("let Foo = true", configuration: ["only_private": true]),
|
|
Example("struct Foo {\n" +
|
|
" let bar = 20.0\n" +
|
|
"}"),
|
|
Example("private var foo = 20.0"),
|
|
Example("public var foo = false"),
|
|
Example("internal var foo = \"Foo\""),
|
|
Example("var foo = true"),
|
|
Example("var foo = true, bar = true"),
|
|
Example("var foo = true, let kFoo = true"),
|
|
Example("let\n" +
|
|
" kFoo = true"),
|
|
Example("var foo: Int {\n" +
|
|
" return a + b\n" +
|
|
"}"),
|
|
Example("let kFoo = {\n" +
|
|
" return a + b\n" +
|
|
"}()"),
|
|
Example("""
|
|
var foo: String {
|
|
let bar = ""
|
|
return bar
|
|
}
|
|
"""),
|
|
Example("""
|
|
if condition() {
|
|
let result = somethingElse()
|
|
print(result)
|
|
exit()
|
|
}
|
|
"""),
|
|
Example(#"""
|
|
[1, 2, 3, 1000, 4000].forEach { number in
|
|
let isSmall = number < 10
|
|
if isSmall {
|
|
print("\(number) is a small number")
|
|
}
|
|
}
|
|
"""#)
|
|
],
|
|
triggeringExamples: [
|
|
Example("private let ↓Foo = 20.0"),
|
|
Example("public let ↓Foo = false"),
|
|
Example("internal let ↓Foo = \"Foo\""),
|
|
Example("let ↓Foo = true"),
|
|
Example("let ↓foo = 2, ↓bar = true"),
|
|
Example("let\n" +
|
|
" ↓foo = true"),
|
|
Example("let ↓foo = {\n" +
|
|
" return a + b\n" +
|
|
"}()")
|
|
]
|
|
)
|
|
|
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
|
Visitor(onlyPrivateMembers: configuration.onlyPrivateMembers)
|
|
}
|
|
}
|
|
|
|
private extension PrefixedTopLevelConstantRule {
|
|
final class Visitor: ViolationsSyntaxVisitor {
|
|
private let onlyPrivateMembers: Bool
|
|
private let topLevelPrefix = "k"
|
|
|
|
init(onlyPrivateMembers: Bool) {
|
|
self.onlyPrivateMembers = onlyPrivateMembers
|
|
super.init(viewMode: .sourceAccurate)
|
|
}
|
|
|
|
override var skippableDeclarations: [DeclSyntaxProtocol.Type] { .all }
|
|
|
|
override func visitPost(_ node: VariableDeclSyntax) {
|
|
guard node.bindingKeyword.tokenKind == .keyword(.let) else {
|
|
return
|
|
}
|
|
|
|
if onlyPrivateMembers, !node.modifiers.isPrivateOrFileprivate {
|
|
return
|
|
}
|
|
|
|
for binding in node.bindings {
|
|
guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self),
|
|
!pattern.identifier.text.hasPrefix(topLevelPrefix) else {
|
|
continue
|
|
}
|
|
|
|
violations.append(binding.pattern.positionAfterSkippingLeadingTrivia)
|
|
}
|
|
}
|
|
|
|
override func visit(_ node: CodeBlockSyntax) -> SyntaxVisitorContinueKind {
|
|
.skipChildren
|
|
}
|
|
|
|
override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
|
|
.skipChildren
|
|
}
|
|
}
|
|
}
|