mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
Check that a reference is not just extended
This commit is contained in:
@@ -223,6 +223,11 @@
|
||||
[SimplyDanny](https://github.com/SimplyDanny)
|
||||
[#5489](https://github.com/realm/SwiftLint/pull/5489)
|
||||
|
||||
* Ensure that declarations referenced only as extended types do not count as
|
||||
used by means of the `unused_declaration` rule.
|
||||
[SimplyDanny](https://github.com/SimplyDanny)
|
||||
[#5550](https://github.com/realm/SwiftLint/issues/5550)
|
||||
|
||||
* Fix some false positives in `multiline_literal_brackets` rule that would
|
||||
happen when comments are present.
|
||||
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||
|
||||
@@ -46,7 +46,7 @@ struct UnusedDeclarationRule: AnalyzerRule, CollectingRule {
|
||||
}
|
||||
|
||||
return FileUSRs(
|
||||
referenced: file.referencedUSRs(index: index),
|
||||
referenced: file.referencedUSRs(index: index, editorOpen: editorOpen),
|
||||
declared: file.declaredUSRs(index: index,
|
||||
editorOpen: editorOpen,
|
||||
compilerArguments: compilerArguments,
|
||||
@@ -89,11 +89,16 @@ private extension SwiftLintFile {
|
||||
.map(SourceKittenDictionary.init)
|
||||
}
|
||||
|
||||
func referencedUSRs(index: SourceKittenDictionary) -> Set<String> {
|
||||
return Set(index.traverseEntitiesDepthFirst { _, entity -> String? in
|
||||
func referencedUSRs(index: SourceKittenDictionary, editorOpen: SourceKittenDictionary) -> Set<String> {
|
||||
return Set(index.traverseEntitiesDepthFirst { parent, entity -> String? in
|
||||
if let usr = entity.usr,
|
||||
let kind = entity.kind,
|
||||
kind.starts(with: "source.lang.swift.ref") {
|
||||
let kind = entity.kind,
|
||||
kind.starts(with: "source.lang.swift.ref"),
|
||||
!parent.extends(reference: entity),
|
||||
let line = entity.line,
|
||||
let column = entity.column,
|
||||
let nameOffset = stringView.byteOffset(forLine: line, bytePosition: column),
|
||||
editorOpen.propertyAtOffset(nameOffset, property: \.kind) != "source.lang.swift.decl.extension" {
|
||||
return usr
|
||||
}
|
||||
|
||||
@@ -133,7 +138,8 @@ private extension SwiftLintFile {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !configuration.includePublicAndOpen, [.public, .open].contains(editorOpen.aclAtOffset(nameOffset)) {
|
||||
if !configuration.includePublicAndOpen,
|
||||
[.public, .open].contains(editorOpen.propertyAtOffset(nameOffset, property: \.accessibility)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -230,14 +236,14 @@ private extension SourceKittenDictionary {
|
||||
return value["key.is_implicit"] as? Bool == true
|
||||
}
|
||||
|
||||
func aclAtOffset(_ offset: ByteCount) -> AccessControlLevel? {
|
||||
func propertyAtOffset<T>(_ offset: ByteCount, property: KeyPath<Self, T?>) -> T? {
|
||||
if let nameOffset,
|
||||
nameOffset == offset,
|
||||
let acl = accessibility {
|
||||
return acl
|
||||
let field = self[keyPath: property] {
|
||||
return field
|
||||
}
|
||||
for child in substructure {
|
||||
if let acl = child.aclAtOffset(offset) {
|
||||
if let acl = child.propertyAtOffset(offset, property: property) {
|
||||
return acl
|
||||
}
|
||||
}
|
||||
@@ -296,6 +302,21 @@ private extension SourceKittenDictionary {
|
||||
|
||||
return resultBuilderStaticMethods.contains(name)
|
||||
}
|
||||
|
||||
func extends(reference other: Self) -> Bool {
|
||||
if let kind, kind.starts(with: "source.lang.swift.decl.extension") {
|
||||
let extendedKind = kind.components(separatedBy: ".").last
|
||||
return extendedKind != nil && extendedKind == other.referencedKind
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var referencedKind: String? {
|
||||
if let kind, kind.starts(with: "source.lang.swift.ref") {
|
||||
return kind.components(separatedBy: ".").last
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Skip initializers, deinit, enum cases and subscripts since we can't reliably detect if they're used.
|
||||
|
||||
@@ -207,8 +207,23 @@ struct UnusedDeclarationRuleExamples {
|
||||
}
|
||||
|
||||
_ = ComponentBuilder()
|
||||
"""),
|
||||
Example("""
|
||||
protocol ↓Foo {}
|
||||
extension Foo {}
|
||||
"""),
|
||||
Example("""
|
||||
class ↓C<T> {}
|
||||
extension C<Int> {}
|
||||
""")
|
||||
] + platformSpecificTriggeringExamples
|
||||
] + ["actor", "enum", "class", "struct"].map {
|
||||
Example("""
|
||||
protocol Foo {}
|
||||
\($0) ↓FooImpl {}
|
||||
extension FooImpl {}
|
||||
extension FooImpl: Foo {}
|
||||
""", excludeFromDocumentation: true)
|
||||
} + platformSpecificTriggeringExamples
|
||||
|
||||
#if os(macOS)
|
||||
private static let platformSpecificNonTriggeringExamples = [
|
||||
|
||||
Reference in New Issue
Block a user