Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Substitute underlying type from typealias #1292

Merged
merged 4 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions SourceryRuntime/Sources/Composer/ParserResultsComposed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -513,11 +513,26 @@ internal struct ParserResultsComposed {
var resolvedIdentifier = finalLookup.generic?.name ?? finalLookup.unwrappedTypeName
for alias in resolvedTypealiases {
/// iteratively replace all typealiases from the resolvedIdentifier to get to the actual type name requested
if resolvedIdentifier.contains(alias.value.name) {
resolvedIdentifier.replace(alias.value.name, with: alias.value.typeName.name)
if resolvedIdentifier.contains(alias.value.name), let range = resolvedIdentifier.range(of: alias.value.name) {
resolvedIdentifier = resolvedIdentifier.replacingCharacters(in: range, with: alias.value.typeName.name)
}
}
// should we cache resolved typenames?
if unique[resolvedIdentifier] == nil {
// peek into typealiases, if any of them contain the same typeName
// this is done after the initial attempt in order to prioritise local (recognized) types first
// before even trying to substitute the requested type with any typealias
for alias in resolvedTypealiases {
/// iteratively replace all typealiases from the resolvedIdentifier to get to the actual type name requested,
/// ignoring namespacing
if resolvedIdentifier == alias.value.aliasName {
resolvedIdentifier = alias.value.typeName.name
typeName.actualTypeName = alias.value.typeName
break
}
}
}

return unique[resolvedIdentifier]
}

Expand Down
83 changes: 80 additions & 3 deletions SourcerySwift/Sources/SourceryRuntime.content.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4869,7 +4869,7 @@ internal struct ParserResultsComposed {
return modules[moduleName]?[typeName]
}

func resolveType(typeName: TypeName, containingType: Type?) -> Type? {
func resolveType(typeName: TypeName, containingType: Type?, method: Method? = nil) -> Type? {
let resolveTypeWithName = { (typeName: TypeName) -> Type? in
return self.resolveType(typeName: typeName, containingType: containingType)
}
Expand Down Expand Up @@ -4910,6 +4910,7 @@ internal struct ParserResultsComposed {
array: lookupName.array,
dictionary: lookupName.dictionary,
closure: lookupName.closure,
set: lookupName.set,
generic: lookupName.generic
)
}
Expand All @@ -4933,6 +4934,7 @@ internal struct ParserResultsComposed {
array: array,
dictionary: lookupName.dictionary,
closure: lookupName.closure,
set: lookupName.set,
generic: typeName.generic
)
}
Expand Down Expand Up @@ -4960,6 +4962,7 @@ internal struct ParserResultsComposed {
array: lookupName.array,
dictionary: dictionary,
closure: lookupName.closure,
set: lookupName.set,
generic: dictionary.asGeneric
)
}
Expand All @@ -4985,6 +4988,7 @@ internal struct ParserResultsComposed {
array: lookupName.array,
dictionary: lookupName.dictionary,
closure: closure,
set: lookupName.set,
generic: lookupName.generic
)
}
Expand Down Expand Up @@ -5020,6 +5024,7 @@ internal struct ParserResultsComposed {
array: lookupName.array, // TODO: asArray
dictionary: lookupName.dictionary, // TODO: asDictionary
closure: lookupName.closure,
set: lookupName.set,
generic: generic
)
}
Expand All @@ -5029,10 +5034,79 @@ internal struct ParserResultsComposed {
typeName.actualTypeName = aliasedName
}

let finalLookup = typeName.actualTypeName ?? typeName
let resolvedIdentifier = finalLookup.generic?.name ?? finalLookup.unwrappedTypeName
let hasGenericRequirements = containingType?.genericRequirements.isEmpty == false
|| (method != nil && method?.genericRequirements.isEmpty == false)

if hasGenericRequirements {
// we should consider if we are looking up return type of a method with generic constraints
// where `typeName` passed would include `... where ...` suffix
let typeNameForLookup = typeName.name.split(separator: " ").first!
let genericRequirements: [GenericRequirement]
if let requirements = containingType?.genericRequirements, !requirements.isEmpty {
genericRequirements = requirements
} else {
genericRequirements = method?.genericRequirements ?? []
}
let relevantRequirements = genericRequirements.filter {
// matched type against a generic requirement name
// thus type should be replaced with a protocol composition
$0.leftType.name == typeNameForLookup
}
if relevantRequirements.count > 1 {
// compose protocols into `ProtocolComposition` and generate TypeName
var implements: [String: Type] = [:]
relevantRequirements.forEach {
implements[$0.rightType.typeName.name] = $0.rightType.type
}
let composedProtocols = ProtocolComposition(
inheritedTypes: relevantRequirements.map { $0.rightType.typeName.unwrappedTypeName },
isGeneric: true,
composedTypes: relevantRequirements.compactMap { $0.rightType.type },
implements: implements
)
typeName.actualTypeName = TypeName(name: "(\\(relevantRequirements.map { $0.rightType.typeName.unwrappedTypeName }.joined(separator: " & ")))", isProtocolComposition: true)
return composedProtocols
} else if let protocolRequirement = relevantRequirements.first {
// create TypeName off a single generic's protocol requirement
typeName.actualTypeName = TypeName(name: "(\\(protocolRequirement.rightType.typeName))")
return protocolRequirement.rightType.type
}
}

