Skip to content

Commit

Permalink
Identify unused CodingKey enum cases
Browse files Browse the repository at this point in the history
  • Loading branch information
ileitch committed Dec 29, 2023
1 parent 46d7a51 commit 11ebbc5
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 95 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

- Add experimental unused import analysis.
- The option `--external-encodable-protocols` is deprecated, use `--external-codable-protocols` instead.
- CodingKey enum case are now identified as unused if the corresponding property is also unused in the Codable type.

##### Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,28 @@ final class CodingKeyEnumReferenceBuilder: SourceGraphMutator {
return [.protocol, .typealias].contains($0.kind) && codableTypes.contains(name)
}

if isCodingKey && isParentCodable {
for usr in enumDeclaration.usrs {
let newReference = Reference(kind: .enum, usr: usr, location: enumDeclaration.location)
newReference.name = enumDeclaration.name
newReference.parent = parent
graph.add(newReference, from: parent)
guard isCodingKey, isParentCodable else { continue }

// Build a reference from the Codable type to the CodingKey enum.
for usr in enumDeclaration.usrs {
let newReference = Reference(kind: .enum, usr: usr, location: enumDeclaration.location)
newReference.name = enumDeclaration.name
newReference.parent = parent
graph.add(newReference, from: parent)
}

// For each property in the Codable type, build a reference to its corresponding
// CodingKey enum element.
for decl in parent.declarations {
guard decl.kind == .varInstance,
let enumCase = enumDeclaration.declarations.first(where: { $0.kind == .enumelement && $0.name == decl.name })
else { continue }

for usr in enumCase.usrs {
let newReference = Reference(kind: .enumelement, usr: usr, location: decl.location)
newReference.name = enumCase.name
newReference.parent = decl
graph.add(newReference, from: decl)
}
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public final class SourceGraphMutatorRunner {
PubliclyAccessibleRetainer.self,
XCTestRetainer.self,
SwiftUIRetainer.self,
EncodablePropertyRetainer.self,
StringInterpolationAppendInterpolationRetainer.self,
PropertyWrapperRetainer.self,
ResultBuilderRetainer.self,
Expand Down
10 changes: 10 additions & 0 deletions Tests/Fixtures/RetentionFixtures/testCodingKeyEnum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,13 @@ public struct FixtureClass218: CustomStringConvertible {
case someVar
}
}

public class FixtureClass220: Encodable {
public var someUsedVar: String?
var someUnusedVar: String?

enum CodingKeys: CodingKey {
case someUsedVar
case someUnusedVar
}
}
46 changes: 9 additions & 37 deletions Tests/PeripheryTests/RetentionTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,15 @@ final class RetentionTest: FixtureSourceGraphTestCase {
assertReferenced(.struct("FixtureClass218")) {
self.assertReferenced(.enum("CodingKeys"))
}
assertReferenced(.class("FixtureClass220")) {
self.assertReferenced(.varInstance("someUsedVar"))
self.assertNotReferenced(.varInstance("someUnusedVar"))

self.assertReferenced(.enum("CodingKeys")) {
self.assertReferenced(.enumelement("someUsedVar"))
self.assertNotReferenced(.enumelement("someUnusedVar"))
}
}
}
}

Expand Down Expand Up @@ -951,43 +960,6 @@ final class RetentionTest: FixtureSourceGraphTestCase {
}
}

func testRetainsEncodableProperties() {
let configuration = Configuration.shared
// CustomStringConvertible doesn't actually inherit Encodable, we're just using it because we don't have an
// external module in which to declare our own type.
configuration.externalCodableProtocols = ["CustomStringConvertible"]

analyze(retainPublic: true) {
self.assertReferenced(.class("FixtureClass204")) {
self.assertReferenced(.varInstance("someVar"))
}

self.assertReferenced(.class("FixtureClass205")) {
self.assertReferenced(.varInstance("someVar"))
}

self.assertReferenced(.class("FixtureClass206")) {
self.assertReferenced(.varInstance("someVar"))
}

self.assertReferenced(.class("FixtureClass207")) {
self.assertReferenced(.varInstance("someVar"))
}

self.assertReferenced(.class("FixtureClass208")) {
self.assertReferenced(.varInstance("someVar"))
}

self.assertReferenced(.class("FixtureClass209")) {
self.assertReferenced(.varInstance("someVar"))
}

self.assertReferenced(.class("FixtureClass210")) {
self.assertReferenced(.varInstance("someVar"))
}
}
}

func testCircularTypeInheritance() {
analyze {
// Intentionally blank.
Expand Down

0 comments on commit 11ebbc5

Please sign in to comment.