Cuckoo/Generator/Source/CuckooGeneratorFramework/Tokens/Method.swift

159 lines
6.4 KiB
Swift

//
// Method.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
public protocol Method: Token, HasAccessibility {
var name: String { get }
var returnSignature: ReturnSignature { get }
var range: CountableRange<Int> { get }
var nameRange: CountableRange<Int> { get }
var parameters: [MethodParameter] { get }
var isOptional: Bool { get }
var isOverriding: Bool { get }
var hasClosureParams: Bool { get }
var hasOptionalParams: Bool { get }
var attributes: [Attribute] { get }
var genericParameters: [GenericParameter] { get }
}
public extension Method {
var rawName: String {
return name.takeUntil(occurence: "(") ?? ""
}
var isInit: Bool {
return rawName == "init"
}
var isDeinit: Bool {
return rawName == "deinit"
}
var fullyQualifiedName: String {
let parameterTypes = parameters.map { ($0.isInout ? "inout " : "") + $0.type.sugarized }
let nameParts = name.components(separatedBy: ":")
let lastNamePart = nameParts.last ?? ""
let returnSignatureDescription = returnSignature.description
let returnSignatureString = returnSignatureDescription.isEmpty ? "" : " \(returnSignatureDescription)"
return zip(nameParts.dropLast(), parameterTypes)
.map { $0 + ": " + $1 }
.joined(separator: ", ") + lastNamePart + returnSignatureString
}
var isThrowing: Bool {
guard let throwType = returnSignature.throwType else { return false }
return throwType.isThrowing || throwType.isRethrowing
}
var returnType: WrappableType {
return returnSignature.returnType
}
var hasClosureParams: Bool {
return parameters.contains { $0.isClosure }
}
var hasOptionalParams: Bool {
return parameters.contains { $0.isOptional }
}
func isEqual(to other: Token) -> Bool {
guard let other = other as? Method else { return false }
return self.name == other.name && self.parameters == other.parameters && self.returnType == other.returnType
}
func serialize() -> [String : Any] {
let call = parameters.map {
let referencedName = "\($0.isInout ? "&" : "")\($0.name)"
if let label = $0.label {
return "\(label): \(referencedName)"
} else {
return referencedName
}
}.joined(separator: ", ")
let stubFunctionPrefix = isOverriding ? "Class" : "Protocol"
let returnString = returnType.sugarized == "Void" ? "NoReturn" : ""
let throwingString = isThrowing ? "Throwing" : ""
let stubFunction = "Cuckoo.\(stubFunctionPrefix)Stub\(returnString)\(throwingString)Function"
let escapingParameterNames = parameters.map { parameter in
if parameter.isClosure && !parameter.isEscaping {
let parameterCount = parameter.closureParamCount
let parameterSignature = parameterCount > 0 ? (1...parameterCount).map { _ in "_" }.joined(separator: ", ") : "()"
// FIXME: Instead of parsing the closure return type here, Tokenizer should do it and pass the information in a data structure
let returnSignature: String
let closureReturnType = extractClosureReturnType(parameter: parameter.type.sugarized)
if let closureReturnType = closureReturnType, !closureReturnType.isEmpty && closureReturnType != "Void" {
returnSignature = " -> " + closureReturnType
} else {
returnSignature = ""
}
return "{ \(parameterSignature)\(returnSignature) in fatalError(\"This is a stub! It's not supposed to be called!\") }"
} else {
return parameter.name
}
}.joined(separator: ", ")
let genericParametersString = genericParameters.map { $0.description }.joined(separator: ", ")
let isGeneric = !genericParameters.isEmpty
return [
"self": self,
"name": rawName,
"accessibility": accessibility.sourceName,
"returnSignature": returnSignature.description,
"parameters": parameters,
"parameterNames": parameters.map { $0.name }.joined(separator: ", "),
"escapingParameterNames": escapingParameterNames,
"isInit": isInit,
"returnType": returnType.explicitOptionalOnly.sugarized,
"isThrowing": isThrowing,
"throwType": returnSignature.throwType?.description ?? "",
"fullyQualifiedName": fullyQualifiedName,
"call": call,
"isOverriding": isOverriding,
"parameterSignature": parameters.map { "\($0.labelAndName): \($0.isInout ? "inout " : "")\($0.type)" }.joined(separator: ", "),
"parameterSignatureWithoutNames": parameters.map { "\($0.name): \($0.type)" }.joined(separator: ", "),
"argumentSignature": parameters.map { $0.type.description }.joined(separator: ", "),
"stubFunction": stubFunction,
"inputTypes": parameters.map { $0.typeWithoutAttributes }.joined(separator: ", "),
"isOptional": isOptional,
"hasClosureParams": hasClosureParams,
"hasOptionalParams": hasOptionalParams,
"attributes": attributes.filter { $0.isSupported },
"genericParameters": isGeneric ? "<\(genericParametersString)>" : "",
]
}
private func extractClosureReturnType(parameter: String) -> String? {
var parenLevel = 0
for i in 0..<parameter.count {
let index = parameter.index(parameter.startIndex, offsetBy: i)
let character = parameter[index]
if character == "(" {
parenLevel += 1
} else if character == ")" {
parenLevel -= 1
if parenLevel == 0 {
let returnSignature = String(parameter[parameter.index(after: index)..<parameter.endIndex])
let regex = try! NSRegularExpression(pattern: "\\s*->\\s*(.*)\\s*")
guard let result = regex.matches(in: returnSignature, range: NSRange(location: 0, length: returnSignature.count)).first else { return nil }
return returnSignature[result.range(at: 1)]
}
}
}
return nil
}
}