From 7153768c655c5e2435323cd08f01ed8b2d13f765 Mon Sep 17 00:00:00 2001 From: Ruslan A Date: Wed, 19 Jun 2024 22:50:18 +0400 Subject: [PATCH] Fix associatedtype generics (#1345) * Preliminary associatedtype support * Implemented associatedtype support with generic requirements * Fixed failing test * Squashed commit of the following: commit 9d01e6f99aa8af23581451267de3341107e41f8b Author: Ruslan A Date: Fri Jun 14 20:06:41 2024 +0400 Improved concurrency support in SwiftTemplate caching (#1344) * Removed test code * Removed comment * Updated Linux classes * update internal boilerplate code. * Updated generated code * Removed warnings * Updated expected file * Updated expected file * Adjusted protocol type for Linux * Removed protocol composition due to Swift compiler crash under Linux --- .../SwiftSyntax/SyntaxTreeCollector.swift | 23 ++- .../Sources/Common/AST/Actor.swift | 12 +- .../Sources/Common/AST/Class.swift | 11 +- .../Sources/Common/AST/Protocol.swift | 16 +- .../Common/AST/ProtocolComposition.swift | 11 +- .../Sources/Common/AST/Struct.swift | 11 +- .../Sources/Common/Composer/Composer.swift | 12 +- .../Composer/ParserResultsComposed.swift | 54 +++++-- .../Sources/Linux/AST/Enum_Linux.swift | 7 +- .../Sources/Linux/AST/Method_Linux.swift | 22 +-- .../Sources/Linux/AST/Protocol_Linux.swift | 147 +++++++++++++++++ .../Sources/Linux/AST/Type_Linux.swift | 21 ++- SourceryRuntime/Sources/macOS/AST/Enum.swift | 8 +- .../Sources/macOS/AST/Method.swift | 2 +- SourceryRuntime/Sources/macOS/AST/Type.swift | 18 ++- SourceryStencil/Sources/StencilTemplate.swift | 45 +++++- .../SourceryRuntime.content.generated.swift | 148 +++++++++++++---- ...rceryRuntime_Linux.content.generated.swift | 150 +++++++++++++----- SourceryTests/Output/DryOutputSpec.swift | 9 -- SourceryTests/Parsing/ComposerSpec.swift | 4 +- .../FileParser_AssociatedTypeSpec.swift | 4 +- .../FileParser_AttributesModifierSpec.swift | 2 +- Templates/Templates/AutoMockable.stencil | 44 ++++- Templates/Tests/Context/AutoMockable.swift | 22 +++ .../Tests/Context_Linux/AutoMockable.swift | 22 +++ .../Tests/Expected/AutoMockable.expected | 106 ++++++++++++- .../Generated/AutoMockable.generated.swift | 106 ++++++++++++- 27 files changed, 889 insertions(+), 148 deletions(-) create mode 100644 SourceryRuntime/Sources/Linux/AST/Protocol_Linux.swift diff --git a/SourceryFramework/Sources/Parsing/SwiftSyntax/SyntaxTreeCollector.swift b/SourceryFramework/Sources/Parsing/SwiftSyntax/SyntaxTreeCollector.swift index 2d2091c83..eaaa3e660 100644 --- a/SourceryFramework/Sources/Parsing/SwiftSyntax/SyntaxTreeCollector.swift +++ b/SourceryFramework/Sources/Parsing/SwiftSyntax/SyntaxTreeCollector.swift @@ -246,16 +246,37 @@ class SyntaxTreeCollector: SyntaxVisitor { let name = node.name.text.trimmed var typeName: TypeName? var type: Type? + if let possibleTypeName = node.inheritanceClause?.inheritedTypes.description.trimmed { + var genericRequirements: [GenericRequirement] = [] + if let genericWhereClause = node.genericWhereClause { + genericRequirements = genericWhereClause.requirements.compactMap { requirement in + if let sameType = requirement.requirement.as(SameTypeRequirementSyntax.self) { + return GenericRequirement(sameType) + } else if let conformanceType = requirement.requirement.as(ConformanceRequirementSyntax.self) { + return GenericRequirement(conformanceType) + } + return nil + } + } + if let composition = processPossibleProtocolComposition(for: possibleTypeName, localName: "") { + type = composition + } else { + type = Protocol(name: possibleTypeName, genericRequirements: genericRequirements) + } + typeName = TypeName(possibleTypeName) + } else if let possibleTypeName = (node.initializer?.value as? TypeSyntax)?.description.trimmed { type = processPossibleProtocolComposition(for: possibleTypeName, localName: "") typeName = TypeName(possibleTypeName) + } else { + type = Type(name: "Any") + typeName = TypeName(name: "Any", actualTypeName: TypeName(name: "Any")) } sourceryProtocol.associatedTypes[name] = AssociatedType(name: name, typeName: typeName, type: type) return .skipChildren } - public override func visit(_ node: OperatorDeclSyntax) -> SyntaxVisitorContinueKind { return .skipChildren } diff --git a/SourceryRuntime/Sources/Common/AST/Actor.swift b/SourceryRuntime/Sources/Common/AST/Actor.swift index accc9d390..44a55bede 100644 --- a/SourceryRuntime/Sources/Common/AST/Actor.swift +++ b/SourceryRuntime/Sources/Common/AST/Actor.swift @@ -6,8 +6,12 @@ import Foundation @objc(SwiftActor) @objcMembers #endif public final class Actor: Type { + + // sourcery: skipJSExport + public class var kind: String { return "actor" } + /// Returns "actor" - public override var kind: String { return "actor" } + public override var kind: String { Self.kind } /// Whether type is final public var isFinal: Bool { @@ -36,7 +40,8 @@ public final class Actor: Type { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Actor.kind) { super.init( name: name, parent: parent, @@ -54,7 +59,8 @@ public final class Actor: Type { annotations: annotations, documentation: documentation, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } diff --git a/SourceryRuntime/Sources/Common/AST/Class.swift b/SourceryRuntime/Sources/Common/AST/Class.swift index 10d5acce0..7e5dd071f 100644 --- a/SourceryRuntime/Sources/Common/AST/Class.swift +++ b/SourceryRuntime/Sources/Common/AST/Class.swift @@ -5,8 +5,11 @@ import Foundation @objc(SwiftClass) @objcMembers #endif public final class Class: Type { + // sourcery: skipJSExport + public class var kind: String { return "class" } + /// Returns "class" - public override var kind: String { return "class" } + public override var kind: String { Self.kind } /// Whether type is final public var isFinal: Bool { @@ -30,7 +33,8 @@ public final class Class: Type { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Class.kind) { super.init( name: name, parent: parent, @@ -48,7 +52,8 @@ public final class Class: Type { annotations: annotations, documentation: documentation, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } diff --git a/SourceryRuntime/Sources/Common/AST/Protocol.swift b/SourceryRuntime/Sources/Common/AST/Protocol.swift index 7b2354f57..71a36e741 100644 --- a/SourceryRuntime/Sources/Common/AST/Protocol.swift +++ b/SourceryRuntime/Sources/Common/AST/Protocol.swift @@ -8,17 +8,20 @@ import Foundation +#if canImport(ObjectiveC) + /// :nodoc: public typealias SourceryProtocol = Protocol /// Describes Swift protocol -#if canImport(ObjectiveC) @objcMembers -#endif public final class Protocol: Type { + // sourcery: skipJSExport + public class var kind: String { return "protocol" } + /// Returns "protocol" - public override var kind: String { return "protocol" } + public override var kind: String { Self.kind } /// list of all declared associated types with their names as keys public var associatedTypes: [String: AssociatedType] { @@ -52,7 +55,8 @@ public final class Protocol: Type { modifiers: [SourceryModifier] = [], annotations: [String: NSObject] = [:], documentation: [String] = [], - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Protocol.kind) { self.associatedTypes = associatedTypes super.init( name: name, @@ -71,7 +75,8 @@ public final class Protocol: Type { annotations: annotations, documentation: documentation, isGeneric: !associatedTypes.isEmpty || !genericRequirements.isEmpty, - implements: implements + implements: implements, + kind: kind ) } @@ -132,3 +137,4 @@ public final class Protocol: Type { } // sourcery:end } +#endif \ No newline at end of file diff --git a/SourceryRuntime/Sources/Common/AST/ProtocolComposition.swift b/SourceryRuntime/Sources/Common/AST/ProtocolComposition.swift index a3fea9cc1..c9ab5351a 100644 --- a/SourceryRuntime/Sources/Common/AST/ProtocolComposition.swift +++ b/SourceryRuntime/Sources/Common/AST/ProtocolComposition.swift @@ -9,8 +9,11 @@ import Foundation #endif public final class ProtocolComposition: Type { + // sourcery: skipJSExport + public class var kind: String { return "protocolComposition" } + /// Returns "protocolComposition" - public override var kind: String { return "protocolComposition" } + public override var kind: String { Self.kind } /// The names of the types composed to form this composition public let composedTypeNames: [TypeName] @@ -35,7 +38,8 @@ public final class ProtocolComposition: Type { isGeneric: Bool = false, composedTypeNames: [TypeName] = [], composedTypes: [Type]? = nil, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = ProtocolComposition.kind) { self.composedTypeNames = composedTypeNames self.composedTypes = composedTypes super.init( @@ -51,7 +55,8 @@ public final class ProtocolComposition: Type { typealiases: typealiases, annotations: annotations, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } diff --git a/SourceryRuntime/Sources/Common/AST/Struct.swift b/SourceryRuntime/Sources/Common/AST/Struct.swift index 4560abd0e..2e2f6d03a 100644 --- a/SourceryRuntime/Sources/Common/AST/Struct.swift +++ b/SourceryRuntime/Sources/Common/AST/Struct.swift @@ -15,8 +15,11 @@ import Foundation #endif public final class Struct: Type { + // sourcery: skipJSExport + public class var kind: String { return "struct" } + /// Returns "struct" - public override var kind: String { return "struct" } + public override var kind: String { Self.kind } /// :nodoc: public override init(name: String = "", @@ -35,7 +38,8 @@ public final class Struct: Type { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Struct.kind) { super.init( name: name, parent: parent, @@ -53,7 +57,8 @@ public final class Struct: Type { annotations: annotations, documentation: documentation, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } diff --git a/SourceryRuntime/Sources/Common/Composer/Composer.swift b/SourceryRuntime/Sources/Common/Composer/Composer.swift index 5508fc312..9cafbace4 100644 --- a/SourceryRuntime/Sources/Common/Composer/Composer.swift +++ b/SourceryRuntime/Sources/Common/Composer/Composer.swift @@ -25,11 +25,11 @@ public enum Composer { let composed = ParserResultsComposed(parserResult: parserResult) let resolveType = { (typeName: TypeName, containingType: Type?) -> Type? in - return composed.resolveType(typeName: typeName, containingType: containingType) + composed.resolveType(typeName: typeName, containingType: containingType) } let methodResolveType = { (typeName: TypeName, containingType: Type?, method: Method) -> Type? in - return composed.resolveType(typeName: typeName, containingType: containingType, method: method) + composed.resolveType(typeName: typeName, containingType: containingType, method: method) } let processType = { (type: Type) in @@ -52,7 +52,7 @@ public enum Composer { } if let sourceryProtocol = type as? SourceryProtocol { - resolveProtocolTypes(sourceryProtocol, resolve: resolveType) + resolveAssociatedTypes(sourceryProtocol, resolve: resolveType) } } @@ -180,11 +180,13 @@ public enum Composer { protocolComposition.composedTypes = composedTypes } - private static func resolveProtocolTypes(_ sourceryProtocol: SourceryProtocol, resolve: TypeResolver) { + private static func resolveAssociatedTypes(_ sourceryProtocol: SourceryProtocol, resolve: TypeResolver) { sourceryProtocol.associatedTypes.forEach { (_, value) in guard let typeName = value.typeName, let type = resolve(typeName, sourceryProtocol) - else { return } + else { + return + } value.type = type } diff --git a/SourceryRuntime/Sources/Common/Composer/ParserResultsComposed.swift b/SourceryRuntime/Sources/Common/Composer/ParserResultsComposed.swift index e0c029b62..ba5e95f3e 100644 --- a/SourceryRuntime/Sources/Common/Composer/ParserResultsComposed.swift +++ b/SourceryRuntime/Sources/Common/Composer/ParserResultsComposed.swift @@ -13,6 +13,7 @@ internal struct ParserResultsComposed { let parsedTypes: [Type] let functions: [SourceryMethod] let resolvedTypealiases: [String: Typealias] + let associatedTypes: [String: AssociatedType] let unresolvedTypealiases: [String: Typealias] init(parserResult: FileParserResult) { @@ -23,6 +24,7 @@ internal struct ParserResultsComposed { let aliases = Self.typealiases(parserResult) resolvedTypealiases = aliases.resolved unresolvedTypealiases = aliases.unresolved + associatedTypes = Self.extractAssociatedTypes(parserResult) parsedTypes = parserResult.types // set definedInType for all methods and variables @@ -51,6 +53,11 @@ internal struct ParserResultsComposed { alias.type = resolveType(typeName: alias.typeName, containingType: alias.parent) } + /// Map associated types + associatedTypes.forEach { + typeMap[$0.key] = $0.value.type + } + types = unifyTypes() } @@ -96,7 +103,7 @@ internal struct ParserResultsComposed { resolveExtensionOfNestedType(type) } - if let resolved = resolveGlobalName(for: oldName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases)?.name { + 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)." @@ -116,7 +123,7 @@ internal struct ParserResultsComposed { // extend all types with their extensions parsedTypes.forEach { type in let inheritedTypes: [[String]] = type.inheritedTypes.compactMap { inheritedName in - if let resolvedGlobalName = resolveGlobalName(for: inheritedName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases)?.name { + if let resolvedGlobalName = resolveGlobalName(for: inheritedName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases, associatedTypes: associatedTypes)?.name { return [resolvedGlobalName] } if let baseType = Composer.findBaseType(for: type, name: inheritedName, typesByName: typeMap) { @@ -175,6 +182,14 @@ internal struct ParserResultsComposed { }) } + // extract associated types from all types and add them to types + private static func extractAssociatedTypes(_ parserResult: FileParserResult) -> [String: AssociatedType] { + parserResult.types + .compactMap { $0 as? SourceryProtocol } + .map { $0.associatedTypes } + .flatMap { $0 }.reduce(into: [:]) { $0[$1.key] = $1.value } + } + /// returns typealiases map to their full names, with `resolved` removing intermediate /// typealises and `unresolved` including typealiases that reference other typealiases. private static func typealiases(_ parserResult: FileParserResult) -> (resolved: [String: Typealias], unresolved: [String: Typealias]) { @@ -207,11 +222,14 @@ internal struct ParserResultsComposed { } /// Resolves type identifier for name - func resolveGlobalName(for type: String, - containingType: Type? = nil, - unique: [String: Type]? = nil, - modules: [String: [String: Type]], - typealiases: [String: Typealias]) -> (name: String, typealias: Typealias?)? { + func resolveGlobalName( + for type: String, + containingType: Type? = nil, + unique: [String: Type]? = nil, + modules: [String: [String: Type]], + typealiases: [String: Typealias], + associatedTypes: [String: AssociatedType] + ) -> (name: String, typealias: Typealias?)? { // if the type exists for this name and isn't an extension just return it's name // if it's extension we need to check if there aren't other options TODO: verify if let realType = unique?[type], realType.isExtension == false { @@ -222,6 +240,13 @@ internal struct ParserResultsComposed { return (name: alias.type?.globalName ?? alias.typeName.name, typealias: alias) } + if let associatedType = associatedTypes[type], + let actualType = associatedType.type + { + let typeName = associatedType.typeName ?? TypeName(name: actualType.name) + return (name: actualType.globalName, typealias: Typealias(aliasName: type, typeName: typeName)) + } + if let containingType = containingType { if type == "Self" { return (name: containingType.globalName, typealias: nil) @@ -231,7 +256,7 @@ internal struct ParserResultsComposed { while currentContainer != nil, let parentName = currentContainer?.globalName { /// TODO: no parent for sure? /// manually walk the containment tree - if let name = resolveGlobalName(for: "\(parentName).\(type)", containingType: nil, unique: unique, modules: modules, typealiases: typealiases) { + if let name = resolveGlobalName(for: "\(parentName).\(type)", containingType: nil, unique: unique, modules: modules, typealiases: typealiases, associatedTypes: associatedTypes) { return name } @@ -565,20 +590,27 @@ internal struct ParserResultsComposed { } } - return unique[resolvedIdentifier] + if let associatedType = associatedTypes[resolvedIdentifier] { + return associatedType.type + } + + return unique[resolvedIdentifier] ?? unique[typeName.name] } private func actualTypeName(for typeName: TypeName, - containingType: Type? = nil) -> TypeName? { + containingType: Type? = nil) -> TypeName? { let unique = typeMap let typealiases = resolvedTypealiases + let associatedTypes = associatedTypes var unwrapped = typeName.unwrappedTypeName if let generic = typeName.generic { unwrapped = generic.name + } else if let type = associatedTypes[unwrapped] { + unwrapped = type.name } - guard let aliased = resolveGlobalName(for: unwrapped, containingType: containingType, unique: unique, modules: modules, typealiases: typealiases) else { + guard let aliased = resolveGlobalName(for: unwrapped, containingType: containingType, unique: unique, modules: modules, typealiases: typealiases, associatedTypes: associatedTypes) else { return nil } diff --git a/SourceryRuntime/Sources/Linux/AST/Enum_Linux.swift b/SourceryRuntime/Sources/Linux/AST/Enum_Linux.swift index 5bd2f6e2d..dc35eabf0 100644 --- a/SourceryRuntime/Sources/Linux/AST/Enum_Linux.swift +++ b/SourceryRuntime/Sources/Linux/AST/Enum_Linux.swift @@ -19,9 +19,12 @@ public final class Enum: Type { } } + // sourcery: skipJSExport + public class var kind: String { return "enum" } + // sourcery: skipDescription /// Returns "enum" - public override var kind: String { return "enum" } + public override var kind: String { Self.kind } /// Enum cases public var cases: [EnumCase] @@ -93,7 +96,7 @@ public final class Enum: Type { self.rawTypeName = rawTypeName self.hasRawType = rawTypeName != nil || !inheritedTypes.isEmpty - super.init(name: name, parent: parent, accessLevel: accessLevel, isExtension: isExtension, variables: variables, methods: methods, inheritedTypes: inheritedTypes, containedTypes: containedTypes, typealiases: typealiases, attributes: attributes, modifiers: modifiers, annotations: annotations, documentation: documentation, isGeneric: isGeneric) + super.init(name: name, parent: parent, accessLevel: accessLevel, isExtension: isExtension, variables: variables, methods: methods, inheritedTypes: inheritedTypes, containedTypes: containedTypes, typealiases: typealiases, attributes: attributes, modifiers: modifiers, annotations: annotations, documentation: documentation, isGeneric: isGeneric, kind: Self.kind) if let rawTypeName = rawTypeName?.name, let index = self.inheritedTypes.firstIndex(of: rawTypeName) { self.inheritedTypes.remove(at: index) diff --git a/SourceryRuntime/Sources/Linux/AST/Method_Linux.swift b/SourceryRuntime/Sources/Linux/AST/Method_Linux.swift index 3e232cc14..f84b822c5 100644 --- a/SourceryRuntime/Sources/Linux/AST/Method_Linux.swift +++ b/SourceryRuntime/Sources/Linux/AST/Method_Linux.swift @@ -374,25 +374,25 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin /// :nodoc: required public init?(coder aDecoder: NSCoder) { - guard let name: String = aDecoder.decode(forKey: "name") else { + guard let name: String = aDecoder.decode(forKey: "name") else { withVaList(["name"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } fatalError() }; self.name = name - guard let selectorName: String = aDecoder.decode(forKey: "selectorName") else { + guard let selectorName: String = aDecoder.decode(forKey: "selectorName") else { withVaList(["selectorName"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } fatalError() }; self.selectorName = selectorName - guard let parameters: [MethodParameter] = aDecoder.decode(forKey: "parameters") else { + guard let parameters: [MethodParameter] = aDecoder.decode(forKey: "parameters") else { withVaList(["parameters"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } fatalError() }; self.parameters = parameters - guard let returnTypeName: TypeName = aDecoder.decode(forKey: "returnTypeName") else { + guard let returnTypeName: TypeName = aDecoder.decode(forKey: "returnTypeName") else { withVaList(["returnTypeName"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } @@ -402,7 +402,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin self.isAsync = aDecoder.decode(forKey: "isAsync") self.`throws` = aDecoder.decode(forKey: "`throws`") self.`rethrows` = aDecoder.decode(forKey: "`rethrows`") - guard let accessLevel: String = aDecoder.decode(forKey: "accessLevel") else { + guard let accessLevel: String = aDecoder.decode(forKey: "accessLevel") else { withVaList(["accessLevel"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } @@ -411,13 +411,13 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin self.isStatic = aDecoder.decode(forKey: "isStatic") self.isClass = aDecoder.decode(forKey: "isClass") self.isFailableInitializer = aDecoder.decode(forKey: "isFailableInitializer") - guard let annotations: Annotations = aDecoder.decode(forKey: "annotations") else { + guard let annotations: Annotations = aDecoder.decode(forKey: "annotations") else { withVaList(["annotations"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } fatalError() }; self.annotations = annotations - guard let documentation: Documentation = aDecoder.decode(forKey: "documentation") else { + guard let documentation: Documentation = aDecoder.decode(forKey: "documentation") else { withVaList(["documentation"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } @@ -425,25 +425,25 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin }; self.documentation = documentation self.definedInTypeName = aDecoder.decode(forKey: "definedInTypeName") self.definedInType = aDecoder.decode(forKey: "definedInType") - guard let attributes: AttributeList = aDecoder.decode(forKey: "attributes") else { + guard let attributes: AttributeList = aDecoder.decode(forKey: "attributes") else { withVaList(["attributes"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } fatalError() }; self.attributes = attributes - guard let modifiers: [SourceryModifier] = aDecoder.decode(forKey: "modifiers") else { + guard let modifiers: [SourceryModifier] = aDecoder.decode(forKey: "modifiers") else { withVaList(["modifiers"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } fatalError() }; self.modifiers = modifiers - guard let genericRequirements: [GenericRequirement] = aDecoder.decode(forKey: "genericRequirements") else { + guard let genericRequirements: [GenericRequirement] = aDecoder.decode(forKey: "genericRequirements") else { withVaList(["genericRequirements"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } fatalError() }; self.genericRequirements = genericRequirements - guard let genericParameters: [GenericParameter] = aDecoder.decode(forKey: "genericParameters") else { + guard let genericParameters: [GenericParameter] = aDecoder.decode(forKey: "genericParameters") else { withVaList(["genericParameters"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } diff --git a/SourceryRuntime/Sources/Linux/AST/Protocol_Linux.swift b/SourceryRuntime/Sources/Linux/AST/Protocol_Linux.swift new file mode 100644 index 000000000..5de92035f --- /dev/null +++ b/SourceryRuntime/Sources/Linux/AST/Protocol_Linux.swift @@ -0,0 +1,147 @@ +// +// Protocol.swift +// Sourcery +// +// Created by Krzysztof Zablocki on 09/12/2016. +// Copyright © 2016 Pixle. All rights reserved. +// + +import Foundation + +#if !canImport(ObjectiveC) + +/// :nodoc: +public typealias SourceryProtocol = Protocol + +/// Describes Swift protocol +public final class Protocol: Type { + public override subscript(dynamicMember member: String) -> Any? { + switch member { + case "associatedTypes": + return associatedTypes + default: + return super[dynamicMember: member] + } + } + + // sourcery: skipJSExport + public class var kind: String { return "protocol" } + + /// Returns "protocol" + public override var kind: String { Self.kind } + + /// list of all declared associated types with their names as keys + public var associatedTypes: [String: AssociatedType] { + didSet { + isGeneric = !associatedTypes.isEmpty || !genericRequirements.isEmpty + } + } + + // sourcery: skipCoding + /// list of generic requirements + public override var genericRequirements: [GenericRequirement] { + didSet { + isGeneric = !associatedTypes.isEmpty || !genericRequirements.isEmpty + } + } + + /// :nodoc: + public init(name: String = "", + parent: Type? = nil, + accessLevel: AccessLevel = .internal, + isExtension: Bool = false, + variables: [Variable] = [], + methods: [Method] = [], + subscripts: [Subscript] = [], + inheritedTypes: [String] = [], + containedTypes: [Type] = [], + typealiases: [Typealias] = [], + associatedTypes: [String: AssociatedType] = [:], + genericRequirements: [GenericRequirement] = [], + attributes: AttributeList = [:], + modifiers: [SourceryModifier] = [], + annotations: [String: NSObject] = [:], + documentation: [String] = [], + implements: [String: Type] = [:], + kind: String = Protocol.kind) { + self.associatedTypes = associatedTypes + super.init( + name: name, + parent: parent, + accessLevel: accessLevel, + isExtension: isExtension, + variables: variables, + methods: methods, + subscripts: subscripts, + inheritedTypes: inheritedTypes, + containedTypes: containedTypes, + typealiases: typealiases, + genericRequirements: genericRequirements, + attributes: attributes, + modifiers: modifiers, + annotations: annotations, + documentation: documentation, + isGeneric: !associatedTypes.isEmpty || !genericRequirements.isEmpty, + implements: implements, + kind: kind + ) + } + + /// :nodoc: + // sourcery: skipJSExport + override public var description: String { + var string = super.description + string.append(", ") + string.append("kind = \(String(describing: self.kind)), ") + string.append("associatedTypes = \(String(describing: self.associatedTypes)), ") + return string + } + + override public func diffAgainst(_ object: Any?) -> DiffableResult { + let results = DiffableResult() + guard let castObject = object as? Protocol else { + results.append("Incorrect type ") + return results + } + results.append(contentsOf: DiffableResult(identifier: "associatedTypes").trackDifference(actual: self.associatedTypes, expected: castObject.associatedTypes)) + results.append(contentsOf: super.diffAgainst(castObject)) + return results + } + + /// :nodoc: + // sourcery: skipJSExport + public override var hash: Int { + var hasher = Hasher() + hasher.combine(self.associatedTypes) + hasher.combine(super.hash) + return hasher.finalize() + } + + /// :nodoc: + public override func isEqual(_ object: Any?) -> Bool { + guard let rhs = object as? Protocol else { return false } + if self.associatedTypes != rhs.associatedTypes { return false } + return super.isEqual(rhs) + } + +// sourcery:inline:Protocol.AutoCoding + + /// :nodoc: + required public init?(coder aDecoder: NSCoder) { + guard let associatedTypes: [String: AssociatedType] = aDecoder.decode(forKey: "associatedTypes") else { + withVaList(["associatedTypes"]) { arguments in + NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) + } + fatalError() + }; self.associatedTypes = associatedTypes + super.init(coder: aDecoder) + } + + /// :nodoc: + override public func encode(with aCoder: NSCoder) { + super.encode(with: aCoder) + aCoder.encode(self.associatedTypes, forKey: "associatedTypes") + } +// sourcery:end +} +#endif \ No newline at end of file diff --git a/SourceryRuntime/Sources/Linux/AST/Type_Linux.swift b/SourceryRuntime/Sources/Linux/AST/Type_Linux.swift index a754c0868..f546b58ed 100644 --- a/SourceryRuntime/Sources/Linux/AST/Type_Linux.swift +++ b/SourceryRuntime/Sources/Linux/AST/Type_Linux.swift @@ -92,7 +92,11 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou // sourcery: forceEquality /// Kind of type declaration, i.e. `enum`, `struct`, `class`, `protocol` or `extension` - public var kind: String { isExtension ? "extension" : "unknown" } + public var kind: String { isExtension ? "extension" : _kind } + + // sourcery: skipJSExport + /// Kind of a backing store for `self.kind` + private var _kind: String /// Type access level, i.e. `internal`, `private`, `fileprivate`, `public`, `open` public let accessLevel: String @@ -422,7 +426,8 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = "unknown") { self.localName = name self.accessLevel = accessLevel.rawValue self.isExtension = isExtension @@ -441,6 +446,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou self.isGeneric = isGeneric self.genericRequirements = genericRequirements self.implements = implements + self._kind = kind super.init() containedTypes.forEach { containedType[$0.localName] = $0 @@ -471,13 +477,15 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou /// :nodoc: // sourcery: skipJSExport override public var description: String { - var string = "\(Swift.type(of: self)): " + let type: Type.Type = Swift.type(of: self) + var string = "\(type): " string.append("module = \(String(describing: self.module)), ") string.append("imports = \(String(describing: self.imports)), ") string.append("allImports = \(String(describing: self.allImports)), ") string.append("typealiases = \(String(describing: self.typealiases)), ") string.append("isExtension = \(String(describing: self.isExtension)), ") string.append("kind = \(String(describing: self.kind)), ") + string.append("_kind = \(String(describing: self._kind)), ") string.append("accessLevel = \(String(describing: self.accessLevel)), ") string.append("name = \(String(describing: self.name)), ") string.append("isUnknownExtension = \(String(describing: self.isUnknownExtension)), ") @@ -613,6 +621,12 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou fatalError() }; self.typealiases = typealiases self.isExtension = aDecoder.decode(forKey: "isExtension") + guard let _kind: String = aDecoder.decode(forKey: "_kind") else { + withVaList(["_kind"]) { arguments in + NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) + } + fatalError() + }; self._kind = _kind guard let accessLevel: String = aDecoder.decode(forKey: "accessLevel") else { withVaList(["accessLevel"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) @@ -731,6 +745,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou aCoder.encode(self.imports, forKey: "imports") aCoder.encode(self.typealiases, forKey: "typealiases") aCoder.encode(self.isExtension, forKey: "isExtension") + aCoder.encode(self._kind, forKey: "_kind") aCoder.encode(self.accessLevel, forKey: "accessLevel") aCoder.encode(self.isGeneric, forKey: "isGeneric") aCoder.encode(self.localName, forKey: "localName") diff --git a/SourceryRuntime/Sources/macOS/AST/Enum.swift b/SourceryRuntime/Sources/macOS/AST/Enum.swift index da43ffa11..f6d98ac78 100644 --- a/SourceryRuntime/Sources/macOS/AST/Enum.swift +++ b/SourceryRuntime/Sources/macOS/AST/Enum.swift @@ -9,9 +9,13 @@ import Foundation /// Defines Swift enum @objcMembers public final class Enum: Type { + + // sourcery: skipJSExport + public class var kind: String { return "enum" } + // sourcery: skipDescription /// Returns "enum" - public override var kind: String { return "enum" } + public override var kind: String { Self.kind } /// Enum cases public var cases: [EnumCase] @@ -83,7 +87,7 @@ public final class Enum: Type { self.rawTypeName = rawTypeName self.hasRawType = rawTypeName != nil || !inheritedTypes.isEmpty - super.init(name: name, parent: parent, accessLevel: accessLevel, isExtension: isExtension, variables: variables, methods: methods, inheritedTypes: inheritedTypes, containedTypes: containedTypes, typealiases: typealiases, attributes: attributes, modifiers: modifiers, annotations: annotations, documentation: documentation, isGeneric: isGeneric) + super.init(name: name, parent: parent, accessLevel: accessLevel, isExtension: isExtension, variables: variables, methods: methods, inheritedTypes: inheritedTypes, containedTypes: containedTypes, typealiases: typealiases, attributes: attributes, modifiers: modifiers, annotations: annotations, documentation: documentation, isGeneric: isGeneric, kind: Self.kind) if let rawTypeName = rawTypeName?.name, let index = self.inheritedTypes.firstIndex(of: rawTypeName) { self.inheritedTypes.remove(at: index) diff --git a/SourceryRuntime/Sources/macOS/AST/Method.swift b/SourceryRuntime/Sources/macOS/AST/Method.swift index f62f846ee..776d9c2af 100644 --- a/SourceryRuntime/Sources/macOS/AST/Method.swift +++ b/SourceryRuntime/Sources/macOS/AST/Method.swift @@ -404,7 +404,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin } fatalError() }; self.genericRequirements = genericRequirements - guard let genericParameters: [GenericParameter] = aDecoder.decode(forKey: "genericParameters") else { + guard let genericParameters: [GenericParameter] = aDecoder.decode(forKey: "genericParameters") else { withVaList(["genericParameters"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) } diff --git a/SourceryRuntime/Sources/macOS/AST/Type.swift b/SourceryRuntime/Sources/macOS/AST/Type.swift index 159f54d59..d90d6ac17 100644 --- a/SourceryRuntime/Sources/macOS/AST/Type.swift +++ b/SourceryRuntime/Sources/macOS/AST/Type.swift @@ -48,7 +48,11 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { // sourcery: forceEquality /// Kind of type declaration, i.e. `enum`, `struct`, `class`, `protocol` or `extension` - public var kind: String { isExtension ? "extension" : "unknown" } + public var kind: String { isExtension ? "extension" : _kind } + + // sourcery: skipJSExport + /// Kind of a backing store for `self.kind` + private var _kind: String /// Type access level, i.e. `internal`, `private`, `fileprivate`, `public`, `open` public let accessLevel: String @@ -378,7 +382,8 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = "unknown") { self.localName = name self.accessLevel = accessLevel.rawValue self.isExtension = isExtension @@ -397,6 +402,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { self.isGeneric = isGeneric self.genericRequirements = genericRequirements self.implements = implements + self._kind = kind super.init() containedTypes.forEach { containedType[$0.localName] = $0 @@ -435,6 +441,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { string.append("typealiases = \(String(describing: self.typealiases)), ") string.append("isExtension = \(String(describing: self.isExtension)), ") string.append("kind = \(String(describing: self.kind)), ") + string.append("_kind = \(String(describing: self._kind)), ") string.append("accessLevel = \(String(describing: self.accessLevel)), ") string.append("name = \(String(describing: self.name)), ") string.append("isUnknownExtension = \(String(describing: self.isUnknownExtension)), ") @@ -570,6 +577,12 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { fatalError() }; self.typealiases = typealiases self.isExtension = aDecoder.decode(forKey: "isExtension") + guard let _kind: String = aDecoder.decode(forKey: "_kind") else { + withVaList(["_kind"]) { arguments in + NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) + } + fatalError() + }; self._kind = _kind guard let accessLevel: String = aDecoder.decode(forKey: "accessLevel") else { withVaList(["accessLevel"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) @@ -688,6 +701,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { aCoder.encode(self.imports, forKey: "imports") aCoder.encode(self.typealiases, forKey: "typealiases") aCoder.encode(self.isExtension, forKey: "isExtension") + aCoder.encode(self._kind, forKey: "_kind") aCoder.encode(self.accessLevel, forKey: "accessLevel") aCoder.encode(self.isGeneric, forKey: "isGeneric") aCoder.encode(self.localName, forKey: "localName") diff --git a/SourceryStencil/Sources/StencilTemplate.swift b/SourceryStencil/Sources/StencilTemplate.swift index 873ac51f0..258502219 100644 --- a/SourceryStencil/Sources/StencilTemplate.swift +++ b/SourceryStencil/Sources/StencilTemplate.swift @@ -63,6 +63,7 @@ public final class StencilTemplate: StencilSwiftKit.StencilSwiftTemplate { ext.registerAccessLevelFilters(.private) ext.registerAccessLevelFilters(.fileprivate) ext.registerAccessLevelFilters(.internal) + ext.registerAccessLevelFilters(.package) ext.registerBoolFilterOrWithArguments("based", filter: { (t: Type, name: String) in t.based[name] != nil }, @@ -86,6 +87,14 @@ public final class StencilTemplate: StencilSwiftKit.StencilSwiftTemplate { ext.registerFilter("isEmpty", filter: isEmpty) ext.registerFilter("reversed", filter: reversed) ext.registerFilter("toArray", filter: toArray) + ext.registerFilter("sortedValuesByKeys", filter: sortedValuesByKeys) + ext.registerFilter("last", filter: last) + + ext.registerFilterWithArguments("push") { arg1, arg2 in + var array = arg1 as? [Any] ?? [] + array.append(arg2) + return array + } #if canImport(ObjectiveC) ext.registerFilterWithArguments("sorted") { (array, propertyName: String) -> Any? in @@ -172,7 +181,6 @@ public final class StencilTemplate: StencilSwiftKit.StencilSwiftTemplate { public extension Annotated { func isAnnotated(with annotation: String) -> Bool { - if annotation.contains("=") { let components = annotation.components(separatedBy: "=").map({ $0.trimmingCharacters(in: .whitespaces) }) var keyPath = components[0].components(separatedBy: ".") @@ -284,6 +292,29 @@ public extension Stencil.Extension { } +private func last(_ value: Any?) -> Any? { + switch value { + case let arr as NSArray: + return arr.lastObject + default: + return nil + } +} + +private func sortedValuesByKeys(_ value: Any?) -> Any? { + switch value { + case let dict as NSDictionary: + let keys = dict.allKeys.sorted { (($0 as? String) ?? "" < ($1 as? String) ?? "") } + var retVal: [Any?] = [] + for key in keys { + retVal.append(dict[key]) + } + return retVal + default: + return nil + } +} + private func toArray(_ value: Any?) -> Any? { switch value { case let array as NSArray: @@ -310,11 +341,15 @@ private func count(_ value: Any?) -> Any? { } private func isEmpty(_ value: Any?) -> Any? { - guard let array = value as? NSArray else { - return false + switch value { + case let array as NSArray: + // swiftlint:disable:next empty_count + array.count == 0 + case let string as NSString: + string.length == 0 + default: + false } - // swiftlint:disable:next empty_count - return array.count == 0 } private struct Filter { diff --git a/SourcerySwift/Sources/SourceryRuntime.content.generated.swift b/SourcerySwift/Sources/SourceryRuntime.content.generated.swift index ea101da49..fdd60d854 100644 --- a/SourcerySwift/Sources/SourceryRuntime.content.generated.swift +++ b/SourcerySwift/Sources/SourceryRuntime.content.generated.swift @@ -31,8 +31,12 @@ import Foundation @objc(SwiftActor) @objcMembers #endif public final class Actor: Type { + + // sourcery: skipJSExport + public class var kind: String { return "actor" } + /// Returns "actor" - public override var kind: String { return "actor" } + public override var kind: String { Self.kind } /// Whether type is final public var isFinal: Bool { @@ -61,7 +65,8 @@ public final class Actor: Type { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Actor.kind) { super.init( name: name, parent: parent, @@ -79,7 +84,8 @@ public final class Actor: Type { annotations: annotations, documentation: documentation, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } @@ -620,8 +626,11 @@ import Foundation @objc(SwiftClass) @objcMembers #endif public final class Class: Type { + // sourcery: skipJSExport + public class var kind: String { return "class" } + /// Returns "class" - public override var kind: String { return "class" } + public override var kind: String { Self.kind } /// Whether type is final public var isFinal: Bool { @@ -645,7 +654,8 @@ public final class Class: Type { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Class.kind) { super.init( name: name, parent: parent, @@ -663,7 +673,8 @@ public final class Class: Type { annotations: annotations, documentation: documentation, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } @@ -746,11 +757,11 @@ public enum Composer { let composed = ParserResultsComposed(parserResult: parserResult) let resolveType = { (typeName: TypeName, containingType: Type?) -> Type? in - return composed.resolveType(typeName: typeName, containingType: containingType) + composed.resolveType(typeName: typeName, containingType: containingType) } let methodResolveType = { (typeName: TypeName, containingType: Type?, method: Method) -> Type? in - return composed.resolveType(typeName: typeName, containingType: containingType, method: method) + composed.resolveType(typeName: typeName, containingType: containingType, method: method) } let processType = { (type: Type) in @@ -773,7 +784,7 @@ public enum Composer { } if let sourceryProtocol = type as? SourceryProtocol { - resolveProtocolTypes(sourceryProtocol, resolve: resolveType) + resolveAssociatedTypes(sourceryProtocol, resolve: resolveType) } } @@ -901,11 +912,13 @@ public enum Composer { protocolComposition.composedTypes = composedTypes } - private static func resolveProtocolTypes(_ sourceryProtocol: SourceryProtocol, resolve: TypeResolver) { + private static func resolveAssociatedTypes(_ sourceryProtocol: SourceryProtocol, resolve: TypeResolver) { sourceryProtocol.associatedTypes.forEach { (_, value) in guard let typeName = value.typeName, let type = resolve(typeName, sourceryProtocol) - else { return } + else { + return + } value.type = type } @@ -2218,6 +2231,7 @@ internal struct ParserResultsComposed { let parsedTypes: [Type] let functions: [SourceryMethod] let resolvedTypealiases: [String: Typealias] + let associatedTypes: [String: AssociatedType] let unresolvedTypealiases: [String: Typealias] init(parserResult: FileParserResult) { @@ -2228,6 +2242,7 @@ internal struct ParserResultsComposed { let aliases = Self.typealiases(parserResult) resolvedTypealiases = aliases.resolved unresolvedTypealiases = aliases.unresolved + associatedTypes = Self.extractAssociatedTypes(parserResult) parsedTypes = parserResult.types // set definedInType for all methods and variables @@ -2256,6 +2271,11 @@ internal struct ParserResultsComposed { alias.type = resolveType(typeName: alias.typeName, containingType: alias.parent) } + /// Map associated types + associatedTypes.forEach { + typeMap[$0.key] = $0.value.type + } + types = unifyTypes() } @@ -2301,7 +2321,7 @@ internal struct ParserResultsComposed { resolveExtensionOfNestedType(type) } - if let resolved = resolveGlobalName(for: oldName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases)?.name { + 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)." @@ -2321,7 +2341,7 @@ internal struct ParserResultsComposed { // extend all types with their extensions parsedTypes.forEach { type in let inheritedTypes: [[String]] = type.inheritedTypes.compactMap { inheritedName in - if let resolvedGlobalName = resolveGlobalName(for: inheritedName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases)?.name { + if let resolvedGlobalName = resolveGlobalName(for: inheritedName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases, associatedTypes: associatedTypes)?.name { return [resolvedGlobalName] } if let baseType = Composer.findBaseType(for: type, name: inheritedName, typesByName: typeMap) { @@ -2380,6 +2400,14 @@ internal struct ParserResultsComposed { }) } + // extract associated types from all types and add them to types + private static func extractAssociatedTypes(_ parserResult: FileParserResult) -> [String: AssociatedType] { + parserResult.types + .compactMap { $0 as? SourceryProtocol } + .map { $0.associatedTypes } + .flatMap { $0 }.reduce(into: [:]) { $0[$1.key] = $1.value } + } + /// returns typealiases map to their full names, with `resolved` removing intermediate /// typealises and `unresolved` including typealiases that reference other typealiases. private static func typealiases(_ parserResult: FileParserResult) -> (resolved: [String: Typealias], unresolved: [String: Typealias]) { @@ -2412,11 +2440,14 @@ internal struct ParserResultsComposed { } /// Resolves type identifier for name - func resolveGlobalName(for type: String, - containingType: Type? = nil, - unique: [String: Type]? = nil, - modules: [String: [String: Type]], - typealiases: [String: Typealias]) -> (name: String, typealias: Typealias?)? { + func resolveGlobalName( + for type: String, + containingType: Type? = nil, + unique: [String: Type]? = nil, + modules: [String: [String: Type]], + typealiases: [String: Typealias], + associatedTypes: [String: AssociatedType] + ) -> (name: String, typealias: Typealias?)? { // if the type exists for this name and isn't an extension just return it's name // if it's extension we need to check if there aren't other options TODO: verify if let realType = unique?[type], realType.isExtension == false { @@ -2427,6 +2458,13 @@ internal struct ParserResultsComposed { return (name: alias.type?.globalName ?? alias.typeName.name, typealias: alias) } + if let associatedType = associatedTypes[type], + let actualType = associatedType.type + { + let typeName = associatedType.typeName ?? TypeName(name: actualType.name) + return (name: actualType.globalName, typealias: Typealias(aliasName: type, typeName: typeName)) + } + if let containingType = containingType { if type == "Self" { return (name: containingType.globalName, typealias: nil) @@ -2436,7 +2474,7 @@ internal struct ParserResultsComposed { while currentContainer != nil, let parentName = currentContainer?.globalName { /// TODO: no parent for sure? /// manually walk the containment tree - if let name = resolveGlobalName(for: "\\(parentName).\\(type)", containingType: nil, unique: unique, modules: modules, typealiases: typealiases) { + if let name = resolveGlobalName(for: "\\(parentName).\\(type)", containingType: nil, unique: unique, modules: modules, typealiases: typealiases, associatedTypes: associatedTypes) { return name } @@ -2770,20 +2808,27 @@ internal struct ParserResultsComposed { } } - return unique[resolvedIdentifier] + if let associatedType = associatedTypes[resolvedIdentifier] { + return associatedType.type + } + + return unique[resolvedIdentifier] ?? unique[typeName.name] } private func actualTypeName(for typeName: TypeName, - containingType: Type? = nil) -> TypeName? { + containingType: Type? = nil) -> TypeName? { let unique = typeMap let typealiases = resolvedTypealiases + let associatedTypes = associatedTypes var unwrapped = typeName.unwrappedTypeName if let generic = typeName.generic { unwrapped = generic.name + } else if let type = associatedTypes[unwrapped] { + unwrapped = type.name } - guard let aliased = resolveGlobalName(for: unwrapped, containingType: containingType, unique: unique, modules: modules, typealiases: typealiases) else { + guard let aliased = resolveGlobalName(for: unwrapped, containingType: containingType, unique: unique, modules: modules, typealiases: typealiases, associatedTypes: associatedTypes) else { return nil } @@ -2862,8 +2907,11 @@ public typealias SourceryProtocol = Protocol #endif public final class Protocol: Type { + // sourcery: skipJSExport + public class var kind: String { return "protocol" } + /// Returns "protocol" - public override var kind: String { return "protocol" } + public override var kind: String { Self.kind } /// list of all declared associated types with their names as keys public var associatedTypes: [String: AssociatedType] { @@ -2897,7 +2945,8 @@ public final class Protocol: Type { modifiers: [SourceryModifier] = [], annotations: [String: NSObject] = [:], documentation: [String] = [], - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Protocol.kind) { self.associatedTypes = associatedTypes super.init( name: name, @@ -2916,7 +2965,8 @@ public final class Protocol: Type { annotations: annotations, documentation: documentation, isGeneric: !associatedTypes.isEmpty || !genericRequirements.isEmpty, - implements: implements + implements: implements, + kind: kind ) } @@ -2992,8 +3042,11 @@ import Foundation #endif public final class ProtocolComposition: Type { + // sourcery: skipJSExport + public class var kind: String { return "protocolComposition" } + /// Returns "protocolComposition" - public override var kind: String { return "protocolComposition" } + public override var kind: String { Self.kind } /// The names of the types composed to form this composition public let composedTypeNames: [TypeName] @@ -3018,7 +3071,8 @@ public final class ProtocolComposition: Type { isGeneric: Bool = false, composedTypeNames: [TypeName] = [], composedTypes: [Type]? = nil, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = ProtocolComposition.kind) { self.composedTypeNames = composedTypeNames self.composedTypes = composedTypes super.init( @@ -3034,7 +3088,8 @@ public final class ProtocolComposition: Type { typealiases: typealiases, annotations: annotations, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } @@ -3224,8 +3279,11 @@ import Foundation #endif public final class Struct: Type { + // sourcery: skipJSExport + public class var kind: String { return "struct" } + /// Returns "struct" - public override var kind: String { return "struct" } + public override var kind: String { Self.kind } /// :nodoc: public override init(name: String = "", @@ -3244,7 +3302,8 @@ public final class Struct: Type { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Struct.kind) { super.init( name: name, parent: parent, @@ -3262,7 +3321,8 @@ public final class Struct: Type { annotations: annotations, documentation: documentation, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } @@ -4238,9 +4298,13 @@ import Foundation /// Defines Swift enum @objcMembers public final class Enum: Type { + + // sourcery: skipJSExport + public class var kind: String { return "enum" } + // sourcery: skipDescription /// Returns "enum" - public override var kind: String { return "enum" } + public override var kind: String { Self.kind } /// Enum cases public var cases: [EnumCase] @@ -4312,7 +4376,7 @@ public final class Enum: Type { self.rawTypeName = rawTypeName self.hasRawType = rawTypeName != nil || !inheritedTypes.isEmpty - super.init(name: name, parent: parent, accessLevel: accessLevel, isExtension: isExtension, variables: variables, methods: methods, inheritedTypes: inheritedTypes, containedTypes: containedTypes, typealiases: typealiases, attributes: attributes, modifiers: modifiers, annotations: annotations, documentation: documentation, isGeneric: isGeneric) + super.init(name: name, parent: parent, accessLevel: accessLevel, isExtension: isExtension, variables: variables, methods: methods, inheritedTypes: inheritedTypes, containedTypes: containedTypes, typealiases: typealiases, attributes: attributes, modifiers: modifiers, annotations: annotations, documentation: documentation, isGeneric: isGeneric, kind: Self.kind) if let rawTypeName = rawTypeName?.name, let index = self.inheritedTypes.firstIndex(of: rawTypeName) { self.inheritedTypes.remove(at: index) @@ -6025,7 +6089,11 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { // sourcery: forceEquality /// Kind of type declaration, i.e. `enum`, `struct`, `class`, `protocol` or `extension` - public var kind: String { isExtension ? "extension" : "unknown" } + public var kind: String { isExtension ? "extension" : _kind } + + // sourcery: skipJSExport + /// Kind of a backing store for `self.kind` + private var _kind: String /// Type access level, i.e. `internal`, `private`, `fileprivate`, `public`, `open` public let accessLevel: String @@ -6355,7 +6423,8 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = "unknown") { self.localName = name self.accessLevel = accessLevel.rawValue self.isExtension = isExtension @@ -6374,6 +6443,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { self.isGeneric = isGeneric self.genericRequirements = genericRequirements self.implements = implements + self._kind = kind super.init() containedTypes.forEach { containedType[$0.localName] = $0 @@ -6412,6 +6482,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { string.append("typealiases = \\(String(describing: self.typealiases)), ") string.append("isExtension = \\(String(describing: self.isExtension)), ") string.append("kind = \\(String(describing: self.kind)), ") + string.append("_kind = \\(String(describing: self._kind)), ") string.append("accessLevel = \\(String(describing: self.accessLevel)), ") string.append("name = \\(String(describing: self.name)), ") string.append("isUnknownExtension = \\(String(describing: self.isUnknownExtension)), ") @@ -6547,6 +6618,12 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { fatalError() }; self.typealiases = typealiases self.isExtension = aDecoder.decode(forKey: "isExtension") + guard let _kind: String = aDecoder.decode(forKey: "_kind") else { + withVaList(["_kind"]) { arguments in + NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) + } + fatalError() + }; self._kind = _kind guard let accessLevel: String = aDecoder.decode(forKey: "accessLevel") else { withVaList(["accessLevel"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) @@ -6665,6 +6742,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable { aCoder.encode(self.imports, forKey: "imports") aCoder.encode(self.typealiases, forKey: "typealiases") aCoder.encode(self.isExtension, forKey: "isExtension") + aCoder.encode(self._kind, forKey: "_kind") aCoder.encode(self.accessLevel, forKey: "accessLevel") aCoder.encode(self.isGeneric, forKey: "isGeneric") aCoder.encode(self.localName, forKey: "localName") diff --git a/SourcerySwift/Sources/SourceryRuntime_Linux.content.generated.swift b/SourcerySwift/Sources/SourceryRuntime_Linux.content.generated.swift index ca5c7d95d..e26cfcf50 100644 --- a/SourcerySwift/Sources/SourceryRuntime_Linux.content.generated.swift +++ b/SourcerySwift/Sources/SourceryRuntime_Linux.content.generated.swift @@ -31,8 +31,12 @@ import Foundation @objc(SwiftActor) @objcMembers #endif public final class Actor: Type { + + // sourcery: skipJSExport + public class var kind: String { return "actor" } + /// Returns "actor" - public override var kind: String { return "actor" } + public override var kind: String { Self.kind } /// Whether type is final public var isFinal: Bool { @@ -61,7 +65,8 @@ public final class Actor: Type { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Actor.kind) { super.init( name: name, parent: parent, @@ -79,7 +84,8 @@ public final class Actor: Type { annotations: annotations, documentation: documentation, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } @@ -620,8 +626,11 @@ import Foundation @objc(SwiftClass) @objcMembers #endif public final class Class: Type { + // sourcery: skipJSExport + public class var kind: String { return "class" } + /// Returns "class" - public override var kind: String { return "class" } + public override var kind: String { Self.kind } /// Whether type is final public var isFinal: Bool { @@ -645,7 +654,8 @@ public final class Class: Type { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Class.kind) { super.init( name: name, parent: parent, @@ -663,7 +673,8 @@ public final class Class: Type { annotations: annotations, documentation: documentation, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } @@ -746,11 +757,11 @@ public enum Composer { let composed = ParserResultsComposed(parserResult: parserResult) let resolveType = { (typeName: TypeName, containingType: Type?) -> Type? in - return composed.resolveType(typeName: typeName, containingType: containingType) + composed.resolveType(typeName: typeName, containingType: containingType) } let methodResolveType = { (typeName: TypeName, containingType: Type?, method: Method) -> Type? in - return composed.resolveType(typeName: typeName, containingType: containingType, method: method) + composed.resolveType(typeName: typeName, containingType: containingType, method: method) } let processType = { (type: Type) in @@ -773,7 +784,7 @@ public enum Composer { } if let sourceryProtocol = type as? SourceryProtocol { - resolveProtocolTypes(sourceryProtocol, resolve: resolveType) + resolveAssociatedTypes(sourceryProtocol, resolve: resolveType) } } @@ -901,11 +912,13 @@ public enum Composer { protocolComposition.composedTypes = composedTypes } - private static func resolveProtocolTypes(_ sourceryProtocol: SourceryProtocol, resolve: TypeResolver) { + private static func resolveAssociatedTypes(_ sourceryProtocol: SourceryProtocol, resolve: TypeResolver) { sourceryProtocol.associatedTypes.forEach { (_, value) in guard let typeName = value.typeName, let type = resolve(typeName, sourceryProtocol) - else { return } + else { + return + } value.type = type } @@ -2218,6 +2231,7 @@ internal struct ParserResultsComposed { let parsedTypes: [Type] let functions: [SourceryMethod] let resolvedTypealiases: [String: Typealias] + let associatedTypes: [String: AssociatedType] let unresolvedTypealiases: [String: Typealias] init(parserResult: FileParserResult) { @@ -2228,6 +2242,7 @@ internal struct ParserResultsComposed { let aliases = Self.typealiases(parserResult) resolvedTypealiases = aliases.resolved unresolvedTypealiases = aliases.unresolved + associatedTypes = Self.extractAssociatedTypes(parserResult) parsedTypes = parserResult.types // set definedInType for all methods and variables @@ -2256,6 +2271,11 @@ internal struct ParserResultsComposed { alias.type = resolveType(typeName: alias.typeName, containingType: alias.parent) } + /// Map associated types + associatedTypes.forEach { + typeMap[$0.key] = $0.value.type + } + types = unifyTypes() } @@ -2301,7 +2321,7 @@ internal struct ParserResultsComposed { resolveExtensionOfNestedType(type) } - if let resolved = resolveGlobalName(for: oldName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases)?.name { + 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)." @@ -2321,7 +2341,7 @@ internal struct ParserResultsComposed { // extend all types with their extensions parsedTypes.forEach { type in let inheritedTypes: [[String]] = type.inheritedTypes.compactMap { inheritedName in - if let resolvedGlobalName = resolveGlobalName(for: inheritedName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases)?.name { + if let resolvedGlobalName = resolveGlobalName(for: inheritedName, containingType: type.parent, unique: typeMap, modules: modules, typealiases: resolvedTypealiases, associatedTypes: associatedTypes)?.name { return [resolvedGlobalName] } if let baseType = Composer.findBaseType(for: type, name: inheritedName, typesByName: typeMap) { @@ -2380,6 +2400,14 @@ internal struct ParserResultsComposed { }) } + // extract associated types from all types and add them to types + private static func extractAssociatedTypes(_ parserResult: FileParserResult) -> [String: AssociatedType] { + parserResult.types + .compactMap { $0 as? SourceryProtocol } + .map { $0.associatedTypes } + .flatMap { $0 }.reduce(into: [:]) { $0[$1.key] = $1.value } + } + /// returns typealiases map to their full names, with `resolved` removing intermediate /// typealises and `unresolved` including typealiases that reference other typealiases. private static func typealiases(_ parserResult: FileParserResult) -> (resolved: [String: Typealias], unresolved: [String: Typealias]) { @@ -2412,11 +2440,14 @@ internal struct ParserResultsComposed { } /// Resolves type identifier for name - func resolveGlobalName(for type: String, - containingType: Type? = nil, - unique: [String: Type]? = nil, - modules: [String: [String: Type]], - typealiases: [String: Typealias]) -> (name: String, typealias: Typealias?)? { + func resolveGlobalName( + for type: String, + containingType: Type? = nil, + unique: [String: Type]? = nil, + modules: [String: [String: Type]], + typealiases: [String: Typealias], + associatedTypes: [String: AssociatedType] + ) -> (name: String, typealias: Typealias?)? { // if the type exists for this name and isn't an extension just return it's name // if it's extension we need to check if there aren't other options TODO: verify if let realType = unique?[type], realType.isExtension == false { @@ -2427,6 +2458,13 @@ internal struct ParserResultsComposed { return (name: alias.type?.globalName ?? alias.typeName.name, typealias: alias) } + if let associatedType = associatedTypes[type], + let actualType = associatedType.type + { + let typeName = associatedType.typeName ?? TypeName(name: actualType.name) + return (name: actualType.globalName, typealias: Typealias(aliasName: type, typeName: typeName)) + } + if let containingType = containingType { if type == "Self" { return (name: containingType.globalName, typealias: nil) @@ -2436,7 +2474,7 @@ internal struct ParserResultsComposed { while currentContainer != nil, let parentName = currentContainer?.globalName { /// TODO: no parent for sure? /// manually walk the containment tree - if let name = resolveGlobalName(for: "\\(parentName).\\(type)", containingType: nil, unique: unique, modules: modules, typealiases: typealiases) { + if let name = resolveGlobalName(for: "\\(parentName).\\(type)", containingType: nil, unique: unique, modules: modules, typealiases: typealiases, associatedTypes: associatedTypes) { return name } @@ -2770,20 +2808,27 @@ internal struct ParserResultsComposed { } } - return unique[resolvedIdentifier] + if let associatedType = associatedTypes[resolvedIdentifier] { + return associatedType.type + } + + return unique[resolvedIdentifier] ?? unique[typeName.name] } private func actualTypeName(for typeName: TypeName, - containingType: Type? = nil) -> TypeName? { + containingType: Type? = nil) -> TypeName? { let unique = typeMap let typealiases = resolvedTypealiases + let associatedTypes = associatedTypes var unwrapped = typeName.unwrappedTypeName if let generic = typeName.generic { unwrapped = generic.name + } else if let type = associatedTypes[unwrapped] { + unwrapped = type.name } - guard let aliased = resolveGlobalName(for: unwrapped, containingType: containingType, unique: unique, modules: modules, typealiases: typealiases) else { + guard let aliased = resolveGlobalName(for: unwrapped, containingType: containingType, unique: unique, modules: modules, typealiases: typealiases, associatedTypes: associatedTypes) else { return nil } @@ -2862,8 +2907,11 @@ public typealias SourceryProtocol = Protocol #endif public final class Protocol: Type { + // sourcery: skipJSExport + public class var kind: String { return "protocol" } + /// Returns "protocol" - public override var kind: String { return "protocol" } + public override var kind: String { Self.kind } /// list of all declared associated types with their names as keys public var associatedTypes: [String: AssociatedType] { @@ -2897,7 +2945,8 @@ public final class Protocol: Type { modifiers: [SourceryModifier] = [], annotations: [String: NSObject] = [:], documentation: [String] = [], - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Protocol.kind) { self.associatedTypes = associatedTypes super.init( name: name, @@ -2916,7 +2965,8 @@ public final class Protocol: Type { annotations: annotations, documentation: documentation, isGeneric: !associatedTypes.isEmpty || !genericRequirements.isEmpty, - implements: implements + implements: implements, + kind: kind ) } @@ -2992,8 +3042,11 @@ import Foundation #endif public final class ProtocolComposition: Type { + // sourcery: skipJSExport + public class var kind: String { return "protocolComposition" } + /// Returns "protocolComposition" - public override var kind: String { return "protocolComposition" } + public override var kind: String { Self.kind } /// The names of the types composed to form this composition public let composedTypeNames: [TypeName] @@ -3018,7 +3071,8 @@ public final class ProtocolComposition: Type { isGeneric: Bool = false, composedTypeNames: [TypeName] = [], composedTypes: [Type]? = nil, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = ProtocolComposition.kind) { self.composedTypeNames = composedTypeNames self.composedTypes = composedTypes super.init( @@ -3034,7 +3088,8 @@ public final class ProtocolComposition: Type { typealiases: typealiases, annotations: annotations, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } @@ -3224,8 +3279,11 @@ import Foundation #endif public final class Struct: Type { + // sourcery: skipJSExport + public class var kind: String { return "struct" } + /// Returns "struct" - public override var kind: String { return "struct" } + public override var kind: String { Self.kind } /// :nodoc: public override init(name: String = "", @@ -3244,7 +3302,8 @@ public final class Struct: Type { annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = Struct.kind) { super.init( name: name, parent: parent, @@ -3262,7 +3321,8 @@ public final class Struct: Type { annotations: annotations, documentation: documentation, isGeneric: isGeneric, - implements: implements + implements: implements, + kind: kind ) } @@ -4531,9 +4591,12 @@ public final class Enum: Type { } } + // sourcery: skipJSExport + public class var kind: String { return "enum" } + // sourcery: skipDescription /// Returns "enum" - public override var kind: String { return "enum" } + public override var kind: String { Self.kind } /// Enum cases public var cases: [EnumCase] @@ -4605,7 +4668,7 @@ public final class Enum: Type { self.rawTypeName = rawTypeName self.hasRawType = rawTypeName != nil || !inheritedTypes.isEmpty - super.init(name: name, parent: parent, accessLevel: accessLevel, isExtension: isExtension, variables: variables, methods: methods, inheritedTypes: inheritedTypes, containedTypes: containedTypes, typealiases: typealiases, attributes: attributes, modifiers: modifiers, annotations: annotations, documentation: documentation, isGeneric: isGeneric) + super.init(name: name, parent: parent, accessLevel: accessLevel, isExtension: isExtension, variables: variables, methods: methods, inheritedTypes: inheritedTypes, containedTypes: containedTypes, typealiases: typealiases, attributes: attributes, modifiers: modifiers, annotations: annotations, documentation: documentation, isGeneric: isGeneric, kind: Self.kind) if let rawTypeName = rawTypeName?.name, let index = self.inheritedTypes.firstIndex(of: rawTypeName) { self.inheritedTypes.remove(at: index) @@ -6734,7 +6797,11 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou // sourcery: forceEquality /// Kind of type declaration, i.e. `enum`, `struct`, `class`, `protocol` or `extension` - public var kind: String { isExtension ? "extension" : "unknown" } + public var kind: String { isExtension ? "extension" : _kind } + + // sourcery: skipJSExport + /// Kind of a backing store for `self.kind` + private var _kind: String /// Type access level, i.e. `internal`, `private`, `fileprivate`, `public`, `open` public let accessLevel: String @@ -7064,7 +7131,8 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou annotations: [String: NSObject] = [:], documentation: [String] = [], isGeneric: Bool = false, - implements: [String: Type] = [:]) { + implements: [String: Type] = [:], + kind: String = "unknown") { self.localName = name self.accessLevel = accessLevel.rawValue self.isExtension = isExtension @@ -7083,6 +7151,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou self.isGeneric = isGeneric self.genericRequirements = genericRequirements self.implements = implements + self._kind = kind super.init() containedTypes.forEach { containedType[$0.localName] = $0 @@ -7113,13 +7182,15 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou /// :nodoc: // sourcery: skipJSExport override public var description: String { - var string = "\\(Swift.type(of: self)): " + let type: Type.Type = Swift.type(of: self) + var string = "\\(type): " string.append("module = \\(String(describing: self.module)), ") string.append("imports = \\(String(describing: self.imports)), ") string.append("allImports = \\(String(describing: self.allImports)), ") string.append("typealiases = \\(String(describing: self.typealiases)), ") string.append("isExtension = \\(String(describing: self.isExtension)), ") string.append("kind = \\(String(describing: self.kind)), ") + string.append("_kind = \\(String(describing: self._kind)), ") string.append("accessLevel = \\(String(describing: self.accessLevel)), ") string.append("name = \\(String(describing: self.name)), ") string.append("isUnknownExtension = \\(String(describing: self.isUnknownExtension)), ") @@ -7255,6 +7326,12 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou fatalError() }; self.typealiases = typealiases self.isExtension = aDecoder.decode(forKey: "isExtension") + guard let _kind: String = aDecoder.decode(forKey: "_kind") else { + withVaList(["_kind"]) { arguments in + NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) + } + fatalError() + }; self._kind = _kind guard let accessLevel: String = aDecoder.decode(forKey: "accessLevel") else { withVaList(["accessLevel"]) { arguments in NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments) @@ -7373,6 +7450,7 @@ public class Type: NSObject, SourceryModel, Annotated, Documented, Diffable, Sou aCoder.encode(self.imports, forKey: "imports") aCoder.encode(self.typealiases, forKey: "typealiases") aCoder.encode(self.isExtension, forKey: "isExtension") + aCoder.encode(self._kind, forKey: "_kind") aCoder.encode(self.accessLevel, forKey: "accessLevel") aCoder.encode(self.isGeneric, forKey: "isGeneric") aCoder.encode(self.localName, forKey: "localName") diff --git a/SourceryTests/Output/DryOutputSpec.swift b/SourceryTests/Output/DryOutputSpec.swift index 82a4d940e..e3c609392 100644 --- a/SourceryTests/Output/DryOutputSpec.swift +++ b/SourceryTests/Output/DryOutputSpec.swift @@ -485,15 +485,6 @@ internal func == (lhs: EqEnum, rhs: EqEnum) -> Bool { let outputInterceptor = OutputInterceptor() sourcery.dryOutput = outputInterceptor.handleOutput(_:) - let expectedResults = ["Basic+Other.swift", - "Basic.swift", - "Basic+Other.swift", - "Basic.swift", - "Basic.swift", - "Basic.swift", - "Function.swift"] - .compactMap { try? (Stubs.resultDirectory + Path($0)).read(.utf8) } - expect { try sourcery.processFiles(.sources(Paths(include: [Stubs.sourceDirectory])), usingTemplates: Paths(include: templatePaths), diff --git a/SourceryTests/Parsing/ComposerSpec.swift b/SourceryTests/Parsing/ComposerSpec.swift index 1b5afa221..2bae5b5bb 100644 --- a/SourceryTests/Parsing/ComposerSpec.swift +++ b/SourceryTests/Parsing/ComposerSpec.swift @@ -1793,7 +1793,7 @@ class ParserComposerSpec: QuickSpec { actualTypeName: givenTypealias.typeName ) ) - let actualProtocol = parse(code).first + let actualProtocol = parse(code).last expect(actualProtocol).to(equal(expectedProtocol)) let actualTypeName = (actualProtocol as? SourceryProtocol)?.associatedTypes.first?.value.typeName?.actualTypeName expect(actualTypeName).to(equal(givenTypealias.actualTypeName)) @@ -1902,7 +1902,7 @@ class ParserComposerSpec: QuickSpec { it("resolves protocol generic requirement types and inherits associated types") { let expectedRightType = Struct(name: "RightType") - let genericProtocol = Protocol(name: "GenericProtocol", associatedTypes: ["LeftType": AssociatedType(name: "LeftType")]) + let genericProtocol = Protocol(name: "GenericProtocol", associatedTypes: ["LeftType": AssociatedType(name: "LeftType", typeName: TypeName(name: "Any"))]) let expectedProtocol = Protocol(name: "SomeGenericProtocol", inheritedTypes: ["GenericProtocol"]) expectedProtocol.associatedTypes = genericProtocol.associatedTypes expectedProtocol.genericRequirements = [ diff --git a/SourceryTests/Parsing/FileParser_AssociatedTypeSpec.swift b/SourceryTests/Parsing/FileParser_AssociatedTypeSpec.swift index 2cf50bf31..46d7e2e61 100644 --- a/SourceryTests/Parsing/FileParser_AssociatedTypeSpec.swift +++ b/SourceryTests/Parsing/FileParser_AssociatedTypeSpec.swift @@ -33,7 +33,7 @@ final class FileParserAssociatedTypeSpec: QuickSpec { associatedtype Bar } """ - expect(associatedType(code)).to(equal([AssociatedType(name: "Bar")])) + expect(associatedType(code)).to(equal([AssociatedType(name: "Bar", typeName: TypeName(name: "Any"))])) } } @@ -45,7 +45,7 @@ final class FileParserAssociatedTypeSpec: QuickSpec { associatedtype Baz } """ - expect(associatedType(code).sorted(by: { $0.name < $1.name })).to(equal([AssociatedType(name: "Bar"), AssociatedType(name: "Baz")])) + expect(associatedType(code).sorted(by: { $0.name < $1.name })).to(equal([AssociatedType(name: "Bar", typeName: TypeName(name: "Any")), AssociatedType(name: "Baz", typeName: TypeName(name: "Any"))])) } } diff --git a/SourceryTests/Parsing/FileParser_AttributesModifierSpec.swift b/SourceryTests/Parsing/FileParser_AttributesModifierSpec.swift index bab04ac6b..ecbe90a94 100644 --- a/SourceryTests/Parsing/FileParser_AttributesModifierSpec.swift +++ b/SourceryTests/Parsing/FileParser_AttributesModifierSpec.swift @@ -231,7 +231,7 @@ var variable: Bool expect(arguments).toNot(beNil()) if let parsedArguments = arguments { for expected in expectedArguments { - expect((parsedArguments[expected.key] as? NSObject)).to(equal(expected.value)) + expect(parsedArguments[expected.key]).to(equal(expected.value)) } } } diff --git a/Templates/Templates/AutoMockable.stencil b/Templates/Templates/AutoMockable.stencil index f1562ec64..fb2a45ac5 100755 --- a/Templates/Templates/AutoMockable.stencil +++ b/Templates/Templates/AutoMockable.stencil @@ -332,8 +332,50 @@ import {{ import }} {%- endmacro %} {% macro methodName method %}func {{ method.shortName}}({%- for param in method.parameters %}{% if param.argumentLabel == nil %}_ {% if not param.name == "" %}{{ param.name }}{% else %}arg{{ param.index }}{% endif %}{%elif param.argumentLabel == param.name%}{{ param.name }}{%else%}{{ param.argumentLabel }} {{ param.name }}{% endif %}: {% if param.typeName.isClosure and param.typeName.closure.parameters.count > 1 %}({% endif %}{% call existentialParameterTypeName param.typeName param.isVariadic %}{% if not forloop.last %}, {% endif %}{% endfor -%}){% endmacro %} +{% macro extractProtocolCompositionFromAssociatedTypes type -%} + {%- if type.associatedTypes|sortedValuesByKeys|count > 0 -%} + < + {%- for associatedType in type.associatedTypes|sortedValuesByKeys -%} + {% if associatedType.type.kind != nil and associatedType.type.kind|contains:"protocol" %} + {{ associatedType.name }}: {{ associatedType.typeName }}, + {%- endif -%} + {%- endfor -%} + > + {%- endif -%} +{%- endmacro %} + +{%- macro extractProtocolRequirementsFromAssociatedTypes associatedTypes -%} + {%- for associatedType in associatedTypes -%} + {%- if associatedType.type.kind != nil and associatedType.type.kind|contains:"protocol" -%} + {%- for requirement in associatedType.type.genericRequirements -%} + {%- set requirementString -%} + {{ requirement.leftType.name }} {{ requirement.relationshipSyntax }} {{ requirement.rightType.typeName.name }} + {%- endset -%} + {{ requirementString }}, + {%- endfor -%} + {%- endif -%} + {%- endfor -%} +{%- endmacro -%} + + +{% macro extractProtocolRequirementsFromType type -%} + {%- set requirements -%} + {% call extractProtocolRequirementsFromAssociatedTypes type.associatedTypes|sortedValuesByKeys %} + {%- endset -%} + {% if requirements|isEmpty == false %} + where {{ requirements }}{ + {%- else -%} + { + {% endif %} +{%- endmacro %} + {% for type in types.protocols where type.based.AutoMockable or type|annotated:"AutoMockable" %}{% if type.name != "AutoMockable" %} -{% call accessLevel type.accessLevel %}class {{ type.name }}Mock: {{ type.name }} { +{% call accessLevel type.accessLevel %}class {{ type.name }}Mock{% set generics %}{% call extractProtocolCompositionFromAssociatedTypes type %}{% endset %}{{ generics | replace:",>",">"}}: {{ type.name }} {%- set requirements -%}{% call extractProtocolRequirementsFromType type %}{%- endset -%} {{ requirements|replace:",{","{"|replace:"{"," {" }} +{% for associatedType in type.associatedTypes|sortedValuesByKeys %} + {% if associatedType.type.kind == nil or not associatedType.type.kind|contains:"protocol" %} + typealias {{ associatedType.name }} = {% if associatedType.type != nil %}{{ associatedType.type.name }}{% elif associatedType.typeName != nil %}{{ associatedType.typeName.name }}{% else %}Any{% endif %} + {% endif %} +{% endfor %} {% if type.accessLevel == "public" %}public init() {}{% endif %} diff --git a/Templates/Tests/Context/AutoMockable.swift b/Templates/Tests/Context/AutoMockable.swift index 0678af2cd..990d7730d 100644 --- a/Templates/Tests/Context/AutoMockable.swift +++ b/Templates/Tests/Context/AutoMockable.swift @@ -270,3 +270,25 @@ public protocol ProtocolWithMethodWithInoutParameter { func execute(param: inout String) func execute(param: inout String, bar: Int) } +//sourcery:AutoMockable +protocol TestProtocol { + associatedtype Value: Sequence where Value.Element: Collection, Value.Element: Hashable, Value.Element: Comparable + + func getValue() -> Value + + associatedtype Value2 = Int + + func getValue2() -> Value2 + + associatedtype Value3: Collection where Value3.Element == String + + func getValue3() -> Value3 + + associatedtype Value5: Sequence where Value5.Element == Int + + func getValue5() -> Value5 + + associatedtype Value6: Sequence where Value6.Element == Int, Value6.Element: Hashable, Value6.Element: Comparable + + func getValue6() -> Value6 +} diff --git a/Templates/Tests/Context_Linux/AutoMockable.swift b/Templates/Tests/Context_Linux/AutoMockable.swift index 0678af2cd..990d7730d 100644 --- a/Templates/Tests/Context_Linux/AutoMockable.swift +++ b/Templates/Tests/Context_Linux/AutoMockable.swift @@ -270,3 +270,25 @@ public protocol ProtocolWithMethodWithInoutParameter { func execute(param: inout String) func execute(param: inout String, bar: Int) } +//sourcery:AutoMockable +protocol TestProtocol { + associatedtype Value: Sequence where Value.Element: Collection, Value.Element: Hashable, Value.Element: Comparable + + func getValue() -> Value + + associatedtype Value2 = Int + + func getValue2() -> Value2 + + associatedtype Value3: Collection where Value3.Element == String + + func getValue3() -> Value3 + + associatedtype Value5: Sequence where Value5.Element == Int + + func getValue5() -> Value5 + + associatedtype Value6: Sequence where Value6.Element == Int, Value6.Element: Hashable, Value6.Element: Comparable + + func getValue6() -> Value6 +} diff --git a/Templates/Tests/Expected/AutoMockable.expected b/Templates/Tests/Expected/AutoMockable.expected index 5c596bda8..6218727a4 100644 --- a/Templates/Tests/Expected/AutoMockable.expected +++ b/Templates/Tests/Expected/AutoMockable.expected @@ -1,4 +1,4 @@ -// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.2.4 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT // swiftlint:disable line_length // swiftlint:disable variable_name @@ -31,6 +31,7 @@ import AppKit + public class AccessLevelProtocolMock: AccessLevelProtocol { @@ -1644,6 +1645,109 @@ class SubscriptProtocolMock: SubscriptProtocol { subscript(arg: String) -> T? where T : Cancellable { get throws { fatalError("Subscripts are not fully supported yet") } } +} +class TestProtocolMock< + Value: Sequence, + Value3: Collection, + Value5: Sequence, + Value6: Sequence>: TestProtocol + where Value.Element : Collection,Value.Element : Hashable,Value.Element : Comparable,Value3.Element == String,Value5.Element == Int,Value6.Element == Int,Value6.Element : Hashable,Value6.Element : Comparable { + typealias Value2 = Int + + + + + //MARK: - getValue + + var getValueSequenceCallsCount = 0 + var getValueSequenceCalled: Bool { + return getValueSequenceCallsCount > 0 + } + var getValueSequenceReturnValue: Value! + var getValueSequenceClosure: (() -> Value)? + + func getValue() -> Value { + getValueSequenceCallsCount += 1 + if let getValueSequenceClosure = getValueSequenceClosure { + return getValueSequenceClosure() + } else { + return getValueSequenceReturnValue + } + } + + //MARK: - getValue2 + + var getValue2Value2CallsCount = 0 + var getValue2Value2Called: Bool { + return getValue2Value2CallsCount > 0 + } + var getValue2Value2ReturnValue: Value2! + var getValue2Value2Closure: (() -> Value2)? + + func getValue2() -> Value2 { + getValue2Value2CallsCount += 1 + if let getValue2Value2Closure = getValue2Value2Closure { + return getValue2Value2Closure() + } else { + return getValue2Value2ReturnValue + } + } + + //MARK: - getValue3 + + var getValue3CollectionCallsCount = 0 + var getValue3CollectionCalled: Bool { + return getValue3CollectionCallsCount > 0 + } + var getValue3CollectionReturnValue: Value3! + var getValue3CollectionClosure: (() -> Value3)? + + func getValue3() -> Value3 { + getValue3CollectionCallsCount += 1 + if let getValue3CollectionClosure = getValue3CollectionClosure { + return getValue3CollectionClosure() + } else { + return getValue3CollectionReturnValue + } + } + + //MARK: - getValue5 + + var getValue5SequenceCallsCount = 0 + var getValue5SequenceCalled: Bool { + return getValue5SequenceCallsCount > 0 + } + var getValue5SequenceReturnValue: Value5! + var getValue5SequenceClosure: (() -> Value5)? + + func getValue5() -> Value5 { + getValue5SequenceCallsCount += 1 + if let getValue5SequenceClosure = getValue5SequenceClosure { + return getValue5SequenceClosure() + } else { + return getValue5SequenceReturnValue + } + } + + //MARK: - getValue6 + + var getValue6SequenceCallsCount = 0 + var getValue6SequenceCalled: Bool { + return getValue6SequenceCallsCount > 0 + } + var getValue6SequenceReturnValue: Value6! + var getValue6SequenceClosure: (() -> Value6)? + + func getValue6() -> Value6 { + getValue6SequenceCallsCount += 1 + if let getValue6SequenceClosure = getValue6SequenceClosure { + return getValue6SequenceClosure() + } else { + return getValue6SequenceReturnValue + } + } + + } class ThrowableProtocolMock: ThrowableProtocol { diff --git a/Templates/Tests/Generated/AutoMockable.generated.swift b/Templates/Tests/Generated/AutoMockable.generated.swift index 5c596bda8..6218727a4 100644 --- a/Templates/Tests/Generated/AutoMockable.generated.swift +++ b/Templates/Tests/Generated/AutoMockable.generated.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.2.4 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT // swiftlint:disable line_length // swiftlint:disable variable_name @@ -31,6 +31,7 @@ import AppKit + public class AccessLevelProtocolMock: AccessLevelProtocol { @@ -1644,6 +1645,109 @@ class SubscriptProtocolMock: SubscriptProtocol { subscript(arg: String) -> T? where T : Cancellable { get throws { fatalError("Subscripts are not fully supported yet") } } +} +class TestProtocolMock< + Value: Sequence, + Value3: Collection, + Value5: Sequence, + Value6: Sequence>: TestProtocol + where Value.Element : Collection,Value.Element : Hashable,Value.Element : Comparable,Value3.Element == String,Value5.Element == Int,Value6.Element == Int,Value6.Element : Hashable,Value6.Element : Comparable { + typealias Value2 = Int + + + + + //MARK: - getValue + + var getValueSequenceCallsCount = 0 + var getValueSequenceCalled: Bool { + return getValueSequenceCallsCount > 0 + } + var getValueSequenceReturnValue: Value! + var getValueSequenceClosure: (() -> Value)? + + func getValue() -> Value { + getValueSequenceCallsCount += 1 + if let getValueSequenceClosure = getValueSequenceClosure { + return getValueSequenceClosure() + } else { + return getValueSequenceReturnValue + } + } + + //MARK: - getValue2 + + var getValue2Value2CallsCount = 0 + var getValue2Value2Called: Bool { + return getValue2Value2CallsCount > 0 + } + var getValue2Value2ReturnValue: Value2! + var getValue2Value2Closure: (() -> Value2)? + + func getValue2() -> Value2 { + getValue2Value2CallsCount += 1 + if let getValue2Value2Closure = getValue2Value2Closure { + return getValue2Value2Closure() + } else { + return getValue2Value2ReturnValue + } + } + + //MARK: - getValue3 + + var getValue3CollectionCallsCount = 0 + var getValue3CollectionCalled: Bool { + return getValue3CollectionCallsCount > 0 + } + var getValue3CollectionReturnValue: Value3! + var getValue3CollectionClosure: (() -> Value3)? + + func getValue3() -> Value3 { + getValue3CollectionCallsCount += 1 + if let getValue3CollectionClosure = getValue3CollectionClosure { + return getValue3CollectionClosure() + } else { + return getValue3CollectionReturnValue + } + } + + //MARK: - getValue5 + + var getValue5SequenceCallsCount = 0 + var getValue5SequenceCalled: Bool { + return getValue5SequenceCallsCount > 0 + } + var getValue5SequenceReturnValue: Value5! + var getValue5SequenceClosure: (() -> Value5)? + + func getValue5() -> Value5 { + getValue5SequenceCallsCount += 1 + if let getValue5SequenceClosure = getValue5SequenceClosure { + return getValue5SequenceClosure() + } else { + return getValue5SequenceReturnValue + } + } + + //MARK: - getValue6 + + var getValue6SequenceCallsCount = 0 + var getValue6SequenceCalled: Bool { + return getValue6SequenceCallsCount > 0 + } + var getValue6SequenceReturnValue: Value6! + var getValue6SequenceClosure: (() -> Value6)? + + func getValue6() -> Value6 { + getValue6SequenceCallsCount += 1 + if let getValue6SequenceClosure = getValue6SequenceClosure { + return getValue6SequenceClosure() + } else { + return getValue6SequenceReturnValue + } + } + + } class ThrowableProtocolMock: ThrowableProtocol {