mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
* Compile with `-strict-concurrency=complete` Only in Bazel for now, because this is considered an unsafe flag in SwiftPM which would lead to warnings for downstream consumers of SwiftLint using SwiftPM. Some imports of SwiftSyntax need the `@preconcurrency` annotation until https://github.com/apple/swift-syntax/pull/2322 is available in a release. The following SwiftLint libraries have `-strict-concurrency=complete` applied: * SwiftLintCoreMacros * SwiftLintBuiltInRules * SwiftLintExtraRules The following SwiftLint libraries don't have the flag applied and need to be migrated: * SwiftLintCore * swiftlint (CLI target) So really the rules and macros are now being compiled with `-strict-concurrency=complete`, but the core infrastructure of SwiftLint is not. Still, given that Swift 6 will eventually make these warnings errors by default, it's good to prevent issues from creeping in earlier rather than later. * Add CI job to build with strict concurrency
230 lines
8.2 KiB
Swift
230 lines
8.2 KiB
Swift
import SwiftSyntax
|
|
|
|
@SwiftSyntaxRule
|
|
struct PreferSelfInStaticReferencesRule: SwiftSyntaxCorrectableRule, OptInRule {
|
|
var configuration = SeverityConfiguration<Self>(.warning)
|
|
|
|
static let description = RuleDescription(
|
|
identifier: "prefer_self_in_static_references",
|
|
name: "Prefer Self in Static References",
|
|
description: "Use `Self` to refer to the surrounding type name",
|
|
kind: .style,
|
|
nonTriggeringExamples: PreferSelfInStaticReferencesRuleExamples.nonTriggeringExamples,
|
|
triggeringExamples: PreferSelfInStaticReferencesRuleExamples.triggeringExamples,
|
|
corrections: PreferSelfInStaticReferencesRuleExamples.corrections
|
|
)
|
|
}
|
|
|
|
private extension PreferSelfInStaticReferencesRule {
|
|
private enum ParentDeclBehavior {
|
|
case likeClass(name: String)
|
|
case likeStruct(String)
|
|
case skipReferences
|
|
|
|
var parentName: String? {
|
|
switch self {
|
|
case let .likeClass(name): return name
|
|
case let .likeStruct(name): return name
|
|
case .skipReferences: return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum VariableDeclBehavior {
|
|
case handleReferences
|
|
case skipReferences
|
|
}
|
|
|
|
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
|
|
private var parentDeclScopes = Stack<ParentDeclBehavior>()
|
|
private var variableDeclScopes = Stack<VariableDeclBehavior>()
|
|
|
|
override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind {
|
|
parentDeclScopes.push(.likeClass(name: node.name.text))
|
|
return .skipChildren
|
|
}
|
|
|
|
override func visitPost(_ node: ActorDeclSyntax) {
|
|
parentDeclScopes.pop()
|
|
}
|
|
|
|
override func visit(_ node: AttributeSyntax) -> SyntaxVisitorContinueKind {
|
|
if case .skipReferences = variableDeclScopes.peek() {
|
|
return .skipChildren
|
|
}
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
|
|
parentDeclScopes.push(.likeClass(name: node.name.text))
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visitPost(_ node: ClassDeclSyntax) {
|
|
parentDeclScopes.pop()
|
|
}
|
|
|
|
override func visit(_ node: CodeBlockItemListSyntax) -> SyntaxVisitorContinueKind {
|
|
variableDeclScopes.push(.handleReferences)
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visitPost(_ node: CodeBlockItemListSyntax) {
|
|
variableDeclScopes.pop()
|
|
}
|
|
|
|
override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
|
|
parentDeclScopes.push(.likeStruct(node.name.text))
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visitPost(_ node: EnumDeclSyntax) {
|
|
parentDeclScopes.pop()
|
|
}
|
|
|
|
override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind {
|
|
parentDeclScopes.push(.skipReferences)
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visitPost(_ node: ExtensionDeclSyntax) {
|
|
parentDeclScopes.pop()
|
|
}
|
|
|
|
override func visit(_ node: MemberAccessExprSyntax) -> SyntaxVisitorContinueKind {
|
|
if case .likeClass = parentDeclScopes.peek() {
|
|
if node.declName.baseName.tokenKind == .keyword(.self) {
|
|
return .skipChildren
|
|
}
|
|
}
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visitPost(_ node: DeclReferenceExprSyntax) {
|
|
guard let parent = node.parent, !parent.is(GenericSpecializationExprSyntax.self),
|
|
node.keyPathInParent != \MemberAccessExprSyntax.declName else {
|
|
return
|
|
}
|
|
if parent.is(FunctionCallExprSyntax.self), case .likeClass = parentDeclScopes.peek() {
|
|
return
|
|
}
|
|
addViolation(on: node.baseName)
|
|
}
|
|
|
|
override func visit(_ node: InitializerClauseSyntax) -> SyntaxVisitorContinueKind {
|
|
if case .skipReferences = variableDeclScopes.peek() {
|
|
return .skipChildren
|
|
}
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visit(_ node: MemberBlockSyntax) -> SyntaxVisitorContinueKind {
|
|
if case .likeClass = parentDeclScopes.peek() {
|
|
variableDeclScopes.push(.skipReferences)
|
|
} else {
|
|
variableDeclScopes.push(.handleReferences)
|
|
}
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visitPost(_ node: MemberBlockSyntax) {
|
|
variableDeclScopes.pop()
|
|
}
|
|
|
|
override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind {
|
|
if case .likeClass = parentDeclScopes.peek(), case .identifier("selector") = node.macroName.tokenKind {
|
|
return .visitChildren
|
|
}
|
|
return .skipChildren
|
|
}
|
|
|
|
override func visit(_ node: FunctionParameterClauseSyntax) -> SyntaxVisitorContinueKind {
|
|
if case .likeStruct = parentDeclScopes.peek() {
|
|
return .visitChildren
|
|
}
|
|
return .skipChildren
|
|
}
|
|
|
|
override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {
|
|
parentDeclScopes.push(.skipReferences)
|
|
return .skipChildren
|
|
}
|
|
|
|
override func visitPost(_ node: ProtocolDeclSyntax) {
|
|
parentDeclScopes.pop()
|
|
}
|
|
|
|
override func visit(_ node: ReturnClauseSyntax) -> SyntaxVisitorContinueKind {
|
|
if case .likeStruct = parentDeclScopes.peek() {
|
|
return .visitChildren
|
|
}
|
|
return .skipChildren
|
|
}
|
|
|
|
override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
|
|
parentDeclScopes.push(.likeStruct(node.name.text))
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visitPost(_ node: StructDeclSyntax) {
|
|
parentDeclScopes.pop()
|
|
}
|
|
|
|
override func visit(_ node: GenericArgumentListSyntax) -> SyntaxVisitorContinueKind {
|
|
if case .likeClass = parentDeclScopes.peek() {
|
|
return .skipChildren
|
|
}
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visitPost(_ node: IdentifierTypeSyntax) {
|
|
guard let parent = node.parent else {
|
|
return
|
|
}
|
|
if case .likeClass = parentDeclScopes.peek(), parent.is(GenericArgumentSyntax.self) {
|
|
// Type is a generic parameter in a class.
|
|
return
|
|
}
|
|
if node.genericArguments == nil {
|
|
// Type is specialized.
|
|
addViolation(on: node.name)
|
|
}
|
|
}
|
|
|
|
override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind {
|
|
if case .likeClass = parentDeclScopes.peek() {
|
|
return .skipChildren
|
|
}
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visit(_ node: TypeAnnotationSyntax) -> SyntaxVisitorContinueKind {
|
|
guard case .likeStruct = parentDeclScopes.peek() else {
|
|
return .skipChildren
|
|
}
|
|
if let varDecl = node.parent?.parent?.parent?.as(VariableDeclSyntax.self) {
|
|
if varDecl.parent?.is(CodeBlockItemSyntax.self) == true // Local variable declaration
|
|
|| varDecl.bindings.onlyElement?.accessorBlock != nil // Computed property
|
|
|| !node.type.is(IdentifierTypeSyntax.self) // Complex or collection type
|
|
{
|
|
return .visitChildren
|
|
}
|
|
}
|
|
return .skipChildren
|
|
}
|
|
|
|
private func addViolation(on node: TokenSyntax) {
|
|
if let parentName = parentDeclScopes.peek()?.parentName, node.tokenKind == .identifier(parentName) {
|
|
violations.append(node.positionAfterSkippingLeadingTrivia)
|
|
violationCorrections.append(
|
|
ViolationCorrection(
|
|
start: node.positionAfterSkippingLeadingTrivia,
|
|
end: node.endPositionBeforeTrailingTrivia,
|
|
replacement: "Self"
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|