Trigger `prefer_self_in_static_references` rule on types

This commit is contained in:
Danny Mösch 2022-11-06 19:23:52 +01:00
parent cf1e2e27dc
commit eee97a7b2b
2 changed files with 68 additions and 13 deletions

View File

@ -23,6 +23,11 @@
[SimplyDanny](https://github.com/SimplyDanny) [SimplyDanny](https://github.com/SimplyDanny)
[#3726](https://github.com/realm/SwiftLint/issues/3726) [#3726](https://github.com/realm/SwiftLint/issues/3726)
* Trigger `prefer_self_in_static_references` rule on more type references like:
* Types (e.g. `let a: MyType? { nil }` -> `let a: Self? { nil }`)
[SimplyDanny](https://github.com/SimplyDanny)
#### Bug Fixes #### Bug Fixes
* Fix `lower_acl_than_parent` rule rewriter by preserving leading whitespace. * Fix `lower_acl_than_parent` rule rewriter by preserving leading whitespace.

View File

@ -71,7 +71,7 @@ struct PreferSelfInStaticReferencesRule: SwiftSyntaxRule, CorrectableRule, Confi
} }
""", excludeFromDocumentation: true), """, excludeFromDocumentation: true),
Example(""" Example("""
class Record<T> { struct Record<T> {
static func get() -> Record<T> { Record<T>() } static func get() -> Record<T> { Record<T>() }
} }
""", excludeFromDocumentation: true), """, excludeFromDocumentation: true),
@ -80,6 +80,13 @@ struct PreferSelfInStaticReferencesRule: SwiftSyntaxRule, CorrectableRule, Confi
@objc var s = "" @objc var s = ""
@objc func f() { _ = #keyPath(C.s) } @objc func f() { _ = #keyPath(C.s) }
} }
""", excludeFromDocumentation: true),
Example("""
class C<T> {
let i = 1
let c: C = C()
func f(c: C) -> KeyPath<C, Int> { \\Self.i }
}
""", excludeFromDocumentation: true) """, excludeFromDocumentation: true)
], ],
triggeringExamples: [ triggeringExamples: [
@ -112,8 +119,8 @@ struct PreferSelfInStaticReferencesRule: SwiftSyntaxRule, CorrectableRule, Confi
static let i = 1 static let i = 1
static func f() -> Int { S.i } static func f() -> Int { S.i }
func g() -> Any { S.self } func g() -> Any { S.self }
func h() -> S { S(j: 2) } func h() -> S { S(j: 2) }
func i() -> KeyPath<S, Int> { \\S.j } func i() -> KeyPath<S, Int> { \\S.j }
func j(@Wrap(-S.i, S.i) n: Int = S.i) {} func j(@Wrap(-S.i, S.i) n: Int = S.i) {}
} }
"""), """),
@ -131,8 +138,8 @@ struct PreferSelfInStaticReferencesRule: SwiftSyntaxRule, CorrectableRule, Confi
Example(""" Example("""
enum E { enum E {
case A case A
static func f() -> E { E.A } static func f() -> E { E.A }
static func g() -> E { E.f() } static func g() -> E { E.f() }
} }
"""), """),
Example(""" Example("""
@ -149,10 +156,31 @@ struct PreferSelfInStaticReferencesRule: SwiftSyntaxRule, CorrectableRule, Confi
""", excludeFromDocumentation: true), """, excludeFromDocumentation: true),
Example(""" Example("""
class C { class C {
let d: C? = nil
var c: C { C() } var c: C { C() }
init() {}
func f(e: C) -> C {
let f: C = C()
return f
}
} }
final class D { final class D {
var d: D { D() } let c: D? = nil
var d: D { D() }
init() {}
func f(e: D) -> D {
let f: D = D()
return f
}
}
struct S {
// let s: S? = nil // Struct cannot contain itself
var t: S { S() }
init() {}
func f(e: S) -> S {
let f: S = S()
return f
}
} }
""", excludeFromDocumentation: true) """, excludeFromDocumentation: true)
], ],
@ -227,13 +255,13 @@ struct PreferSelfInStaticReferencesRule: SwiftSyntaxRule, CorrectableRule, Confi
private class Visitor: ViolationsSyntaxVisitor { private class Visitor: ViolationsSyntaxVisitor {
private enum ParentDeclBehavior { private enum ParentDeclBehavior {
case likeClass(name: String, isFinal: Bool) case likeClass(name: String)
case likeStruct(String) case likeStruct(String)
case skipReferences case skipReferences
var parentName: String? { var parentName: String? {
switch self { switch self {
case let .likeClass(name, _): return name case let .likeClass(name): return name
case let .likeStruct(name): return name case let .likeStruct(name): return name
case .skipReferences: return nil case .skipReferences: return nil
} }
@ -250,7 +278,7 @@ private class Visitor: ViolationsSyntaxVisitor {
private(set) var corrections = [(start: AbsolutePosition, end: AbsolutePosition)]() private(set) var corrections = [(start: AbsolutePosition, end: AbsolutePosition)]()
override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind { override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind {
parentDeclScopes.append(.likeClass(name: node.identifier.text, isFinal: node.modifiers.isFinal)) parentDeclScopes.append(.likeClass(name: node.identifier.text))
return .skipChildren return .skipChildren
} }
@ -259,7 +287,7 @@ private class Visitor: ViolationsSyntaxVisitor {
} }
override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
parentDeclScopes.append(.likeClass(name: node.identifier.text, isFinal: node.modifiers.isFinal)) parentDeclScopes.append(.likeClass(name: node.identifier.text))
return .visitChildren return .visitChildren
} }
@ -310,7 +338,7 @@ private class Visitor: ViolationsSyntaxVisitor {
!parent.is(ArrayElementSyntax.self) else { !parent.is(ArrayElementSyntax.self) else {
return return
} }
if parent.is(FunctionCallExprSyntax.self), case .likeClass(_, false) = parentDeclScopes.last { if parent.is(FunctionCallExprSyntax.self), case .likeClass = parentDeclScopes.last {
return return
} }
addViolation(on: node.identifier) addViolation(on: node.identifier)
@ -362,14 +390,36 @@ private class Visitor: ViolationsSyntaxVisitor {
} }
override func visitPost(_ node: SimpleTypeIdentifierSyntax) { override func visitPost(_ node: SimpleTypeIdentifierSyntax) {
if node.parent?.is(KeyPathExprSyntax.self) == true { guard let parent = node.parent else {
return
}
if case .likeClass = parentDeclScopes.last,
parent.is(GenericArgumentSyntax.self) || parent.is(ReturnClauseSyntax.self) {
// Type is a generic parameter or the return type of a function.
return
}
if node.genericArguments == nil {
// Type is specialized.
addViolation(on: node.name) addViolation(on: node.name)
} }
} }
override func visit(_ node: TypeAnnotationSyntax) -> SyntaxVisitorContinueKind {
guard case .likeStruct = parentDeclScopes.last else {
return .skipChildren
}
if let varDecl = node.parent?.parent?.parent?.as(VariableDeclSyntax.self) {
if varDecl.parent?.is(CodeBlockItemSyntax.self) == true || varDecl.bindings.onlyElement?.accessor != nil {
// Is either a local variable declaration or a computed property.
return .visitChildren
}
}
return .skipChildren
}
override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
if node.bindings.onlyElement?.accessor != nil { if node.bindings.onlyElement?.accessor != nil {
// Computed property // Variable declaration is a computed property.
return .visitChildren return .visitChildren
} }
if case .handleReferences = variableDeclScopes.last { if case .handleReferences = variableDeclScopes.last {