Improve error handling.
This commit is contained in:
parent
4a1dbbd97c
commit
f3064ae797
|
@ -1,6 +1,12 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.8.2
|
||||||
|
|
||||||
|
* Show error in generator in build log.
|
||||||
|
* Fixed crash of generator when instance variable type is not explicitly set.
|
||||||
|
|
||||||
## 0.8.1
|
## 0.8.1
|
||||||
|
|
||||||
* Set "Reflection Metadata" to "None" to fix #72
|
* Set "Reflection Metadata" to "None" to fix #72
|
||||||
|
|
||||||
## 0.8.0
|
## 0.8.0
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
DCAD1F141D0DB6540091EECE /* Commandant.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCAD1ECC1D0DB3640091EECE /* Commandant.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
DCAD1F141D0DB6540091EECE /* Commandant.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCAD1ECC1D0DB3640091EECE /* Commandant.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
DCDEE5141CFF124E007F18BC /* CuckooGeneratorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDEE5101CFF124E007F18BC /* CuckooGeneratorError.swift */; };
|
DCDEE5141CFF124E007F18BC /* CuckooGeneratorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDEE5101CFF124E007F18BC /* CuckooGeneratorError.swift */; };
|
||||||
DCDEE5151CFF124E007F18BC /* GenerateMocksCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDEE5111CFF124E007F18BC /* GenerateMocksCommand.swift */; };
|
DCDEE5151CFF124E007F18BC /* GenerateMocksCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDEE5111CFF124E007F18BC /* GenerateMocksCommand.swift */; };
|
||||||
|
DCE2DDEF1E06F073006E462C /* StderrPrint.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE2DDEE1E06F073006E462C /* StderrPrint.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -345,6 +346,7 @@
|
||||||
DCAD1EC61D0DB3640091EECE /* Commandant.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Commandant.xcodeproj; path = Dependencies/SourceKitten/Carthage/Checkouts/Commandant/Commandant.xcodeproj; sourceTree = "<group>"; };
|
DCAD1EC61D0DB3640091EECE /* Commandant.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Commandant.xcodeproj; path = Dependencies/SourceKitten/Carthage/Checkouts/Commandant/Commandant.xcodeproj; sourceTree = "<group>"; };
|
||||||
DCDEE5101CFF124E007F18BC /* CuckooGeneratorError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuckooGeneratorError.swift; sourceTree = "<group>"; };
|
DCDEE5101CFF124E007F18BC /* CuckooGeneratorError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuckooGeneratorError.swift; sourceTree = "<group>"; };
|
||||||
DCDEE5111CFF124E007F18BC /* GenerateMocksCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GenerateMocksCommand.swift; sourceTree = "<group>"; };
|
DCDEE5111CFF124E007F18BC /* GenerateMocksCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GenerateMocksCommand.swift; sourceTree = "<group>"; };
|
||||||
|
DCE2DDEE1E06F073006E462C /* StderrPrint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StderrPrint.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -431,6 +433,7 @@
|
||||||
DC5176331D2EE4D300E922F2 /* CodeBuilder.swift */,
|
DC5176331D2EE4D300E922F2 /* CodeBuilder.swift */,
|
||||||
18E16D591C45A85E0084EF54 /* FileHeaderHandler.swift */,
|
18E16D591C45A85E0084EF54 /* FileHeaderHandler.swift */,
|
||||||
18F2BC1B1C46E8760015A95F /* Generator.swift */,
|
18F2BC1B1C46E8760015A95F /* Generator.swift */,
|
||||||
|
DCE2DDEE1E06F073006E462C /* StderrPrint.swift */,
|
||||||
18E16D531C45A6AF0084EF54 /* Tokenizer.swift */,
|
18E16D531C45A6AF0084EF54 /* Tokenizer.swift */,
|
||||||
18E16D511C45A6890084EF54 /* Utils.swift */,
|
18E16D511C45A6890084EF54 /* Utils.swift */,
|
||||||
18601E8A1C46FA7400693A66 /* Supporting Files */,
|
18601E8A1C46FA7400693A66 /* Supporting Files */,
|
||||||
|
@ -932,6 +935,7 @@
|
||||||
DC4094301D13F91F006FB137 /* Import.swift in Sources */,
|
DC4094301D13F91F006FB137 /* Import.swift in Sources */,
|
||||||
18E16D831C45D8B30084EF54 /* Utils.swift in Sources */,
|
18E16D831C45D8B30084EF54 /* Utils.swift in Sources */,
|
||||||
DC9EFA7B1CFC4F4D0034DFE5 /* Method.swift in Sources */,
|
DC9EFA7B1CFC4F4D0034DFE5 /* Method.swift in Sources */,
|
||||||
|
DCE2DDEF1E06F073006E462C /* StderrPrint.swift in Sources */,
|
||||||
DC4094861D140184006FB137 /* ExtensionDeclaration.swift in Sources */,
|
DC4094861D140184006FB137 /* ExtensionDeclaration.swift in Sources */,
|
||||||
DC9EFA741CFC4F4D0034DFE5 /* Accessibility.swift in Sources */,
|
DC9EFA741CFC4F4D0034DFE5 /* Accessibility.swift in Sources */,
|
||||||
DC9EFA781CFC4F4D0034DFE5 /* ContainerToken.swift in Sources */,
|
DC9EFA781CFC4F4D0034DFE5 /* ContainerToken.swift in Sources */,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import FileKit
|
||||||
public enum CuckooGeneratorError: Error {
|
public enum CuckooGeneratorError: Error {
|
||||||
case ioError(FileKitError)
|
case ioError(FileKitError)
|
||||||
case unknownError(Error)
|
case unknownError(Error)
|
||||||
|
case stderrUsed
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -19,6 +20,8 @@ public enum CuckooGeneratorError: Error {
|
||||||
return error.description
|
return error.description
|
||||||
case .unknownError(let error):
|
case .unknownError(let error):
|
||||||
return "\(error)"
|
return "\(error)"
|
||||||
|
case .stderrUsed:
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// StderrPrint.swift
|
||||||
|
// CuckooGenerator
|
||||||
|
//
|
||||||
|
// Created by Filip Dolnik on 18.12.16.
|
||||||
|
// Copyright © 2016 Brightify. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public private(set) var stderrUsed = false
|
||||||
|
|
||||||
|
func stderrPrint(_ item: Any) {
|
||||||
|
stderrUsed = true
|
||||||
|
fputs("\(item)\n", stderr)
|
||||||
|
}
|
|
@ -92,9 +92,13 @@ public struct Tokenizer {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if type == nil {
|
||||||
|
stderrPrint("Type of instance variable \(name) could not be inferred. Please specify it explicitly. (\(file.path ?? ""))")
|
||||||
|
}
|
||||||
|
|
||||||
return InstanceVariable(
|
return InstanceVariable(
|
||||||
name: name,
|
name: name,
|
||||||
type: type!,
|
type: type ?? "__UnknownType",
|
||||||
accessibility: accessibility!,
|
accessibility: accessibility!,
|
||||||
setterAccessibility: setterAccessibility,
|
setterAccessibility: setterAccessibility,
|
||||||
range: range!,
|
range: range!,
|
||||||
|
@ -142,12 +146,9 @@ public struct Tokenizer {
|
||||||
parameters: parameters)
|
parameters: parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Kinds.Mark.rawValue:
|
|
||||||
// Do not log warning
|
|
||||||
return nil
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fputs("Unknown kind: \(kind)", stderr)
|
// Do not log anything, until the parser contains all known cases.
|
||||||
|
// stderrPrint("Unknown kind. Dictionary: \(dictionary) \(file.path ?? "")")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,9 +156,15 @@ public struct Tokenizer {
|
||||||
private func tokenize(methodName: String, parameters: [SourceKitRepresentable]) -> [MethodParameter] {
|
private func tokenize(methodName: String, parameters: [SourceKitRepresentable]) -> [MethodParameter] {
|
||||||
// Takes the string between `(` and `)`
|
// Takes the string between `(` and `)`
|
||||||
let parameterNames = methodName.components(separatedBy: "(").last?.characters.dropLast(1).map { "\($0)" }.joined(separator: "")
|
let parameterNames = methodName.components(separatedBy: "(").last?.characters.dropLast(1).map { "\($0)" }.joined(separator: "")
|
||||||
let parameterLabels: [String?] = parameterNames?.components(separatedBy: ":").map { $0 != "_" ? $0 : nil } ?? []
|
var parameterLabels: [String?] = parameterNames?.components(separatedBy: ":").map { $0 != "_" ? $0 : nil } ?? []
|
||||||
|
|
||||||
return zip(parameterLabels, parameters).flatMap(tokenize)
|
// Last element is not type.
|
||||||
|
parameterLabels = Array(parameterLabels.dropLast())
|
||||||
|
|
||||||
|
// Substructure can contain some other informations after the parameters.
|
||||||
|
let filteredParameters = parameters[0..<min(parameterLabels.count, parameters.count)]
|
||||||
|
|
||||||
|
return zip(parameterLabels, filteredParameters).flatMap(tokenize)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func tokenize(parameterLabel: String?, parameter: SourceKitRepresentable) -> MethodParameter? {
|
private func tokenize(parameterLabel: String?, parameter: SourceKitRepresentable) -> MethodParameter? {
|
||||||
|
@ -174,7 +181,7 @@ public struct Tokenizer {
|
||||||
return MethodParameter(label: parameterLabel, name: name, type: type!, range: range!, nameRange: nameRange!)
|
return MethodParameter(label: parameterLabel, name: name, type: type!, range: range!, nameRange: nameRange!)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fputs("Unknown method parameter kind: \(kind)", stderr)
|
stderrPrint("Unknown method parameter. Dictionary: \(dictionary) \(file.path ?? "")")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,4 @@ public enum Kinds: String {
|
||||||
case ClassDeclaration = "source.lang.swift.decl.class"
|
case ClassDeclaration = "source.lang.swift.decl.class"
|
||||||
case ExtensionDeclaration = "source.lang.swift.decl.extension"
|
case ExtensionDeclaration = "source.lang.swift.decl.extension"
|
||||||
case InstanceVariable = "source.lang.swift.decl.var.instance"
|
case InstanceVariable = "source.lang.swift.decl.var.instance"
|
||||||
|
|
||||||
case Mark = "source.lang.swift.syntaxtype.comment.mark"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,9 @@ public struct GenerateMocksCommand: CommandProtocol {
|
||||||
public let function = "Generates mock files"
|
public let function = "Generates mock files"
|
||||||
|
|
||||||
public func run(_ options: Options) -> Result<Void, CuckooGeneratorError> {
|
public func run(_ options: Options) -> Result<Void, CuckooGeneratorError> {
|
||||||
let inputFiles = Array(Set(options.files.map { Path($0).standardRawValue }))
|
let inputPathValues = Array(Set(options.files.map { Path($0).standardRawValue }))
|
||||||
let tokens = inputFiles.flatMap { File(path: $0) }.map { Tokenizer(sourceFile: $0).tokenize() }
|
let inputFiles = inputPathValues.map { File(path: $0) }
|
||||||
|
let tokens = inputFiles.flatMap { $0 }.map { Tokenizer(sourceFile: $0).tokenize() }
|
||||||
let parsedFiles = options.noClassMocking ? removeClasses(tokens) : tokens
|
let parsedFiles = options.noClassMocking ? removeClasses(tokens) : tokens
|
||||||
|
|
||||||
let headers = parsedFiles.map { options.noHeader ? "" : FileHeaderHandler.getHeader(of: $0, includeTimestamp: !options.noTimestamp) }
|
let headers = parsedFiles.map { options.noHeader ? "" : FileHeaderHandler.getHeader(of: $0, includeTimestamp: !options.noTimestamp) }
|
||||||
|
@ -36,7 +37,7 @@ public struct GenerateMocksCommand: CommandProtocol {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if outputPath.isDirectory {
|
if outputPath.isDirectory {
|
||||||
let inputPaths = inputFiles.map { Path($0) }
|
let inputPaths = inputPathValues.map { Path($0) }
|
||||||
for (inputPath, outputText) in zip(inputPaths, mergedFiles) {
|
for (inputPath, outputText) in zip(inputPaths, mergedFiles) {
|
||||||
let fileName = options.filePrefix + inputPath.fileName
|
let fileName = options.filePrefix + inputPath.fileName
|
||||||
let outputFile = TextFile(path: outputPath + fileName)
|
let outputFile = TextFile(path: outputPath + fileName)
|
||||||
|
@ -51,7 +52,9 @@ public struct GenerateMocksCommand: CommandProtocol {
|
||||||
} catch let error {
|
} catch let error {
|
||||||
return .failure(.unknownError(error))
|
return .failure(.unknownError(error))
|
||||||
}
|
}
|
||||||
return .success()
|
|
||||||
|
let couldNotOpenFile = inputFiles.contains { $0 == nil }
|
||||||
|
return stderrUsed || couldNotOpenFile ? .failure(.stderrUsed) : .success()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func removeClasses(_ filesRepresentation: [FileRepresentation]) -> [FileRepresentation] {
|
private func removeClasses(_ filesRepresentation: [FileRepresentation]) -> [FileRepresentation] {
|
||||||
|
|
|
@ -17,5 +17,10 @@ let helpCommand = HelpCommand(registry: registry)
|
||||||
registry.register(helpCommand)
|
registry.register(helpCommand)
|
||||||
|
|
||||||
registry.main(defaultVerb: helpCommand.verb) { error in
|
registry.main(defaultVerb: helpCommand.verb) { error in
|
||||||
fputs(error.description + "\n", stderr)
|
switch error {
|
||||||
|
case .stderrUsed:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
fputs(error.description + "\n", stderr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
//
|
||||||
|
// ImplicitInstanceVariableType.swift
|
||||||
|
// Cuckoo
|
||||||
|
//
|
||||||
|
// Created by Tadeas Kriz on 09/02/16.
|
||||||
|
// Copyright © 2016 Brightify. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
class ImplicitInstanceVariableType {
|
||||||
|
|
||||||
|
var variable = 5
|
||||||
|
}
|
|
@ -40,12 +40,25 @@ Feature: Generate command
|
||||||
|
|
||||||
# Not recorded tests
|
# Not recorded tests
|
||||||
|
|
||||||
|
Scenario: success
|
||||||
|
When I run `runcuckoo generate SourceFiles/EmptyClass.swift`
|
||||||
|
Then the exit status should be 0
|
||||||
|
|
||||||
Scenario: non existing input file
|
Scenario: non existing input file
|
||||||
When I run `runcuckoo generate non_existing_file.swift`
|
When I run `runcuckoo generate non_existing_file.swift`
|
||||||
Then the output should contain:
|
Then the output should contain:
|
||||||
"""
|
"""
|
||||||
Could not read contents of `non_existing_file.swift`
|
Could not read contents of `non_existing_file.swift`
|
||||||
"""
|
"""
|
||||||
|
And the exit status should be 1
|
||||||
|
|
||||||
|
Scenario: implicit instance variable type
|
||||||
|
When I run `runcuckoo generate SourceFiles/ImplicitInstanceVariableType.swift`
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
Type of instance variable variable could not be inferred. Please specify it explicitly. (/Users/filip/Documents/Brightify/Cuckoo/Generator/Build/tmp/SourceFiles/ImplicitInstanceVariableType.swift)
|
||||||
|
"""
|
||||||
|
And the exit status should be 1
|
||||||
|
|
||||||
# Tests reusing code from recoreded tests (if they fail these will fail because of it.)
|
# Tests reusing code from recoreded tests (if they fail these will fail because of it.)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
FILE_NAME="cuckoo_generator.app"
|
FILE_NAME="cuckoo_generator.app"
|
||||||
if [[ "$1" != "--no-build" || ! -f "$FILE_NAME" ]]; then
|
if [[ "$1" == "--clean" || ! -d "$FILE_NAME" ]]; then
|
||||||
rm -rf Build
|
rm -rf Build
|
||||||
mkdir Build
|
mkdir Build
|
||||||
xcodebuild -project 'CuckooGenerator.xcodeproj' -scheme 'CuckooGenerator' -configuration 'Release' CONFIGURATION_BUILD_DIR="$PWD/Build" clean build
|
xcodebuild -project 'CuckooGenerator.xcodeproj' -scheme 'CuckooGenerator' -configuration 'Release' CONFIGURATION_BUILD_DIR="$PWD/Build" clean build
|
||||||
|
|
|
@ -31,6 +31,7 @@ List of all changes and new features can be found [here](CHANGELOG.md).
|
||||||
We are still missing support for some important features like:
|
We are still missing support for some important features like:
|
||||||
* inheritance (grandparent methods)
|
* inheritance (grandparent methods)
|
||||||
* generics
|
* generics
|
||||||
|
* type inference for instance variables (you need to write it explicitly, otherwise it will be replaced with "__UnknownType")
|
||||||
|
|
||||||
## What will not be supported
|
## What will not be supported
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue