-
-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Detect assign-only properties on structs with synthesized initializers
- Loading branch information
Showing
12 changed files
with
152 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
Sources/PeripheryKit/SourceGraph/Mutators/StructImplicitInitializerReferenceBuilder.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import Foundation | ||
import Shared | ||
|
||
/// Builds references from struct implicit initializers to the properties it assigns. | ||
final class StructImplicitInitializerReferenceBuilder: SourceGraphMutator { | ||
private let graph: SourceGraph | ||
|
||
init(graph: SourceGraph, configuration: Configuration) { | ||
self.graph = graph | ||
} | ||
|
||
func mutate() throws { | ||
for structDecl in graph.declarations(ofKind: .struct) { | ||
let implicitInitDecl = structDecl.declarations.first { $0.kind == .functionConstructor && $0.isImplicit } | ||
|
||
guard let implicitInitDecl, let name = implicitInitDecl.name else { continue } | ||
|
||
let propertyNames = name | ||
.dropFirst("init(".count) | ||
.dropLast(")".count) | ||
.split(separator: ":") | ||
.map(String.init) | ||
|
||
let initPropertyDecls = structDecl.declarations.filter { | ||
guard $0.kind == .varInstance, let name = $0.name, propertyNames.contains(name) | ||
else { return false } | ||
return true | ||
} | ||
|
||
for propertyDecl in initPropertyDecls { | ||
guard let setterDecl = propertyDecl.declarations.first(where: { $0.kind == .functionAccessorSetter }) | ||
else { continue } | ||
|
||
for decl in [propertyDecl, setterDecl] { | ||
for usr in decl.usrs { | ||
let ref = Reference(kind: decl.kind, usr: usr, location: implicitInitDecl.location) | ||
ref.name = decl.name | ||
ref.parent = implicitInitDecl | ||
graph.add(ref, from: implicitInitDecl) | ||
} | ||
} | ||
} | ||
|
||
// The index contains references to properties at the call site of the implicit | ||
// initializer. This pattern is contrary to explicit initializers, where only the | ||
// initializer is referenced. Now that we've built the property references stemming from | ||
// the implicit initializer, we can remove the additional property references at the | ||
// call site. This enables assign-only property detection for structs using implicit | ||
// initializers. | ||
for initRef in graph.references(to: implicitInitDecl) { | ||
guard let caller = initRef.parent, caller != structDecl else { continue } | ||
|
||
let sortedReferences = caller.references.sorted() | ||
|
||
for initPropertyDecl in initPropertyDecls { | ||
let firstRef = sortedReferences.first { initPropertyDecl.usrs.contains($0.usr) && $0.location > initRef.location } | ||
|
||
if let firstRef { | ||
graph.remove(firstRef) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 0 additions & 40 deletions
40
Tests/Fixtures/RetentionFixtures/testRetainsEncodableProperties.swift
This file was deleted.
Oops, something went wrong.
18 changes: 18 additions & 0 deletions
18
Tests/Fixtures/RetentionFixtures/testStructImplicitInitializer.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import Foundation | ||
|
||
public struct FixtureStruct13_Codable: Codable { | ||
let assignOnly: Int | ||
} | ||
|
||
public struct FixtureStruct13_NotCodable { | ||
let assignOnly: Int | ||
let used: Int | ||
} | ||
|
||
public struct FixtureStruct13Retainer { | ||
public func retain() throws { | ||
let data = "".data(using: .utf8)! | ||
_ = try JSONDecoder().decode(FixtureStruct13_Codable.self, from: data) | ||
_ = FixtureStruct13_NotCodable(assignOnly: 0, used: 0).used | ||
} | ||
} |
Oops, something went wrong.