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

Fixed nested type resolution #1384

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
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
Loading