Skip to content

Commit

Permalink
Added generic requirements and generic parameters to Subscript (#1242)
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-savelev-bumble authored Jan 4, 2024
1 parent 23179e8 commit fe41343
Show file tree
Hide file tree
Showing 25 changed files with 1,322 additions and 390 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## Changes
- Add support for `typealias`es in EJS templates. ([#1208](https://github.com/krzysztofzablocki/Sourcery/pull/1208))
- Add support for existential to Automockable Protocol with generic types. ([#1220](https://github.com/krzysztofzablocki/Sourcery/pull/1220))
- Add support for generic parameters and requirements in subscripts.
([#1242](https://github.com/krzysztofzablocki/Sourcery/pull/1242))
- Throw throwable error after updating mocks's calls counts and received parameters/invocations.
([#1224](https://github.com/krzysztofzablocki/Sourcery/pull/1224))
- Fix unit tests on Linux ([#1225](https://github.com/krzysztofzablocki/Sourcery/pull/1225))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation
import SwiftSyntax
import SourceryRuntime

extension GenericParameter {
convenience init(_ node: GenericParameterSyntax) {
self.init(name: node.name.description.trimmed, inheritedTypeName: node.inheritedType.flatMap(TypeName.init(_:)))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,25 @@ extension Subscript {
readAccess = parentAccess
}

let genericParameters = node.genericParameterClause?.genericParameterList.compactMap { parameter in
return GenericParameter(parameter)
} ?? []

let genericRequirements: [GenericRequirement] = node.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
} ?? []

self.init(
parameters: node.indices.parameterList.map { MethodParameter($0, annotationsParser: annotationsParser) },
returnTypeName: TypeName(node.result.returnType.description.trimmed),
accessLevel: (read: readAccess, write: isWritable ? writeAccess : .none),
genericParameters: genericParameters,
genericRequirements: genericRequirements,
attributes: Attribute.from(node.attributes),
modifiers: modifiers.map(SourceryModifier.init),
annotations: node.firstToken.map { annotationsParser.annotations(fromToken: $0) } ?? [:],
Expand Down
4 changes: 2 additions & 2 deletions SourceryRuntime/Sources/AST/AssociatedType.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#if canImport(ObjectiveC)
import Foundation

/// Describes Swift AssociatedType
#if canImport(ObjectiveC)
@objcMembers
#endif
public final class AssociatedType: NSObject, SourceryModel {
/// Associated type name
public let name: String
Expand Down Expand Up @@ -78,3 +77,4 @@ public final class AssociatedType: NSObject, SourceryModel {
}
// sourcery:end
}
#endif
96 changes: 96 additions & 0 deletions SourceryRuntime/Sources/AST/AssociatedType_Linux.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#if !canImport(ObjectiveC)
import Foundation
// For DynamicMemberLookup we need to import Stencil,
// however, this is different from SourceryRuntime.content.generated.swift, because
// it cannot reference Stencil
import Stencil

/// Describes Swift AssociatedType
public final class AssociatedType: NSObject, SourceryModel, DynamicMemberLookup {
public subscript(dynamicMember member: String) -> Any? {
switch member {
case "name":
return name
case "typeName":
return typeName
case "type":
return type
default:
fatalError("unable to lookup: \(member) in \(self)")
}
}

/// Associated type name
public let name: String

/// Associated type type constraint name, if specified
public let typeName: TypeName?

// sourcery: skipEquality, skipDescription
/// Associated type constrained type, if known, i.e. if the type is declared in the scanned sources.
public var type: Type?

/// :nodoc:
public init(name: String, typeName: TypeName? = nil, type: Type? = nil) {
self.name = name
self.typeName = typeName
self.type = type
}

/// :nodoc:
override public var description: String {
var string = "\(Swift.type(of: self)): "
string += "name = \(String(describing: self.name)), "
string += "typeName = \(String(describing: self.typeName))"
return string
}

public func diffAgainst(_ object: Any?) -> DiffableResult {
let results = DiffableResult()
guard let castObject = object as? AssociatedType else {
results.append("Incorrect type <expected: AssociatedType, received: \(Swift.type(of: object))>")
return results
}
results.append(contentsOf: DiffableResult(identifier: "name").trackDifference(actual: self.name, expected: castObject.name))
results.append(contentsOf: DiffableResult(identifier: "typeName").trackDifference(actual: self.typeName, expected: castObject.typeName))
return results
}

public override var hash: Int {
var hasher = Hasher()
hasher.combine(self.name)
hasher.combine(self.typeName)
return hasher.finalize()
}

/// :nodoc:
public override func isEqual(_ object: Any?) -> Bool {
guard let rhs = object as? AssociatedType else { return false }
if self.name != rhs.name { return false }
if self.typeName != rhs.typeName { return false }
return true
}

// sourcery:inline:AssociatedType.AutoCoding

/// :nodoc:
required public init?(coder aDecoder: NSCoder) {
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
self.typeName = aDecoder.decode(forKey: "typeName")
self.type = aDecoder.decode(forKey: "type")
}

/// :nodoc:
public func encode(with aCoder: NSCoder) {
aCoder.encode(self.name, forKey: "name")
aCoder.encode(self.typeName, forKey: "typeName")
aCoder.encode(self.type, forKey: "type")
}
// sourcery:end
}
#endif
75 changes: 75 additions & 0 deletions SourceryRuntime/Sources/AST/GenericParameter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#if canImport(ObjectiveC)
import Foundation

/// Descibes Swift generic parameter
@objcMembers
public final class GenericParameter: NSObject, SourceryModel, Diffable {

/// Generic parameter name
public var name: String

/// Generic parameter inherited type
public var inheritedTypeName: TypeName?

/// :nodoc:
public init(name: String, inheritedTypeName: TypeName? = nil) {
self.name = name
self.inheritedTypeName = inheritedTypeName
}

/// :nodoc:
override public var description: String {
var string = "\(Swift.type(of: self)): "
string += "name = \(String(describing: self.name)), "
string += "inheritedTypeName = \(String(describing: self.inheritedTypeName))"
return string
}

public func diffAgainst(_ object: Any?) -> DiffableResult {
let results = DiffableResult()
guard let castObject = object as? GenericParameter else {
results.append("Incorrect type <expected: GenericParameter, received: \(Swift.type(of: object))>")
return results
}
results.append(contentsOf: DiffableResult(identifier: "name").trackDifference(actual: self.name, expected: castObject.name))
results.append(contentsOf: DiffableResult(identifier: "inheritedTypeName").trackDifference(actual: self.inheritedTypeName, expected: castObject.inheritedTypeName))
return results
}

public override var hash: Int {
var hasher = Hasher()
hasher.combine(self.name)
hasher.combine(self.inheritedTypeName)
return hasher.finalize()
}

/// :nodoc:
public override func isEqual(_ object: Any?) -> Bool {
guard let rhs = object as? GenericParameter else { return false }
if self.name != rhs.name { return false }
if self.inheritedTypeName != rhs.inheritedTypeName { return false }
return true
}

// sourcery:inline:GenericParameter.AutoCoding

/// :nodoc:
required public init?(coder aDecoder: NSCoder) {
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
self.inheritedTypeName = aDecoder.decode(forKey: "inheritedTypeName")
}

/// :nodoc:
public func encode(with aCoder: NSCoder) {
aCoder.encode(self.name, forKey: "name")
aCoder.encode(self.inheritedTypeName, forKey: "inheritedTypeName")
}

// sourcery:end
}
#endif
88 changes: 88 additions & 0 deletions SourceryRuntime/Sources/AST/GenericParameter_Linux.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#if !canImport(ObjectiveC)
import Foundation
// For DynamicMemberLookup we need to import Stencil,
// however, this is different from SourceryRuntime.content.generated.swift, because
// it cannot reference Stencil
import Stencil

/// Descibes Swift generic parameter
public final class GenericParameter: NSObject, SourceryModel, Diffable, DynamicMemberLookup {
public subscript(dynamicMember member: String) -> Any? {
switch member {
case "name":
return name
case "inheritedTypeName":
return inheritedTypeName
default:
fatalError("unable to lookup: \(member) in \(self)")
}
}

/// Generic parameter name
public var name: String

/// Generic parameter inherited type
public var inheritedTypeName: TypeName?

/// :nodoc:
public init(name: String, inheritedTypeName: TypeName? = nil) {
self.name = name
self.inheritedTypeName = inheritedTypeName
}

/// :nodoc:
override public var description: String {
var string = "\(Swift.type(of: self)): "
string += "name = \(String(describing: self.name)), "
string += "inheritedTypeName = \(String(describing: self.inheritedTypeName))"
return string
}

public func diffAgainst(_ object: Any?) -> DiffableResult {
let results = DiffableResult()
guard let castObject = object as? GenericParameter else {
results.append("Incorrect type <expected: GenericParameter, received: \(Swift.type(of: object))>")
return results
}
results.append(contentsOf: DiffableResult(identifier: "name").trackDifference(actual: self.name, expected: castObject.name))
results.append(contentsOf: DiffableResult(identifier: "inheritedTypeName").trackDifference(actual: self.inheritedTypeName, expected: castObject.inheritedTypeName))
return results
}

public override var hash: Int {
var hasher = Hasher()
hasher.combine(self.name)
hasher.combine(self.inheritedTypeName)
return hasher.finalize()
}

/// :nodoc:
public override func isEqual(_ object: Any?) -> Bool {
guard let rhs = object as? GenericParameter else { return false }
if self.name != rhs.name { return false }
if self.inheritedTypeName != rhs.inheritedTypeName { return false }
return true
}

// sourcery:inline:GenericParameter.AutoCoding

/// :nodoc:
required public init?(coder aDecoder: NSCoder) {
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
self.inheritedTypeName = aDecoder.decode(forKey: "inheritedTypeName")
}

/// :nodoc:
public func encode(with aCoder: NSCoder) {
aCoder.encode(self.name, forKey: "name")
aCoder.encode(self.inheritedTypeName, forKey: "inheritedTypeName")
}

// sourcery:end
}
#endif
7 changes: 3 additions & 4 deletions SourceryRuntime/Sources/AST/GenericRequirement.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#if canImport(ObjectiveC)
import Foundation

/// modifier can be thing like `private`, `class`, `nonmutating`
/// if a declaration has modifier like `private(set)` it's name will be `private` and detail will be `set`
#if canImport(ObjectiveC)
/// Descibes Swift generic requirement
@objcMembers
#endif
public class GenericRequirement: NSObject, SourceryModel, Diffable {

public enum Relationship: String {
Expand Down Expand Up @@ -118,3 +116,4 @@ public class GenericRequirement: NSObject, SourceryModel, Diffable {
}
// sourcery:end
}
#endif
Loading

0 comments on commit fe41343

Please sign in to comment.