// try to peek into typealias, maybe part of the typeName is a composed identifier from a type and typealias
// i.e.
// enum Module {
// typealias ID = MyView
// }
// class MyView {
// class ID: String {}
// }
//
// let variable: Module.ID.ID // should be resolved as MyView.ID type
let finalLookup = typeName.actualTypeName ?? typeName
var resolvedIdentifier = finalLookup.generic?.name ?? finalLookup.unwrappedTypeName
for alias in resolvedTypealiases {
/// iteratively replace all typealiases from the resolvedIdentifier to get to the actual type name requested
if resolvedIdentifier.contains(alias.value.name), let range = resolvedIdentifier.range(of: alias.value.name) {
resolvedIdentifier = resolvedIdentifier.replacingCharacters(in: range, with: alias.value.typeName.name)
}
}
// should we cache resolved typenames?
if unique[resolvedIdentifier] == nil {
// peek into typealiases, if any of them contain the same typeName
// this is done after the initial attempt in order to prioritise local (recognized) types first
// before even trying to substitute the requested type with any typealias
for alias in resolvedTypealiases {
/// iteratively replace all typealiases from the resolvedIdentifier to get to the actual type name requested,
/// ignoring namespacing
if resolvedIdentifier == alias.value.aliasName {
resolvedIdentifier = alias.value.typeName.name
typeName.actualTypeName = alias.value.typeName
break
}
}
}

return unique[resolvedIdentifier]
}

Expand All @@ -5057,6 +5131,8 @@ internal struct ParserResultsComposed {
dictionary?.name = aliased.name
let array = typeName.array.map { ArrayType(name: $0.name, elementTypeName: $0.elementTypeName, elementType: $0.elementType) }
array?.name = aliased.name
let set = typeName.set.map { SetType(name: $0.name, elementTypeName: $0.elementTypeName, elementType: $0.elementType) }
set?.name = aliased.name

return TypeName(name: aliased.name,
isOptional: typeName.isOptional,
Expand All @@ -5065,6 +5141,7 @@ internal struct ParserResultsComposed {
array: aliased.typealias?.typeName.array ?? array,
dictionary: aliased.typealias?.typeName.dictionary ?? dictionary,
closure: aliased.typealias?.typeName.closure ?? typeName.closure,
set: aliased.typealias?.typeName.set ?? set,
generic: aliased.typealias?.typeName.generic ?? generic
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4284,7 +4284,7 @@ internal struct ParserResultsComposed {
return modules[moduleName]?[typeName]
}

func resolveType(typeName: TypeName, containingType: Type?) -> Type? {
func resolveType(typeName: TypeName, containingType: Type?, method: Method? = nil) -> Type? {
let resolveTypeWithName = { (typeName: TypeName) -> Type? in
return self.resolveType(typeName: typeName, containingType: containingType)
}
Expand Down Expand Up @@ -4325,6 +4325,7 @@ internal struct ParserResultsComposed {
array: lookupName.array,
dictionary: lookupName.dictionary,
closure: lookupName.closure,
set: lookupName.set,
generic: lookupName.generic
)
}
Expand All @@ -4348,6 +4349,7 @@ internal struct ParserResultsComposed {
array: array,
dictionary: lookupName.dictionary,
closure: lookupName.closure,
set: lookupName.set,
generic: typeName.generic
)
}
Expand Down Expand Up @@ -4375,6 +4377,7 @@ internal struct ParserResultsComposed {
array: lookupName.array,
dictionary: dictionary,
closure: lookupName.closure,
set: lookupName.set,
generic: dictionary.asGeneric
)
}
Expand All @@ -4400,6 +4403,7 @@ internal struct ParserResultsComposed {
array: lookupName.array,
dictionary: lookupName.dictionary,
closure: closure,
set: lookupName.set,
generic: lookupName.generic
)
}
Expand Down Expand Up @@ -4435,6 +4439,7 @@ internal struct ParserResultsComposed {
array: lookupName.array, // TODO: asArray
dictionary: lookupName.dictionary, // TODO: asDictionary
closure: lookupName.closure,
set: lookupName.set,
generic: generic
)
}
Expand All @@ -4444,10 +4449,79 @@ internal struct ParserResultsComposed {
typeName.actualTypeName = aliasedName
}

