mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
With the binding of configurations to their associated rule types "unknown configuration" errors can be made more specific mentioning also the rule's identifier in the printed message.
152 lines
5.1 KiB
Swift
152 lines
5.1 KiB
Swift
import SwiftSyntax
|
|
|
|
struct RedundantSetAccessControlRule: ConfigurationProviderRule, SwiftSyntaxRule {
|
|
var configuration = SeverityConfiguration<Self>(.warning)
|
|
|
|
static let description = RuleDescription(
|
|
identifier: "redundant_set_access_control",
|
|
name: "Redundant Access Control for Setter",
|
|
description: "Property setter access level shouldn't be explicit if " +
|
|
"it's the same as the variable access level",
|
|
kind: .idiomatic,
|
|
nonTriggeringExamples: [
|
|
Example("private(set) public var foo: Int"),
|
|
Example("public let foo: Int"),
|
|
Example("public var foo: Int"),
|
|
Example("var foo: Int"),
|
|
Example("""
|
|
private final class A {
|
|
private(set) var value: Int
|
|
}
|
|
"""),
|
|
Example("""
|
|
fileprivate class A {
|
|
public fileprivate(set) var value: Int
|
|
}
|
|
""", excludeFromDocumentation: true),
|
|
Example("""
|
|
extension Color {
|
|
public internal(set) static var someColor = Color.anotherColor
|
|
}
|
|
""")
|
|
],
|
|
triggeringExamples: [
|
|
Example("↓private(set) private var foo: Int"),
|
|
Example("↓fileprivate(set) fileprivate var foo: Int"),
|
|
Example("↓internal(set) internal var foo: Int"),
|
|
Example("↓public(set) public var foo: Int"),
|
|
Example("""
|
|
open class Foo {
|
|
↓open(set) open var bar: Int
|
|
}
|
|
"""),
|
|
Example("""
|
|
class A {
|
|
↓internal(set) var value: Int
|
|
}
|
|
"""),
|
|
Example("""
|
|
internal class A {
|
|
↓internal(set) var value: Int
|
|
}
|
|
"""),
|
|
Example("""
|
|
fileprivate class A {
|
|
↓fileprivate(set) var value: Int
|
|
}
|
|
""")
|
|
]
|
|
)
|
|
|
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
|
Visitor(viewMode: .sourceAccurate)
|
|
}
|
|
}
|
|
|
|
private extension RedundantSetAccessControlRule {
|
|
final class Visitor: ViolationsSyntaxVisitor {
|
|
override var skippableDeclarations: [DeclSyntaxProtocol.Type] {
|
|
[FunctionDeclSyntax.self]
|
|
}
|
|
|
|
override func visitPost(_ node: VariableDeclSyntax) {
|
|
guard let modifiers = node.modifiers, let setAccessor = modifiers.setAccessor else {
|
|
return
|
|
}
|
|
|
|
let uniqueModifiers = Set(modifiers.map(\.name.tokenKind))
|
|
if uniqueModifiers.count != modifiers.count {
|
|
violations.append(modifiers.positionAfterSkippingLeadingTrivia)
|
|
return
|
|
}
|
|
|
|
if setAccessor.name.tokenKind == .keyword(.fileprivate),
|
|
modifiers.getAccessor == nil,
|
|
let closestDeclModifiers = node.closestDecl()?.modifiers {
|
|
let closestDeclIsFilePrivate = closestDeclModifiers.contains {
|
|
$0.name.tokenKind == .keyword(.fileprivate)
|
|
}
|
|
|
|
if closestDeclIsFilePrivate {
|
|
violations.append(modifiers.positionAfterSkippingLeadingTrivia)
|
|
return
|
|
}
|
|
}
|
|
|
|
if setAccessor.name.tokenKind == .keyword(.internal),
|
|
modifiers.getAccessor == nil,
|
|
let closesDecl = node.closestDecl(),
|
|
let closestDeclModifiers = closesDecl.modifiers {
|
|
let closestDeclIsInternal = closestDeclModifiers.isEmpty || closestDeclModifiers.contains {
|
|
$0.name.tokenKind == .keyword(.internal)
|
|
}
|
|
|
|
if closestDeclIsInternal {
|
|
violations.append(modifiers.positionAfterSkippingLeadingTrivia)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension SyntaxProtocol {
|
|
func closestDecl() -> DeclSyntax? {
|
|
if let decl = self.parent?.as(DeclSyntax.self) {
|
|
return decl
|
|
}
|
|
|
|
return parent?.closestDecl()
|
|
}
|
|
}
|
|
|
|
private extension DeclSyntax {
|
|
var modifiers: ModifierListSyntax? {
|
|
if let decl = self.as(ClassDeclSyntax.self) {
|
|
return decl.modifiers ?? ModifierListSyntax([])
|
|
} else if let decl = self.as(ActorDeclSyntax.self) {
|
|
return decl.modifiers ?? ModifierListSyntax([])
|
|
} else if let decl = self.as(StructDeclSyntax.self) {
|
|
return decl.modifiers ?? ModifierListSyntax([])
|
|
} else if let decl = self.as(ProtocolDeclSyntax.self) {
|
|
return decl.modifiers ?? ModifierListSyntax([])
|
|
} else if let decl = self.as(ExtensionDeclSyntax.self) {
|
|
return decl.modifiers ?? ModifierListSyntax([])
|
|
} else if let decl = self.as(EnumDeclSyntax.self) {
|
|
return decl.modifiers ?? ModifierListSyntax([])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private extension ModifierListSyntax {
|
|
var setAccessor: DeclModifierSyntax? {
|
|
first { $0.detail?.detail.tokenKind == .identifier("set") }
|
|
}
|
|
|
|
var getAccessor: DeclModifierSyntax? {
|
|
first { $0.detail == nil }
|
|
}
|
|
}
|