diff --git a/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift b/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift index b3b054558..18185665d 100644 --- a/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift +++ b/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift @@ -66,6 +66,17 @@ final class UsedDeclarationMarker: SourceGraphMutator { for ref in declaration.related { markUsed(declarationsReferenced(by: ref)) } + + // Follow type references from child property declarations. + // Property type references are associated with the property declaration + // by the indexer, not the containing type. Walking varType references + // ensures types used as property types are marked used when the parent + // type is used. + for childDecl in declaration.declarations where childDecl.kind.isVariableKind { + for ref in childDecl.references where ref.role == .varType { + markUsed(declarationsReferenced(by: ref)) + } + } } } diff --git a/Tests/Fixtures/Sources/RetentionFixtures/testRetainsPropertyTypeReferencesOfUsedDeclaration.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsPropertyTypeReferencesOfUsedDeclaration.swift new file mode 100644 index 000000000..6ecc0cd04 --- /dev/null +++ b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsPropertyTypeReferencesOfUsedDeclaration.swift @@ -0,0 +1,18 @@ +import Foundation + +struct FixtureItem222: Identifiable, Hashable { + let id: UUID + let name: String +} + +public struct FixtureViewModel222 { + struct FixtureSection222: Identifiable, Hashable { + let id: UUID + var equipment: [FixtureItem222] + } + var sections: [FixtureSection222] = [] + + public mutating func addSection() { + sections.append(FixtureSection222(id: UUID(), equipment: [])) + } +} diff --git a/Tests/Fixtures/Sources/RetentionFixtures/testRetainsStaticMethodOnExternalTypeExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsStaticMethodOnExternalTypeExtension.swift new file mode 100644 index 000000000..defb6fa0b --- /dev/null +++ b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsStaticMethodOnExternalTypeExtension.swift @@ -0,0 +1,21 @@ +import Foundation + +public class FixtureClassStaticExtMethod { + public func someMethod() { + let _ = [Int].emptyArray() + let _ = [String].constrainedFactory("hello") + NumberFormatter.customFormat() + } +} + +extension Array { + static func emptyArray() -> [Any] { [] } +} + +extension Array where Element == String { + static func constrainedFactory(_ value: String) -> [String] { [value] } +} + +extension NumberFormatter { + static func customFormat() {} +} diff --git a/Tests/PeripheryTests/RetentionTest.swift b/Tests/PeripheryTests/RetentionTest.swift index b81868b78..168542872 100644 --- a/Tests/PeripheryTests/RetentionTest.swift +++ b/Tests/PeripheryTests/RetentionTest.swift @@ -594,6 +594,23 @@ final class RetentionTest: FixtureSourceGraphTestCase { } } + func testRetainsStaticMethodOnExternalTypeExtension() { + analyze(retainPublic: true) { + assertReferenced(.class("FixtureClassStaticExtMethod")) { + self.assertReferenced(.functionMethodInstance("someMethod()")) + } + assertReferenced(.extensionStruct("Array", line: 11)) { + self.assertReferenced(.functionMethodStatic("emptyArray()")) + } + assertReferenced(.extensionStruct("Array", line: 15)) { + self.assertReferenced(.functionMethodStatic("constrainedFactory(_:)")) + } + assertReferenced(.extensionClass("NumberFormatter", line: 19)) { + self.assertReferenced(.functionMethodStatic("customFormat()")) + } + } + } + func testRetainsExtendedTypeAlias() { analyze(retainPublic: true) { assertReferenced(.typealias("Fixture214TypeAlias")) @@ -848,7 +865,14 @@ final class RetentionTest: FixtureSourceGraphTestCase { assertReferenced(.class("FixtureClass71")) { self.assertNotReferenced(.varInstance("someVar")) } - assertNotReferenced(.class("FixtureClass72")) + assertReferenced(.class("FixtureClass72")) + } + } + + func testRetainsPropertyTypeReferencesOfUsedDeclaration() { + analyze(retainPublic: true) { + assertReferenced(.struct("FixtureViewModel222")) + assertReferenced(.struct("FixtureItem222")) } }