let finalLookup = typeName.actualTypeName ?? typeName
let resolvedIdentifier = finalLookup.generic?.name ?? finalLookup.unwrappedTypeName
let hasGenericRequirements = containingType?.genericRequirements.isEmpty == false
|| (method != nil && method?.genericRequirements.isEmpty == false)

if hasGenericRequirements {
// we should consider if we are looking up return type of a method with generic constraints
// where `typeName` passed would include `... where ...` suffix
let typeNameForLookup = typeName.name.split(separator: " ").first!
let genericRequirements: [GenericRequirement]
if let requirements = containingType?.genericRequirements, !requirements.isEmpty {
genericRequirements = requirements
} else {
genericRequirements = method?.genericRequirements ?? []
}
let relevantRequirements = genericRequirements.filter {
// matched type against a generic requirement name
// thus type should be replaced with a protocol composition
$0.leftType.name == typeNameForLookup
}
if relevantRequirements.count > 1 {
// compose protocols into `ProtocolComposition` and generate TypeName
var implements: [String: Type] = [:]
relevantRequirements.forEach {
implements[$0.rightType.typeName.name] = $0.rightType.type
}
let composedProtocols = ProtocolComposition(
inheritedTypes: relevantRequirements.map { $0.rightType.typeName.unwrappedTypeName },
isGeneric: true,
composedTypes: relevantRequirements.compactMap { $0.rightType.type },
implements: implements
)
typeName.actualTypeName = TypeName(name: "(\\(relevantRequirements.map { $0.rightType.typeName.unwrappedTypeName }.joined(separator: " & ")))", isProtocolComposition: true)
return composedProtocols
} else if let protocolRequirement = relevantRequirements.first {
// create TypeName off a single generic's protocol requirement
typeName.actualTypeName = TypeName(name: "(\\(protocolRequirement.rightType.typeName))")
return protocolRequirement.rightType.type
}
}

// try to peek into typealias, maybe part of the typeName is a composed identifier from a type and typealias
// i.e.
// enum Module {
// typealias ID = MyView
// }
// class MyView {
// class ID: String {}
// }
//
// let variable: Module.ID.ID // should be resolved as MyView.ID type
let finalLookup = typeName.actualTypeName ?? typeName
var resolvedIdentifier = finalLookup.generic?.name ?? finalLookup.unwrappedTypeName
for alias in resolvedTypealiases {
/// iteratively replace all typealiases from the resolvedIdentifier to get to the actual type name requested
if resolvedIdentifier.contains(alias.value.name), let range = resolvedIdentifier.range(of: alias.value.name) {
resolvedIdentifier = resolvedIdentifier.replacingCharacters(in: range, with: alias.value.typeName.name)
}
}
// should we cache resolved typenames?
if unique[resolvedIdentifier] == nil {
// peek into typealiases, if any of them contain the same typeName
// this is done after the initial attempt in order to prioritise local (recognized) types first
// before even trying to substitute the requested type with any typealias
for alias in resolvedTypealiases {
/// iteratively replace all typealiases from the resolvedIdentifier to get to the actual type name requested,
/// ignoring namespacing
if resolvedIdentifier == alias.value.aliasName {
resolvedIdentifier = alias.value.typeName.name
typeName.actualTypeName = alias.value.typeName
break
}
}
}

return unique[resolvedIdentifier]
}

Expand All @@ -4472,6 +4546,8 @@ internal struct ParserResultsComposed {
dictionary?.name = aliased.name
let array = typeName.array.map { ArrayType(name: $0.name, elementTypeName: $0.elementTypeName, elementType: $0.elementType) }
array?.name = aliased.name
let set = typeName.set.map { SetType(name: $0.name, elementTypeName: $0.elementTypeName, elementType: $0.elementType) }
set?.name = aliased.name

return TypeName(name: aliased.name,
isOptional: typeName.isOptional,
Expand All @@ -4480,6 +4556,7 @@ internal struct ParserResultsComposed {
array: aliased.typealias?.typeName.array ?? array,
dictionary: aliased.typealias?.typeName.dictionary ?? dictionary,
closure: aliased.typealias?.typeName.closure ?? typeName.closure,
set: aliased.typealias?.typeName.set ?? set,
generic: aliased.typealias?.typeName.generic ?? generic
)
}
Expand Down
Loading