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

Add generic requirements to Method #1284

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ extension GenericRequirement {
convenience init(_ node: SameTypeRequirementSyntax) {
let leftType = node.leftTypeIdentifier.description.trimmed
let rightTypeName = TypeName(node.rightTypeIdentifier.description.trimmed)
let rightType = Type(name: rightTypeName.unwrappedTypeName)
let rightType = Type(name: rightTypeName.unwrappedTypeName, isGeneric: true)
let protocolType = SourceryProtocol(name: rightTypeName.unwrappedTypeName, implements: [rightTypeName.unwrappedTypeName: rightType])
self.init(leftType: .init(name: leftType), rightType: .init(typeName: rightTypeName, type: protocolType), relationship: .equals)
}

convenience init(_ node: ConformanceRequirementSyntax) {
let leftType = node.leftTypeIdentifier.description.trimmed
let rightTypeName = TypeName(node.rightTypeIdentifier.description.trimmed)
let rightType = Type(name: rightTypeName.unwrappedTypeName)
let rightType = Type(name: rightTypeName.unwrappedTypeName, isGeneric: true)
let protocolType = SourceryProtocol(name: rightTypeName.unwrappedTypeName, implements: [rightTypeName.unwrappedTypeName: rightType])
self.init(leftType: .init(name: leftType), rightType: .init(typeName: rightTypeName, type: protocolType), relationship: .conformsTo)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,16 @@ extension SourceryMethod {
if let generics = genericParameterClause?.genericParameterList {
fullName = funcName + "<\(generics.description.trimmed)>"
}

var genericRequirements: [GenericRequirement] = []
if let genericWhereClause = genericWhereClause {
// TODO: add generic requirement to method
genericRequirements = genericWhereClause.requirementList.compactMap { requirement in
if let sameType = requirement.body.as(SameTypeRequirementSyntax.self) {
return GenericRequirement(sameType)
} else if let conformanceType = requirement.body.as(ConformanceRequirementSyntax.self) {
return GenericRequirement(conformanceType)
}
return nil
}
// TODO: TBR
returnTypeName = TypeName(name: returnTypeName.name + " \(genericWhereClause.withoutTrivia().description.trimmed)",
unwrappedTypeName: returnTypeName.unwrappedTypeName,
Expand Down Expand Up @@ -135,7 +142,8 @@ extension SourceryMethod {
modifiers: modifiers.map(SourceryModifier.init),
annotations: annotations,
documentation: documentation,
definedInTypeName: typeName
definedInTypeName: typeName,
genericRequirements: genericRequirements
)
}

Expand Down
21 changes: 18 additions & 3 deletions SourceryRuntime/Sources/AST/Method.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
/// :nodoc:
public var __parserData: Any?

/// list of generic requirements
public var genericRequirements: [GenericRequirement]

/// :nodoc:
public init(name: String,
selectorName: String? = nil,
Expand All @@ -188,8 +191,8 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
modifiers: [SourceryModifier] = [],
annotations: [String: NSObject] = [:],
documentation: [String] = [],
definedInTypeName: TypeName? = nil) {

definedInTypeName: TypeName? = nil,
genericRequirements: [GenericRequirement] = []) {
self.name = name
self.selectorName = selectorName ?? name
self.parameters = parameters
Expand All @@ -206,6 +209,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
self.annotations = annotations
self.documentation = documentation
self.definedInTypeName = definedInTypeName
self.genericRequirements = genericRequirements
}

/// :nodoc:
Expand All @@ -226,7 +230,8 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
string += "documentation = \(String(describing: self.documentation)), "
string += "definedInTypeName = \(String(describing: self.definedInTypeName)), "
string += "attributes = \(String(describing: self.attributes)), "
string += "modifiers = \(String(describing: self.modifiers))"
string += "modifiers = \(String(describing: self.modifiers)), "
string += "genericRequirements = \(String(describing: self.genericRequirements))"
return string
}

Expand All @@ -252,6 +257,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
results.append(contentsOf: DiffableResult(identifier: "definedInTypeName").trackDifference(actual: self.definedInTypeName, expected: castObject.definedInTypeName))
results.append(contentsOf: DiffableResult(identifier: "attributes").trackDifference(actual: self.attributes, expected: castObject.attributes))
results.append(contentsOf: DiffableResult(identifier: "modifiers").trackDifference(actual: self.modifiers, expected: castObject.modifiers))
results.append(contentsOf: DiffableResult(identifier: "genericRequirements").trackDifference(actual: self.genericRequirements, expected: castObject.genericRequirements))
return results
}

Expand All @@ -273,6 +279,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
hasher.combine(self.definedInTypeName)
hasher.combine(self.attributes)
hasher.combine(self.modifiers)
hasher.combine(self.genericRequirements)
return hasher.finalize()
}

Expand All @@ -295,6 +302,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
if self.definedInTypeName != rhs.definedInTypeName { return false }
if self.attributes != rhs.attributes { return false }
if self.modifiers != rhs.modifiers { return false }
if self.genericRequirements != rhs.genericRequirements { return false }
return true
}

