Skip to content

Commit

Permalink
Fixed nested type resolution in various cases (#1384)
Browse files Browse the repository at this point in the history
Co-authored-by: Krzysztof Zabłocki <[email protected]>
  • Loading branch information
till0xff and krzysztofzablocki authored Nov 21, 2024
1 parent b851897 commit 74275e8
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 62 deletions.
59 changes: 41 additions & 18 deletions SourceryRuntime/Sources/Common/Composer/ParserResultsComposed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ internal struct ParserResultsComposed {
associatedTypes = Self.extractAssociatedTypes(parserResult)
parsedTypes = parserResult.types

var moduleAndTypeNameCollisions: Set<String> = []

for type in parsedTypes where !type.isExtension && type.parent == nil {
if let module = type.module, type.localName == module {
moduleAndTypeNameCollisions.insert(module)
}
}

// set definedInType for all methods and variables
parsedTypes
.forEach { type in
Expand All @@ -36,16 +44,23 @@ internal struct ParserResultsComposed {
}

// map all known types to their names
parsedTypes
.filter { !$0.isExtension }
.forEach {
typeMap[$0.globalName] = $0
if let module = $0.module {
var typesByModules = modules[module, default: [:]]
typesByModules[$0.name] = $0
modules[module] = typesByModules
}

for type in parsedTypes where !type.isExtension && type.parent == nil {
let name = type.name
// If a type name has the `<module>.` prefix, and the type `<module>.<module>` is undefined, we can safely remove the `<module>.` prefix
if let module = type.module, name.hasPrefix(module), name.dropFirst(module.count).hasPrefix("."), !moduleAndTypeNameCollisions.contains(module) {
type.localName.removeFirst(module.count + 1)
}
}

for type in parsedTypes where !type.isExtension {
typeMap[type.globalName] = type
if let module = type.module {
var typesByModules = modules[module, default: [:]]
typesByModules[type.name] = type
modules[module] = typesByModules
}
}

/// Resolve typealiases
let typealiases = Array(unresolvedTypealiases.values)
Expand All @@ -66,9 +81,14 @@ internal struct ParserResultsComposed {
types = unifyTypes()
}

private func resolveExtensionOfNestedType(_ type: Type) {
mutating private func resolveExtensionOfNestedType(_ type: Type) {
var components = type.localName.components(separatedBy: ".")
let rootName = type.module ?? components.removeFirst() // Module/parent name
let rootName: String
if type.parent != nil, let module = type.module {
rootName = module
} else {
rootName = components.removeFirst()
}
if let moduleTypes = modules[rootName], let baseType = moduleTypes[components.joined(separator: ".")] ?? moduleTypes[type.localName] {
type.localName = baseType.localName
type.module = baseType.module
Expand All @@ -85,6 +105,14 @@ internal struct ParserResultsComposed {
}
}
}
// Parent extensions should always be processed before `type`, as this affects the globalName of `type`.
for parent in type.parentTypes where parent.isExtension && parent.localName.contains(".") {
let oldName = parent.globalName
resolveExtensionOfNestedType(parent)
if oldName != parent.globalName {
rewriteChildren(of: parent)
}
}
}

// if it had contained types, they might have been fully defined and so their name has to be noted in uniques
Expand All @@ -103,19 +131,14 @@ internal struct ParserResultsComposed {
.forEach { (type: Type) in
let oldName = type.globalName

let hasDotInLocalName = type.localName.contains(".") as Bool
if let _ = type.parent, hasDotInLocalName {
if type.localName.contains(".") {
resolveExtensionOfNestedType(type)
}

if let resolved = resolveGlobalName(for: oldName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases, associatedTypes: associatedTypes)?.name {
} else if let resolved = resolveGlobalName(for: oldName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases, associatedTypes: associatedTypes)?.name {
var moduleName: String = ""
if let module = type.module {
moduleName = "\(module)."
}
type.localName = resolved.replacingOccurrences(of: moduleName, with: "")
} else {
return
}

// nothing left to do
Expand Down
1 change: 0 additions & 1 deletion SourceryRuntime/Sources/Generated/JSExport.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,6 @@ extension Type: TypeAutoJSExport {}
var set: SetType? { get }
var asSource: String { get }
var description: String { get }
var hash: Int { get }
var debugDescription: String { get }
}

Expand Down
80 changes: 60 additions & 20 deletions SourcerySwift/Sources/SourceryRuntime.content.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2246,6 +2246,14 @@ internal struct ParserResultsComposed {
associatedTypes = Self.extractAssociatedTypes(parserResult)
parsedTypes = parserResult.types
var moduleAndTypeNameCollisions: Set<String> = []
for type in parsedTypes where !type.isExtension && type.parent == nil {
if let module = type.module, type.localName == module {
moduleAndTypeNameCollisions.insert(module)
}
}
// set definedInType for all methods and variables
parsedTypes
.forEach { type in
Expand All @@ -2255,16 +2263,23 @@ internal struct ParserResultsComposed {
}
// map all known types to their names
parsedTypes
.filter { !$0.isExtension }
.forEach {
typeMap[$0.globalName] = $0
if let module = $0.module {
var typesByModules = modules[module, default: [:]]
typesByModules[$0.name] = $0
modules[module] = typesByModules
}
for type in parsedTypes where !type.isExtension && type.parent == nil {
let name = type.name
// If a type name has the `<module>.` prefix, and the type `<module>.<module>` is undefined, we can safely remove the `<module>.` prefix
if let module = type.module, name.hasPrefix(module), name.dropFirst(module.count).hasPrefix("."), !moduleAndTypeNameCollisions.contains(module) {
type.localName.removeFirst(module.count + 1)
}
}
for type in parsedTypes where !type.isExtension {
typeMap[type.globalName] = type
if let module = type.module {
var typesByModules = modules[module, default: [:]]
typesByModules[type.name] = type
modules[module] = typesByModules
}
}
/// Resolve typealiases
let typealiases = Array(unresolvedTypealiases.values)
Expand All @@ -2274,15 +2289,25 @@ internal struct ParserResultsComposed {
/// Map associated types
associatedTypes.forEach {
typeMap[$0.key] = $0.value.type
if let globalName = $0.value.type?.globalName,
let type = typeMap[globalName] {
typeMap[$0.key] = type
} else {
typeMap[$0.key] = $0.value.type
}
}
types = unifyTypes()
}
private func resolveExtensionOfNestedType(_ type: Type) {
mutating private func resolveExtensionOfNestedType(_ type: Type) {
var components = type.localName.components(separatedBy: ".")
let rootName = type.module ?? components.removeFirst() // Module/parent name
let rootName: String
if type.parent != nil, let module = type.module {
rootName = module
} else {
rootName = components.removeFirst()
}
if let moduleTypes = modules[rootName], let baseType = moduleTypes[components.joined(separator: ".")] ?? moduleTypes[type.localName] {
type.localName = baseType.localName
type.module = baseType.module
Expand All @@ -2299,6 +2324,14 @@ internal struct ParserResultsComposed {
}
}
}
// Parent extensions should always be processed before `type`, as this affects the globalName of `type`.
for parent in type.parentTypes where parent.isExtension && parent.localName.contains(".") {
let oldName = parent.globalName
resolveExtensionOfNestedType(parent)
if oldName != parent.globalName {
rewriteChildren(of: parent)
}
}
}
// if it had contained types, they might have been fully defined and so their name has to be noted in uniques
Expand All @@ -2317,19 +2350,14 @@ internal struct ParserResultsComposed {
.forEach { (type: Type) in
let oldName = type.globalName
let hasDotInLocalName = type.localName.contains(".") as Bool
if let _ = type.parent, hasDotInLocalName {
if type.localName.contains(".") {
resolveExtensionOfNestedType(type)
}
if let resolved = resolveGlobalName(for: oldName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases, associatedTypes: associatedTypes)?.name {
} else if let resolved = resolveGlobalName(for: oldName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases, associatedTypes: associatedTypes)?.name {
var moduleName: String = ""
if let module = type.module {
moduleName = "\\(module)."
}
type.localName = resolved.replacingOccurrences(of: moduleName, with: "")
} else {
return
}
// nothing left to do
Expand Down Expand Up @@ -3509,7 +3537,8 @@ public final class TemplateContext: NSObject, SourceryModel, NSCoding, Diffable
"based": types.based,
"inheriting": types.inheriting,
"implementing": types.implementing,
"protocolCompositions": types.protocolCompositions
"protocolCompositions": types.protocolCompositions,
"typealiases": types.typealiases
] as [String : Any],
"functions": functions,
"type": types.typesByName,
Expand Down Expand Up @@ -3547,6 +3576,9 @@ public final class Typealias: NSObject, Typed, SourceryModel, Diffable {
/// module in which this typealias was declared
public var module: String?
/// Imports that existed in the file that contained this typealias declaration
public var imports: [Import] = []
/// typealias annotations
public var annotations: Annotations = [:]
Expand Down Expand Up @@ -3661,6 +3693,12 @@ public final class Typealias: NSObject, Typed, SourceryModel, Diffable {
}; self.typeName = typeName
self.type = aDecoder.decode(forKey: "type")
self.module = aDecoder.decode(forKey: "module")
guard let imports: [Import] = aDecoder.decode(forKey: "imports") else {
withVaList(["imports"]) { arguments in
NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments)
}
fatalError()
}; self.imports = imports
guard let annotations: Annotations = aDecoder.decode(forKey: "annotations") else {
withVaList(["annotations"]) { arguments in
NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments)
Expand Down Expand Up @@ -3689,6 +3727,7 @@ public final class Typealias: NSObject, Typed, SourceryModel, Diffable {
aCoder.encode(self.typeName, forKey: "typeName")
aCoder.encode(self.type, forKey: "type")
aCoder.encode(self.module, forKey: "module")
aCoder.encode(self.imports, forKey: "imports")
aCoder.encode(self.annotations, forKey: "annotations")
aCoder.encode(self.documentation, forKey: "documentation")
aCoder.encode(self.parent, forKey: "parent")
Expand Down Expand Up @@ -8506,6 +8545,7 @@ extension TypeName: TypeNameAutoJSExport {}
var typeName: TypeName { get }
var type: Type? { get }
var module: String? { get }
var imports: [Import] { get }
var annotations: Annotations { get }
var documentation: Documentation { get }
var parent: Type? { get }
Expand Down
Loading

0 comments on commit 74275e8

Please sign in to comment.