mirror of
https://github.com/krzysztofzablocki/Sourcery.git
synced 2026-04-07 19:17:40 +00:00
7153768c65
* Preliminary associatedtype support
* Implemented associatedtype support with generic requirements
* Fixed failing test
* Squashed commit of the following:
commit 9d01e6f99a
Author: Ruslan A <r.alikhamov@gmail.com>
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
480 lines
23 KiB
Swift
480 lines
23 KiB
Swift
#if !canImport(ObjectiveC)
|
|
import Foundation
|
|
|
|
/// :nodoc:
|
|
public typealias SourceryMethod = Method
|
|
|
|
/// Describes method
|
|
public final class Method: NSObject, SourceryModel, Annotated, Documented, Definition, Diffable, SourceryDynamicMemberLookup {
|
|
public subscript(dynamicMember member: String) -> Any? {
|
|
switch member {
|
|
case "definedInType":
|
|
return definedInType
|
|
case "shortName":
|
|
return shortName
|
|
case "name":
|
|
return name
|
|
case "selectorName":
|
|
return selectorName
|
|
case "callName":
|
|
return callName
|
|
case "parameters":
|
|
return parameters
|
|
case "throws":
|
|
return `throws`
|
|
case "isInitializer":
|
|
return isInitializer
|
|
case "accessLevel":
|
|
return accessLevel
|
|
case "isStatic":
|
|
return isStatic
|
|
case "returnTypeName":
|
|
return returnTypeName
|
|
case "isAsync":
|
|
return isAsync
|
|
case "attributes":
|
|
return attributes
|
|
case "isOptionalReturnType":
|
|
return isOptionalReturnType
|
|
case "actualReturnTypeName":
|
|
return actualReturnTypeName
|
|
case "isDynamic":
|
|
return isDynamic
|
|
case "genericRequirements":
|
|
return genericRequirements
|
|
default:
|
|
fatalError("unable to lookup: \(member) in \(self)")
|
|
}
|
|
}
|
|
|
|
/// Full method name, including generic constraints, i.e. `foo<T>(bar: T)`
|
|
public let name: String
|
|
|
|
/// Method name including arguments names, i.e. `foo(bar:)`
|
|
public var selectorName: String
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Method name without arguments names and parentheses, i.e. `foo<T>`
|
|
public var shortName: String {
|
|
return name.range(of: "(").map({ String(name[..<$0.lowerBound]) }) ?? name
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Method name without arguments names, parentheses and generic types, i.e. `foo` (can be used to generate code for method call)
|
|
public var callName: String {
|
|
return shortName.range(of: "<").map({ String(shortName[..<$0.lowerBound]) }) ?? shortName
|
|
}
|
|
|
|
/// Method parameters
|
|
public var parameters: [MethodParameter]
|
|
|
|
/// Return value type name used in declaration, including generic constraints, i.e. `where T: Equatable`
|
|
public var returnTypeName: TypeName
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Actual return value type name if declaration uses typealias, otherwise just a `returnTypeName`
|
|
public var actualReturnTypeName: TypeName {
|
|
return returnTypeName.actualTypeName ?? returnTypeName
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Actual return value type, if known
|
|
public var returnType: Type?
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether return value type is optional
|
|
public var isOptionalReturnType: Bool {
|
|
return returnTypeName.isOptional || isFailableInitializer
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether return value type is implicitly unwrapped optional
|
|
public var isImplicitlyUnwrappedOptionalReturnType: Bool {
|
|
return returnTypeName.isImplicitlyUnwrappedOptional
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Return value type name without attributes and optional type information
|
|
public var unwrappedReturnTypeName: String {
|
|
return returnTypeName.unwrappedTypeName
|
|
}
|
|
|
|
/// Whether method is async method
|
|
public let isAsync: Bool
|
|
|
|
/// Whether method is distributed
|
|
public var isDistributed: Bool {
|
|
modifiers.contains(where: { $0.name == "distributed" })
|
|
}
|
|
|
|
/// Whether method throws
|
|
public let `throws`: Bool
|
|
|
|
/// Whether method rethrows
|
|
public let `rethrows`: Bool
|
|
|
|
/// Method access level, i.e. `internal`, `private`, `fileprivate`, `public`, `open`
|
|
public let accessLevel: String
|
|
|
|
/// Whether method is a static method
|
|
public let isStatic: Bool
|
|
|
|
/// Whether method is a class method
|
|
public let isClass: Bool
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is an initializer
|
|
public var isInitializer: Bool {
|
|
return selectorName.hasPrefix("init(") || selectorName == "init"
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is an deinitializer
|
|
public var isDeinitializer: Bool {
|
|
return selectorName == "deinit"
|
|
}
|
|
|
|
/// Whether method is a failable initializer
|
|
public let isFailableInitializer: Bool
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is a convenience initializer
|
|
public var isConvenienceInitializer: Bool {
|
|
modifiers.contains { $0.name == Attribute.Identifier.convenience.rawValue }
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is required
|
|
public var isRequired: Bool {
|
|
modifiers.contains { $0.name == Attribute.Identifier.required.rawValue }
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is final
|
|
public var isFinal: Bool {
|
|
modifiers.contains { $0.name == Attribute.Identifier.final.rawValue }
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is mutating
|
|
public var isMutating: Bool {
|
|
modifiers.contains { $0.name == Attribute.Identifier.mutating.rawValue }
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is generic
|
|
public var isGeneric: Bool {
|
|
shortName.hasSuffix(">")
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is optional (in an Objective-C protocol)
|
|
public var isOptional: Bool {
|
|
modifiers.contains { $0.name == Attribute.Identifier.optional.rawValue }
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is nonisolated (this modifier only applies to actor methods)
|
|
public var isNonisolated: Bool {
|
|
modifiers.contains { $0.name == Attribute.Identifier.nonisolated.rawValue }
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Whether method is dynamic
|
|
public var isDynamic: Bool {
|
|
modifiers.contains { $0.name == Attribute.Identifier.dynamic.rawValue }
|
|
}
|
|
|
|
/// Annotations, that were created with // sourcery: annotation1, other = "annotation value", alterantive = 2
|
|
public let annotations: Annotations
|
|
|
|
public let documentation: Documentation
|
|
|
|
/// Reference to type name where the method is defined,
|
|
/// nil if defined outside of any `enum`, `struct`, `class` etc
|
|
public let definedInTypeName: TypeName?
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Reference to actual type name where the method is defined if declaration uses typealias, otherwise just a `definedInTypeName`
|
|
public var actualDefinedInTypeName: TypeName? {
|
|
return definedInTypeName?.actualTypeName ?? definedInTypeName
|
|
}
|
|
|
|
// sourcery: skipEquality, skipDescription
|
|
/// Reference to actual type where the object is defined,
|
|
/// nil if defined outside of any `enum`, `struct`, `class` etc or type is unknown
|
|
public var definedInType: Type?
|
|
|
|
/// Method attributes, i.e. `@discardableResult`
|
|
public let attributes: AttributeList
|
|
|
|
/// Method modifiers, i.e. `private`
|
|
public let modifiers: [SourceryModifier]
|
|
|
|
// Underlying parser data, never to be used by anything else
|
|
// sourcery: skipEquality, skipDescription, skipCoding, skipJSExport
|
|
/// :nodoc:
|
|
public var __parserData: Any?
|
|
|
|
/// list of generic requirements
|
|
public var genericRequirements: [GenericRequirement]
|
|
|
|
/// List of generic parameters
|
|
///
|
|
/// - Example:
|
|
///
|
|
/// ```swift
|
|
/// func method<GenericParameter>(foo: GenericParameter)
|
|
/// ^ ~ a generic parameter
|
|
/// ```
|
|
public var genericParameters: [GenericParameter]
|
|
|
|
/// :nodoc:
|
|
public init(name: String,
|
|
selectorName: String? = nil,
|
|
parameters: [MethodParameter] = [],
|
|
returnTypeName: TypeName = TypeName(name: "Void"),
|
|
isAsync: Bool = false,
|
|
throws: Bool = false,
|
|
rethrows: Bool = false,
|
|
accessLevel: AccessLevel = .internal,
|
|
isStatic: Bool = false,
|
|
isClass: Bool = false,
|
|
isFailableInitializer: Bool = false,
|
|
attributes: AttributeList = [:],
|
|
modifiers: [SourceryModifier] = [],
|
|
annotations: [String: NSObject] = [:],
|
|
documentation: [String] = [],
|
|
definedInTypeName: TypeName? = nil,
|
|
genericRequirements: [GenericRequirement] = [],
|
|
genericParameters: [GenericParameter] = []) {
|
|
self.name = name
|
|
self.selectorName = selectorName ?? name
|
|
self.parameters = parameters
|
|
self.returnTypeName = returnTypeName
|
|
self.isAsync = isAsync
|
|
self.throws = `throws`
|
|
self.rethrows = `rethrows`
|
|
self.accessLevel = accessLevel.rawValue
|
|
self.isStatic = isStatic
|
|
self.isClass = isClass
|
|
self.isFailableInitializer = isFailableInitializer
|
|
self.attributes = attributes
|
|
self.modifiers = modifiers
|
|
self.annotations = annotations
|
|
self.documentation = documentation
|
|
self.definedInTypeName = definedInTypeName
|
|
self.genericRequirements = genericRequirements
|
|
self.genericParameters = genericParameters
|
|
}
|
|
|
|
/// :nodoc:
|
|
// sourcery: skipJSExport
|
|
override public var description: String {
|
|
var string = "\(Swift.type(of: self)): "
|
|
string.append("name = \(String(describing: self.name)), ")
|
|
string.append("selectorName = \(String(describing: self.selectorName)), ")
|
|
string.append("parameters = \(String(describing: self.parameters)), ")
|
|
string.append("returnTypeName = \(String(describing: self.returnTypeName)), ")
|
|
string.append("isAsync = \(String(describing: self.isAsync)), ")
|
|
string.append("`throws` = \(String(describing: self.`throws`)), ")
|
|
string.append("`rethrows` = \(String(describing: self.`rethrows`)), ")
|
|
string.append("accessLevel = \(String(describing: self.accessLevel)), ")
|
|
string.append("isStatic = \(String(describing: self.isStatic)), ")
|
|
string.append("isClass = \(String(describing: self.isClass)), ")
|
|
string.append("isFailableInitializer = \(String(describing: self.isFailableInitializer)), ")
|
|
string.append("annotations = \(String(describing: self.annotations)), ")
|
|
string.append("documentation = \(String(describing: self.documentation)), ")
|
|
string.append("definedInTypeName = \(String(describing: self.definedInTypeName)), ")
|
|
string.append("attributes = \(String(describing: self.attributes)), ")
|
|
string.append("modifiers = \(String(describing: self.modifiers)), ")
|
|
string.append("genericRequirements = \(String(describing: self.genericRequirements)), ")
|
|
string.append("genericRequirements = \(String(describing: self.genericParameters))")
|
|
return string
|
|
}
|
|
|
|
public func diffAgainst(_ object: Any?) -> DiffableResult {
|
|
let results = DiffableResult()
|
|
guard let castObject = object as? Method else {
|
|
results.append("Incorrect type <expected: Method, 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: "selectorName").trackDifference(actual: self.selectorName, expected: castObject.selectorName))
|
|
results.append(contentsOf: DiffableResult(identifier: "parameters").trackDifference(actual: self.parameters, expected: castObject.parameters))
|
|
results.append(contentsOf: DiffableResult(identifier: "returnTypeName").trackDifference(actual: self.returnTypeName, expected: castObject.returnTypeName))
|
|
results.append(contentsOf: DiffableResult(identifier: "isAsync").trackDifference(actual: self.isAsync, expected: castObject.isAsync))
|
|
results.append(contentsOf: DiffableResult(identifier: "`throws`").trackDifference(actual: self.`throws`, expected: castObject.`throws`))
|
|
results.append(contentsOf: DiffableResult(identifier: "`rethrows`").trackDifference(actual: self.`rethrows`, expected: castObject.`rethrows`))
|
|
results.append(contentsOf: DiffableResult(identifier: "accessLevel").trackDifference(actual: self.accessLevel, expected: castObject.accessLevel))
|
|
results.append(contentsOf: DiffableResult(identifier: "isStatic").trackDifference(actual: self.isStatic, expected: castObject.isStatic))
|
|
results.append(contentsOf: DiffableResult(identifier: "isClass").trackDifference(actual: self.isClass, expected: castObject.isClass))
|
|
results.append(contentsOf: DiffableResult(identifier: "isFailableInitializer").trackDifference(actual: self.isFailableInitializer, expected: castObject.isFailableInitializer))
|
|
results.append(contentsOf: DiffableResult(identifier: "annotations").trackDifference(actual: self.annotations, expected: castObject.annotations))
|
|
results.append(contentsOf: DiffableResult(identifier: "documentation").trackDifference(actual: self.documentation, expected: castObject.documentation))
|
|
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))
|
|
results.append(contentsOf: DiffableResult(identifier: "genericParameters").trackDifference(actual: self.genericParameters, expected: castObject.genericParameters))
|
|
return results
|
|
}
|
|
|
|
/// :nodoc:
|
|
// sourcery: skipJSExport
|
|
public override var hash: Int {
|
|
var hasher = Hasher()
|
|
hasher.combine(self.name)
|
|
hasher.combine(self.selectorName)
|
|
hasher.combine(self.parameters)
|
|
hasher.combine(self.returnTypeName)
|
|
hasher.combine(self.isAsync)
|
|
hasher.combine(self.`throws`)
|
|
hasher.combine(self.`rethrows`)
|
|
hasher.combine(self.accessLevel)
|
|
hasher.combine(self.isStatic)
|
|
hasher.combine(self.isClass)
|
|
hasher.combine(self.isFailableInitializer)
|
|
hasher.combine(self.annotations)
|
|
hasher.combine(self.documentation)
|
|
hasher.combine(self.definedInTypeName)
|
|
hasher.combine(self.attributes)
|
|
hasher.combine(self.modifiers)
|
|
hasher.combine(self.genericRequirements)
|
|
hasher.combine(self.genericParameters)
|
|
return hasher.finalize()
|
|
}
|
|
|
|
/// :nodoc:
|
|
public override func isEqual(_ object: Any?) -> Bool {
|
|
guard let rhs = object as? Method else { return false }
|
|
if self.name != rhs.name { return false }
|
|
if self.selectorName != rhs.selectorName { return false }
|
|
if self.parameters != rhs.parameters { return false }
|
|
if self.returnTypeName != rhs.returnTypeName { return false }
|
|
if self.isAsync != rhs.isAsync { return false }
|
|
if self.isDistributed != rhs.isDistributed { return false }
|
|
if self.`throws` != rhs.`throws` { return false }
|
|
if self.`rethrows` != rhs.`rethrows` { return false }
|
|
if self.accessLevel != rhs.accessLevel { return false }
|
|
if self.isStatic != rhs.isStatic { return false }
|
|
if self.isClass != rhs.isClass { return false }
|
|
if self.isFailableInitializer != rhs.isFailableInitializer { return false }
|
|
if self.annotations != rhs.annotations { return false }
|
|
if self.documentation != rhs.documentation { return false }
|
|
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 }
|
|
if self.genericParameters != rhs.genericParameters { return false }
|
|
return true
|
|
}
|
|
|
|
// sourcery:inline:Method.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
|
|
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 {
|
|
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 {
|
|
withVaList(["returnTypeName"]) { arguments in
|
|
NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments)
|
|
}
|
|
fatalError()
|
|
}; self.returnTypeName = returnTypeName
|
|
self.returnType = aDecoder.decode(forKey: "returnType")
|
|
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 {
|
|
withVaList(["accessLevel"]) { arguments in
|
|
NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments)
|
|
}
|
|
fatalError()
|
|
}; self.accessLevel = accessLevel
|
|
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 {
|
|
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 {
|
|
withVaList(["documentation"]) { arguments in
|
|
NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments)
|
|
}
|
|
fatalError()
|
|
}; self.documentation = documentation
|
|
self.definedInTypeName = aDecoder.decode(forKey: "definedInTypeName")
|
|
self.definedInType = aDecoder.decode(forKey: "definedInType")
|
|
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 {
|
|
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 {
|
|
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 {
|
|
withVaList(["genericParameters"]) { arguments in
|
|
NSException.raise(NSExceptionName.parseErrorException, format: "Key '%@' not found.", arguments: arguments)
|
|
}
|
|
fatalError()
|
|
}; self.genericParameters = genericParameters
|
|
}
|
|
|
|
/// :nodoc:
|
|
public func encode(with aCoder: NSCoder) {
|
|
aCoder.encode(self.name, forKey: "name")
|
|
aCoder.encode(self.selectorName, forKey: "selectorName")
|
|
aCoder.encode(self.parameters, forKey: "parameters")
|
|
aCoder.encode(self.returnTypeName, forKey: "returnTypeName")
|
|
aCoder.encode(self.returnType, forKey: "returnType")
|
|
aCoder.encode(self.isAsync, forKey: "isAsync")
|
|
aCoder.encode(self.`throws`, forKey: "`throws`")
|
|
aCoder.encode(self.`rethrows`, forKey: "`rethrows`")
|
|
aCoder.encode(self.accessLevel, forKey: "accessLevel")
|
|
aCoder.encode(self.isStatic, forKey: "isStatic")
|
|
aCoder.encode(self.isClass, forKey: "isClass")
|
|
aCoder.encode(self.isFailableInitializer, forKey: "isFailableInitializer")
|
|
aCoder.encode(self.annotations, forKey: "annotations")
|
|
aCoder.encode(self.documentation, forKey: "documentation")
|
|
aCoder.encode(self.definedInTypeName, forKey: "definedInTypeName")
|
|
aCoder.encode(self.definedInType, forKey: "definedInType")
|
|
aCoder.encode(self.attributes, forKey: "attributes")
|
|
aCoder.encode(self.modifiers, forKey: "modifiers")
|
|
aCoder.encode(self.genericRequirements, forKey: "genericRequirements")
|
|
aCoder.encode(self.genericParameters, forKey: "genericParameters")
|
|
}
|
|
// sourcery:end
|
|
}
|
|
#endif
|