Expand Down Expand Up @@ -365,6 +373,12 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
}
fatalError()
}; self.modifiers = modifiers
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
}

/// :nodoc:
Expand All @@ -387,6 +401,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
aCoder.encode(self.definedInType, forKey: "definedInType")
aCoder.encode(self.attributes, forKey: "attributes")
aCoder.encode(self.modifiers, forKey: "modifiers")
aCoder.encode(self.genericRequirements, forKey: "genericRequirements")
}
// sourcery:end
}
Expand Down
21 changes: 19 additions & 2 deletions SourceryRuntime/Sources/AST/Method_Linux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
return actualReturnTypeName
case "isDynamic":
return isDynamic
case "genericRequirements":
return genericRequirements
default:
fatalError("unable to lookup: \(member) in \(self)")
}
Expand Down Expand Up @@ -213,6 +215,9 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
/// :nodoc:
public var __parserData: Any?

/// list of generic requirements
public var genericRequirements: [GenericRequirement]

/// :nodoc:
public init(name: String,
selectorName: String? = nil,
Expand All @@ -229,8 +234,8 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
modifiers: [SourceryModifier] = [],
annotations: [String: NSObject] = [:],
documentation: [String] = [],
definedInTypeName: TypeName? = nil) {

definedInTypeName: TypeName? = nil,
genericRequirements: [GenericRequirement] = []) {
self.name = name
self.selectorName = selectorName ?? name
self.parameters = parameters
Expand All @@ -247,6 +252,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
self.annotations = annotations
self.documentation = documentation
self.definedInTypeName = definedInTypeName
self.genericRequirements = genericRequirements
}

/// :nodoc:
Expand All @@ -268,6 +274,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
string += "definedInTypeName = \(String(describing: self.definedInTypeName)), "
string += "attributes = \(String(describing: self.attributes)), "
string += "modifiers = \(String(describing: self.modifiers))"
string += "genericRequirements = \(String(describing: self.genericRequirements))"
return string
}

Expand All @@ -293,6 +300,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
results.append(contentsOf: DiffableResult(identifier: "definedInTypeName").trackDifference(actual: self.definedInTypeName, expected: castObject.definedInTypeName))
results.append(contentsOf: DiffableResult(identifier: "attributes").trackDifference(actual: self.attributes, expected: castObject.attributes))
results.append(contentsOf: DiffableResult(identifier: "modifiers").trackDifference(actual: self.modifiers, expected: castObject.modifiers))
results.append(contentsOf: DiffableResult(identifier: "genericRequirements").trackDifference(actual: self.genericRequirements, expected: castObject.genericRequirements))
return results
}

Expand All @@ -314,6 +322,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
hasher.combine(self.definedInTypeName)
hasher.combine(self.attributes)
hasher.combine(self.modifiers)
hasher.combine(self.genericRequirements)
return hasher.finalize()
}

