Skip to content

Commit

Permalink
Add --retain-encodable-properties option. Closes #736
Browse files Browse the repository at this point in the history
  • Loading branch information
ileitch committed May 19, 2024
1 parent cdb2ba9 commit 742be59
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
##### Enhancements

- Unused import detection is now enabled by default.
- Added the `--retain-encodable-properties` option to retain all properties on `Encodable` types only.

##### Bug Fixes

Expand Down
6 changes: 5 additions & 1 deletion Sources/Frontend/Commands/ScanCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,12 @@ struct ScanCommand: FrontendCommand {
@Flag(help: "Retain SwiftUI previews")
var retainSwiftUIPreviews: Bool = defaultConfiguration.$retainSwiftUIPreviews.defaultValue

@Flag(help: "Retain properties on Codable types")
@Flag(help: "Retain properties on Codable types (including Encodable and Decodable)")
var retainCodableProperties: Bool = defaultConfiguration.$retainCodableProperties.defaultValue

@Flag(help: "Retain properties on Encodable types only")
var retainEncodableProperties: Bool = defaultConfiguration.$retainEncodableProperties.defaultValue

@Flag(help: "Automatically remove code that can be done so safely without introducing build errors (experimental)")
var autoRemove: Bool = defaultConfiguration.$autoRemove.defaultValue

Expand Down Expand Up @@ -165,6 +168,7 @@ struct ScanCommand: FrontendCommand {
configuration.apply(\.$buildArguments, buildArguments)
configuration.apply(\.$relativeResults, relativeResults)
configuration.apply(\.$retainCodableProperties, retainCodableProperties)
configuration.apply(\.$retainEncodableProperties, retainEncodableProperties)
configuration.apply(\.$jsonPackageManifestPath, jsonPackageManifestPath)

try scanBehavior.main { project in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,23 @@ final class CodablePropertyRetainer: SourceGraphMutator {
}

func mutate() {
guard configuration.retainCodableProperties else { return }
if configuration.retainCodableProperties {
for decl in graph.declarations(ofKinds: Declaration.Kind.discreteConformableKinds) {
guard graph.isCodable(decl) else { continue }

for decl in graph.declarations(ofKinds: Declaration.Kind.discreteConformableKinds) {
guard graph.isCodable(decl) else { continue }
for decl in decl.declarations {
guard decl.kind == .varInstance else { continue }
graph.markRetained(decl)
}
}
} else if configuration.retainEncodableProperties {
for decl in graph.declarations(ofKinds: Declaration.Kind.discreteConformableKinds) {
guard graph.isEncodable(decl) else { continue }

for decl in decl.declarations {
guard decl.kind == .varInstance else { continue }
graph.markRetained(decl)
for decl in decl.declarations {
guard decl.kind == .varInstance else { continue }
graph.markRetained(decl)
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions Sources/PeripheryKit/SourceGraph/SourceGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,13 @@ public final class SourceGraph {
return [.protocol, .typealias].contains($0.kind) && codableTypes.contains(name)
}
}

func isEncodable(_ decl: Declaration) -> Bool {
let encodableTypes = ["Encodable"] + configuration.externalEncodableProtocols + configuration.externalCodableProtocols

return inheritedTypeReferences(of: decl).contains {
guard let name = $0.name else { return false }
return [.protocol, .typealias].contains($0.kind) && encodableTypes.contains(name)
}
}
}
10 changes: 10 additions & 0 deletions Sources/Shared/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ public final class Configuration {
@Setting(key: "retain_codable_properties", defaultValue: false)
public var retainCodableProperties: Bool

@Setting(key: "retain_encodable_properties", defaultValue: false)
public var retainEncodableProperties: Bool

@Setting(key: "auto_remove", defaultValue: false)
public var autoRemove: Bool

Expand Down Expand Up @@ -254,6 +257,10 @@ public final class Configuration {
if $retainCodableProperties.hasNonDefaultValue {
config[$retainCodableProperties.key] = retainCodableProperties
}

if $retainEncodableProperties.hasNonDefaultValue {
config[$retainEncodableProperties.key] = retainEncodableProperties
}

if $jsonPackageManifestPath.hasNonDefaultValue {
config[$jsonPackageManifestPath.key] = jsonPackageManifestPath
Expand Down Expand Up @@ -344,6 +351,8 @@ public final class Configuration {
$relativeResults.assign(value)
case $retainCodableProperties.key:
$retainCodableProperties.assign(value)
case $retainEncodableProperties.key:
$retainEncodableProperties.assign(value)
case $jsonPackageManifestPath.key:
$jsonPackageManifestPath.assign(value)
default:
Expand Down Expand Up @@ -386,6 +395,7 @@ public final class Configuration {
$buildArguments.reset()
$relativeResults.reset()
$retainCodableProperties.reset()
$retainEncodableProperties.reset()
$jsonPackageManifestPath.reset()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation

public struct FixtureStruct15: Encodable {
let unused: Int

init(unused: Int) {
self.unused = unused
}
}
22 changes: 22 additions & 0 deletions Tests/PeripheryTests/RetentionTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,28 @@ final class RetentionTest: FixtureSourceGraphTestCase {
}
}

func testRetainsEncodableProperties() {
configuration.retainEncodableProperties = false
configuration.retainAssignOnlyProperties = false

analyze(retainPublic: true) {
assertReferenced(.struct("FixtureStruct15")) {
self.assertNotReferenced(.functionConstructor("init(unused:)"))
self.assertAssignOnlyProperty(.varInstance("unused"))
}
}

configuration.retainEncodableProperties = true

analyze(retainPublic: true) {
assertReferenced(.struct("FixtureStruct15")) {
self.assertNotReferenced(.functionConstructor("init(unused:)"))
self.assertReferenced(.varInstance("unused"))
self.assertNotAssignOnlyProperty(.varInstance("unused"))
}
}
}

func testRetainsFilesOption() {
configuration.retainFiles = [testFixturePath.string]

Expand Down

0 comments on commit 742be59

Please sign in to comment.