diff --git a/Generator/Sources/Internal/Crawlers/Crawler.swift b/Generator/Sources/Internal/Crawlers/Crawler.swift index e8c1fe3d..3e00f065 100644 --- a/Generator/Sources/Internal/Crawlers/Crawler.swift +++ b/Generator/Sources/Internal/Crawlers/Crawler.swift @@ -182,7 +182,11 @@ final class Crawler: SyntaxVisitor { } override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind { - tokens.append(Typealias(syntax: node)) + do { + try tokens.append(Typealias(syntax: node)) + } catch { + log(.error, message: "Error \(url.path):\n\tCan't parse typealias '\(node.trimmedDescription)'.") + } return .skipChildren } @@ -269,7 +273,12 @@ extension Crawler { let type: ComplexType if let explicitType = binding.typeAnnotation?.type { - type = ComplexType(syntax: explicitType) + do { + type = try ComplexType(syntax: explicitType) + } catch { + log(.error, message: "Error \(url.path):\n\tCan't parse explicit type '\(explicitType.trimmedDescription)'.") + return nil + } } else if let initializer = binding.initializer?.value.trimmed.description, let guessedType = TypeGuesser.guessType(from: initializer) { type = .type(guessedType) } else { @@ -318,6 +327,14 @@ extension Crawler { guard accessibility.isAccessible else { return nil } + let parameters: [MethodParameter] + do { + parameters = try self.parameters(from: initializer.signature.parameterClause.parameters) + } catch { + log(.error, message: "Error \(url.path):\n\tCan't parse method parameters '\(initializer.signature.parameterClause.parameters.trimmedDescription)'.") + return nil + } + return Initializer( parent: container, documentation: documentation(from: initializer.leadingTrivia), @@ -325,7 +342,7 @@ extension Crawler { accessibility: accessibility, signature: Method.Signature( genericParameters: genericParameters(from: initializer.genericParameterClause?.parameters), - parameters: parameters(from: initializer.signature.parameterClause.parameters), + parameters: parameters, asyncType: nil, throwType: initializer.signature.effectSpecifiers?.throwsSpecifier.flatMap { ThrowType(rawValue: $0.trimmed.description) }, returnType: nil, @@ -349,6 +366,22 @@ extension Crawler { guard accessibility.isAccessible else { return nil } + let parameters: [MethodParameter] + do { + parameters = try self.parameters(from: method.signature.parameterClause.parameters) + } catch { + log(.error, message: "Error \(url.path):\n\tCan't parse method parameters '\(method.signature.parameterClause.parameters.trimmedDescription)'.") + return nil + } + + let returnType: ComplexType? + do { + returnType = try method.signature.returnClause.flatMap { try ComplexType(syntax: $0.type) } + } catch { + log(.error, message: "Error \(url.path):\n\tCan't parse return type '\(method.signature.returnClause?.type.trimmedDescription ?? "")'.") + return nil + } + return Method( parent: container, documentation: documentation(from: method.leadingTrivia), @@ -357,10 +390,10 @@ extension Crawler { name: identifier, signature: Method.Signature( genericParameters: genericParameters(from: method.genericParameterClause?.parameters), - parameters: parameters(from: method.signature.parameterClause.parameters), + parameters: parameters, asyncType: method.signature.effectSpecifiers?.asyncSpecifier.flatMap { AsyncType(rawValue: $0.trimmed.description) }, throwType: method.signature.effectSpecifiers?.throwsSpecifier.flatMap { ThrowType(rawValue: $0.trimmed.description) }, - returnType: method.signature.returnClause.flatMap { ComplexType(syntax: $0.type) } ?? ComplexType.type("Void"), + returnType: returnType ?? ComplexType.type("Void"), whereConstraints: genericRequirements(from: method.genericWhereClause?.requirements) ), isOptional: method.modifiers.contains { $0.name.tokenKind == .keyword(.optional) } @@ -369,12 +402,12 @@ extension Crawler { } extension Crawler { - private func parameters(from parameterList: FunctionParameterListSyntax) -> [MethodParameter] { - parameterList.map { parameter in + private func parameters(from parameterList: FunctionParameterListSyntax) throws -> [MethodParameter] { + try parameterList.map { parameter in MethodParameter( name: parameter.firstName.trimmed.description, innerName: nil, - type: ComplexType(syntax: parameter.type) + type: try ComplexType(syntax: parameter.type) ) } } @@ -424,11 +457,11 @@ extension Crawler { // case "objcMembers": // return .objcMembers default: - print("Ignoring unsupported attribute '\(attribute.attributeName.trimmedDescription)'") + log(.verbose, message: "Ignoring unsupported attribute '\(attribute.attributeName.trimmedDescription)'") return nil } } else { - print("Ignoring unsupported attribute '\(attribute.attributeName.trimmedDescription)'") + log(.verbose, message: "Ignoring unsupported attribute '\(attribute.attributeName.trimmedDescription)'") return nil } } @@ -440,7 +473,7 @@ extension Crawler { private func genericParameters(from genericParameterList: GenericParameterListSyntax?) -> [GenericParameter] { genericParameterList?.map { genericParameter in GenericParameter( - name: genericParameter.name.identifier, + name: try! genericParameter.name.identifier, inheritedTypes: inheritedTypes(from: genericParameter.inheritedType?.trimmed) ) } ?? [] diff --git a/Generator/Sources/Internal/Helpers/SwiftSyntax+convenience.swift b/Generator/Sources/Internal/Helpers/SwiftSyntax+convenience.swift index 326ad35c..a316841c 100644 --- a/Generator/Sources/Internal/Helpers/SwiftSyntax+convenience.swift +++ b/Generator/Sources/Internal/Helpers/SwiftSyntax+convenience.swift @@ -26,22 +26,28 @@ extension ExprSyntaxProtocol { extension TokenSyntax { var identifier: String { - tokenKind.identifier + get throws { + try tokenKind.identifier + } } } extension IdentifierTypeSyntax { var identifier: String { - name.tokenKind.identifier + get throws { + try name.tokenKind.identifier + } } } extension TokenKind { var identifier: String { - if case .identifier(let identifier) = self { - return identifier - } else { - fatalError("Cuckoo error: Expected identifier. Please create an issue.") + get throws { + if case .identifier(let identifier) = self { + return identifier + } else { + fatalError("Cuckoo error: Expected identifier. Please create an issue.") + } } } } diff --git a/Generator/Sources/Internal/Tokens/ComplexType.swift b/Generator/Sources/Internal/Tokens/ComplexType.swift index d4a5b916..8d98af4f 100644 --- a/Generator/Sources/Internal/Tokens/ComplexType.swift +++ b/Generator/Sources/Internal/Tokens/ComplexType.swift @@ -9,49 +9,49 @@ enum ComplexType { case closure(Closure) case type(String) - init(syntax: TypeSyntax) { + init(syntax: TypeSyntax) throws { if let implicitOptionalType = syntax.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) { - self = .optional(wrappedType: ComplexType(syntax: implicitOptionalType.wrappedType), isImplicit: true) + self = .optional(wrappedType: try ComplexType(syntax: implicitOptionalType.wrappedType), isImplicit: true) } else if let optionalType = syntax.as(OptionalTypeSyntax.self) { - self = .optional(wrappedType: ComplexType(syntax: optionalType.wrappedType), isImplicit: false) + self = .optional(wrappedType: try ComplexType(syntax: optionalType.wrappedType), isImplicit: false) } else if let attributedType = syntax.as(AttributedTypeSyntax.self) { self = .attributed( attributes: [ attributedType.attributes.map { $0.trimmedDescription }, attributedType.specifier.map { [$0.trimmedDescription] } ?? [], ].flatMap { $0 }, - baseType: ComplexType(syntax: attributedType.baseType) + baseType: try ComplexType(syntax: attributedType.baseType) ) } else if let functionType = syntax.as(FunctionTypeSyntax.self) { self = .closure( Closure( - parameters: functionType.parameters.map { + parameters: try functionType.parameters.map { Closure.Parameter( label: $0.secondName?.trimmedDescription, - type: ComplexType(syntax: $0.type) + type: try ComplexType(syntax: $0.type) ) }, effects: Closure.Effects(effectSpecifiers: functionType.effectSpecifiers), - returnType: ComplexType(syntax: functionType.returnClause.type) + returnType: try ComplexType(syntax: functionType.returnClause.type) ) ) } else if let identifierType = syntax.as(IdentifierTypeSyntax.self) { - switch identifierType.identifier { + switch try identifierType.identifier { case "Dictionary": let arguments = identifierType.genericArgumentClause?.arguments guard let keyType = arguments?.first?.argument, let valueType = arguments?.last?.argument else { fatalError("Cuckoo error: Failed to get Dictionary type, please open an issue.") } self = .dictionary( - keyType: ComplexType(syntax: keyType), - valueType: ComplexType(syntax: valueType) + keyType: try ComplexType(syntax: keyType), + valueType: try ComplexType(syntax: valueType) ) case "Array": let arguments = identifierType.genericArgumentClause?.arguments guard let elementType = arguments?.first?.argument else { fatalError("Cuckoo error: Failed to get Array type, please open an issue.") } - self = .array(elementType: ComplexType(syntax: elementType)) + self = .array(elementType: try ComplexType(syntax: elementType)) default: self = .type(syntax.trimmedDescription) } diff --git a/Generator/Sources/Internal/Tokens/Typealias.swift b/Generator/Sources/Internal/Tokens/Typealias.swift index 1de423a7..5c2a7d97 100644 --- a/Generator/Sources/Internal/Tokens/Typealias.swift +++ b/Generator/Sources/Internal/Tokens/Typealias.swift @@ -13,10 +13,10 @@ struct Typealias: Token, CustomStringConvertible { } extension Typealias { - init(syntax: TypeAliasDeclSyntax) { + init(syntax: TypeAliasDeclSyntax) throws { self.init( alias: [ - syntax.name.identifier, + try syntax.name.identifier, syntax.genericParameterClause?.trimmedDescription ] .compactMap { $0 } diff --git a/README.md b/README.md index 1c4facdc..aa0d408b 100644 --- a/README.md +++ b/README.md @@ -118,14 +118,16 @@ output = "Tests/Swift/Generated/GeneratedMocks.swift" [modules.MyProject] output = "Tests/Swift/Generated/GeneratedMocks+MyProject.swift" +# Standard imports added to the generated file(s). imports = ["Foundation"] +# @testable imports if needed. testableImports = ["RxSwift"] sources = [ "Tests/Swift/Source/*.swift", ] exclude = ["ExcludedTestClass"] # Optionally you can use a regular expression to filter only specific classes/protocols. -regex = "" +# regex = "" [modules.MyProject.options] # glob = false @@ -140,6 +142,10 @@ keepDocumentation = false # Path to folder with .xcodeproj, omit this if it's at the same level as Cuckoofile. path = "Generator" target = "Cuckoonator" + +# You can define as many modules as you need, each with different sources/options/output. +[modules.AnotherProject] +# ... ``` ### 3. Usage