Expand All @@ -336,6 +345,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
if self.definedInTypeName != rhs.definedInTypeName { return false }
if self.attributes != rhs.attributes { return false }
if self.modifiers != rhs.modifiers { return false }
if self.genericRequirements != rhs.genericRequirements { return false }
return true
}

Expand Down Expand Up @@ -406,6 +416,12 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
}
fatalError()
}; self.modifiers = modifiers
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
}

/// :nodoc:
Expand All @@ -428,6 +444,7 @@ public final class Method: NSObject, SourceryModel, Annotated, Documented, Defin
aCoder.encode(self.definedInType, forKey: "definedInType")
aCoder.encode(self.attributes, forKey: "attributes")
aCoder.encode(self.modifiers, forKey: "modifiers")
aCoder.encode(self.genericRequirements, forKey: "genericRequirements")
}
// sourcery:end
}
Expand Down
17 changes: 11 additions & 6 deletions SourceryRuntime/Sources/Composer/Composer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ public enum Composer {
return 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.types.parallelPerform { type in
type.variables.forEach {
resolveVariableTypes($0, of: type, resolve: resolveType)
}
type.methods.forEach {
resolveMethodTypes($0, of: type, resolve: resolveType)
resolveMethodTypes($0, of: type, resolve: methodResolveType)
}
type.subscripts.forEach {
resolveSubscriptTypes($0, of: type, resolve: resolveType)
Expand All @@ -52,7 +56,7 @@ public enum Composer {
}

composed.functions.parallelPerform { function in
resolveMethodTypes(function, of: nil, resolve: resolveType)
resolveMethodTypes(function, of: nil, resolve: methodResolveType)
}

updateTypeRelationships(types: composed.types)
Expand All @@ -65,6 +69,7 @@ public enum Composer {
}

typealias TypeResolver = (TypeName, Type?) -> Type?
typealias MethodArgumentTypeResolver = (TypeName, Type?, Method) -> Type?

private static func resolveVariableTypes(_ variable: Variable, of type: Type, resolve: TypeResolver) {
variable.type = resolve(variable.typeName, type)
Expand All @@ -88,17 +93,17 @@ public enum Composer {
}
}

private static func resolveMethodTypes(_ method: SourceryMethod, of type: Type?, resolve: TypeResolver) {
private static func resolveMethodTypes(_ method: SourceryMethod, of type: Type?, resolve: MethodArgumentTypeResolver) {
method.parameters.forEach { parameter in
parameter.type = resolve(parameter.typeName, type)
parameter.type = resolve(parameter.typeName, type, method)
}

/// The actual `definedInType` is assigned in `uniqueTypes` but we still
/// need to resolve the type to correctly parse typealiases
/// @see https://github.com/krzysztofzablocki/Sourcery/pull/374
var definedInType: Type?
if let definedInTypeName = method.definedInTypeName {
definedInType = resolve(definedInTypeName, type)
definedInType = resolve(definedInTypeName, type, method)
}

guard !method.returnTypeName.isVoid else { return }
Expand All @@ -124,7 +129,7 @@ public enum Composer {
}
}
} else {
method.returnType = resolve(method.returnTypeName, type)
method.returnType = resolve(method.returnTypeName, type, method)
}
}

Expand Down
21 changes: 17 additions & 4 deletions SourceryRuntime/Sources/Composer/ParserResultsComposed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ internal struct ParserResultsComposed {
return modules[moduleName]?[typeName]
}

func resolveType(typeName: TypeName, containingType: Type?) -> Type? {
func resolveType(typeName: TypeName, containingType: Type?, method: Method? = nil) -> Type? {
let resolveTypeWithName = { (typeName: TypeName) -> Type? in
return self.resolveType(typeName: typeName, containingType: containingType)
}
Expand Down Expand Up @@ -460,11 +460,23 @@ internal struct ParserResultsComposed {
typeName.actualTypeName = aliasedName
}

if let genericRequirements = containingType?.genericRequirements {
let hasGenericRequirements = containingType?.genericRequirements.isEmpty == false
|| (method != nil && method?.genericRequirements.isEmpty == false)

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