diff --git a/.swiftlint.yml b/.swiftlint.yml index ee8faf64e..44f81728d 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -36,3 +36,4 @@ disabled_rules: - non_optional_string_data_conversion # https://github.com/realm/SwiftLint/issues/5263#issuecomment-2115182747 - balanced_xctest_lifecycle - todo + - for_where diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dae1d235..5343428f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Unused parameter warnings are suppressed in `@available(*, unavailable)` functions. - Fix handling of Xcode projects with single and double quotes in their path and scheme names. +- `@_dynamicReplacement` members are now retained. ## 3.0.3 (2025-03-15) diff --git a/Sources/BUILD.bazel b/Sources/BUILD.bazel index 1edbc2559..df238b8c9 100644 --- a/Sources/BUILD.bazel +++ b/Sources/BUILD.bazel @@ -61,7 +61,7 @@ swift_library( "SourceGraph/Mutators/CodingKeyEnumReferenceBuilder.swift", "SourceGraph/Mutators/ComplexPropertyAccessorReferenceBuilder.swift", "SourceGraph/Mutators/DefaultConstructorReferenceBuilder.swift", - "SourceGraph/Mutators/DynamicMemberLookupReferenceBuilder.swift", + "SourceGraph/Mutators/DynamicMemberRetainer.swift", "SourceGraph/Mutators/EntryPointAttributeRetainer.swift", "SourceGraph/Mutators/EnumCaseReferenceBuilder.swift", "SourceGraph/Mutators/ExtensionReferenceBuilder.swift", diff --git a/Sources/SourceGraph/Mutators/DynamicMemberLookupReferenceBuilder.swift b/Sources/SourceGraph/Mutators/DynamicMemberRetainer.swift similarity index 63% rename from Sources/SourceGraph/Mutators/DynamicMemberLookupReferenceBuilder.swift rename to Sources/SourceGraph/Mutators/DynamicMemberRetainer.swift index d20dd3c35..2969ef6ad 100644 --- a/Sources/SourceGraph/Mutators/DynamicMemberLookupReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/DynamicMemberRetainer.swift @@ -2,7 +2,7 @@ import Configuration import Foundation import Shared -final class DynamicMemberLookupReferenceBuilder: SourceGraphMutator { +final class DynamicMemberRetainer: SourceGraphMutator { private let graph: SourceGraph required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { @@ -15,5 +15,11 @@ final class DynamicMemberLookupReferenceBuilder: SourceGraphMutator { graph.markRetained(decl) } } + + for decl in graph.declarations(ofKinds: [.functionSubscript, .varInstance, .functionMethodInstance]) { + if decl.attributes.contains("_dynamicReplacement") { + graph.markRetained(decl) + } + } } } diff --git a/Sources/SourceGraph/SourceGraphMutatorRunner.swift b/Sources/SourceGraph/SourceGraphMutatorRunner.swift index 43ca4de8a..93aaf963c 100644 --- a/Sources/SourceGraph/SourceGraphMutatorRunner.swift +++ b/Sources/SourceGraph/SourceGraphMutatorRunner.swift @@ -27,10 +27,10 @@ public final class SourceGraphMutatorRunner { ExternalTypeProtocolConformanceReferenceRemover.self, ComplexPropertyAccessorReferenceBuilder.self, EnumCaseReferenceBuilder.self, - DynamicMemberLookupReferenceBuilder.self, DefaultConstructorReferenceBuilder.self, StructImplicitInitializerReferenceBuilder.self, + DynamicMemberRetainer.self, UnusedParameterRetainer.self, AssetReferenceRetainer.self, EntryPointAttributeRetainer.self, diff --git a/Tests/Fixtures/Sources/RetentionFixtures/testRetainsDynamicReplacement.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsDynamicReplacement.swift new file mode 100644 index 000000000..e55db310a --- /dev/null +++ b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsDynamicReplacement.swift @@ -0,0 +1,26 @@ +struct FixtureStruct8 { + dynamic static func originalStaticMethod() {} + dynamic func originalMethod() {} + dynamic var originalProperty: Int { 0 } + dynamic subscript(original index: Int) -> Int { 0} +} + +extension FixtureStruct8 { + @_dynamicReplacement(for: originalMethod) + func replacementMethod() {} + + @_dynamicReplacement(for: originalProperty) + var replacementProperty: Int { 0 } + + @_dynamicReplacement(for: subscript(original:)) + subscript(replacement index: Int) -> Int { 0} +} + +public struct FixtureStruct8Retainer { + public func retain() { + let strct = FixtureStruct8() + strct.originalMethod() + _ = strct.originalProperty + _ = strct[original: 0] + } +} diff --git a/Tests/PeripheryTests/RetentionTest.swift b/Tests/PeripheryTests/RetentionTest.swift index f5020860f..400236d0d 100644 --- a/Tests/PeripheryTests/RetentionTest.swift +++ b/Tests/PeripheryTests/RetentionTest.swift @@ -1089,6 +1089,55 @@ final class RetentionTest: FixtureSourceGraphTestCase { } } + func testMainActorAnnotation() { + analyze(retainPublic: true) { + assertReferenced(.class("FixtureClass132")) { + self.assertReferenced(.functionConstructor("init(value:)")) + } + assertReferenced(.class("FixtureClass133")) + } + } + + // https://github.com/apple/swift/issues/64686 + // https://github.com/peripheryapp/periphery/issues/264 + func testSelfReferencedConstructor() { + analyze(retainPublic: true) { + assertReferenced(.struct("FixtureStruct3")) { + self.assertReferenced(.functionConstructor("init(value:)")) + } + assertReferenced(.struct("FixtureStruct4")) { + self.assertReferenced(.functionConstructor("init(value:)")) + } + assertReferenced(.struct("FixtureStruct5")) { + self.assertNotReferenced(.functionConstructor("init(value:)")) + } + } + } + + // https://github.com/apple/swift/issues/56541 + func testStaticMemberUsedAsSubscriptKey() { + analyze(retainPublic: true) { + assertReferenced(.enum("FixtureEnum128")) { + self.assertReferenced(.varStatic("someVar")) + } + } + } + + func testRetainsDynamicReplacement() { + analyze(retainPublic: true) { + assertReferenced(.struct("FixtureStruct8")) { + self.assertReferenced(.functionMethodInstance("originalMethod()")) + self.assertReferenced(.functionMethodInstance("replacementMethod()")) + + self.assertReferenced(.varInstance("originalProperty")) + self.assertReferenced(.varInstance("replacementProperty")) + + self.assertReferenced(.functionSubscript("subscript(original:)")) + self.assertReferenced(.functionSubscript("subscript(replacement:)")) + } + } + } + // MARK: - Swift Testing #if canImport(Testing) @@ -1613,40 +1662,6 @@ final class RetentionTest: FixtureSourceGraphTestCase { } } - func testMainActorAnnotation() { - analyze(retainPublic: true) { - assertReferenced(.class("FixtureClass132")) { - self.assertReferenced(.functionConstructor("init(value:)")) - } - assertReferenced(.class("FixtureClass133")) - } - } - - // https://github.com/apple/swift/issues/64686 - // https://github.com/peripheryapp/periphery/issues/264 - func testSelfReferencedConstructor() { - analyze(retainPublic: true) { - assertReferenced(.struct("FixtureStruct3")) { - self.assertReferenced(.functionConstructor("init(value:)")) - } - assertReferenced(.struct("FixtureStruct4")) { - self.assertReferenced(.functionConstructor("init(value:)")) - } - assertReferenced(.struct("FixtureStruct5")) { - self.assertNotReferenced(.functionConstructor("init(value:)")) - } - } - } - - // https://github.com/apple/swift/issues/56541 - func testStaticMemberUsedAsSubscriptKey() { - analyze(retainPublic: true) { - assertReferenced(.enum("FixtureEnum128")) { - self.assertReferenced(.varStatic("someVar")) - } - } - } - // MARK: - Known Failures // https://github.com/apple/swift/issues/56165