Improve error handling.

This commit is contained in:
Filip Dolník 2016-12-18 19:54:23 +01:00 committed by Tadeas Kriz
parent 4a1dbbd97c
commit f3064ae797
12 changed files with 85 additions and 17 deletions

View File

@ -1,6 +1,12 @@
# 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
* Set "Reflection Metadata" to "None" to fix #72
## 0.8.0

View File

@ -48,6 +48,7 @@
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 */; };
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 */
/* 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>"; };
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>"; };
DCE2DDEE1E06F073006E462C /* StderrPrint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StderrPrint.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -431,6 +433,7 @@
DC5176331D2EE4D300E922F2 /* CodeBuilder.swift */,
18E16D591C45A85E0084EF54 /* FileHeaderHandler.swift */,
18F2BC1B1C46E8760015A95F /* Generator.swift */,
DCE2DDEE1E06F073006E462C /* StderrPrint.swift */,
18E16D531C45A6AF0084EF54 /* Tokenizer.swift */,
18E16D511C45A6890084EF54 /* Utils.swift */,
18601E8A1C46FA7400693A66 /* Supporting Files */,
@ -932,6 +935,7 @@
DC4094301D13F91F006FB137 /* Import.swift in Sources */,
18E16D831C45D8B30084EF54 /* Utils.swift in Sources */,
DC9EFA7B1CFC4F4D0034DFE5 /* Method.swift in Sources */,
DCE2DDEF1E06F073006E462C /* StderrPrint.swift in Sources */,
DC4094861D140184006FB137 /* ExtensionDeclaration.swift in Sources */,
DC9EFA741CFC4F4D0034DFE5 /* Accessibility.swift in Sources */,
DC9EFA781CFC4F4D0034DFE5 /* ContainerToken.swift in Sources */,

View File

@ -12,6 +12,7 @@ import FileKit
public enum CuckooGeneratorError: Error {
case ioError(FileKitError)
case unknownError(Error)
case stderrUsed
public var description: String {
switch self {
@ -19,6 +20,8 @@ public enum CuckooGeneratorError: Error {
return error.description
case .unknownError(let error):
return "\(error)"
case .stderrUsed:
return ""
}
}
}

View File

@ -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)
}

View File

@ -92,9 +92,13 @@ public struct Tokenizer {
return nil
}
if type == nil {
stderrPrint("Type of instance variable \(name) could not be inferred. Please specify it explicitly. (\(file.path ?? ""))")
}
return InstanceVariable(
name: name,
type: type!,
type: type ?? "__UnknownType",
accessibility: accessibility!,
setterAccessibility: setterAccessibility,
range: range!,
@ -142,12 +146,9 @@ public struct Tokenizer {
parameters: parameters)
}
case Kinds.Mark.rawValue:
// Do not log warning
return nil
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
}
}
@ -155,9 +156,15 @@ public struct Tokenizer {
private func tokenize(methodName: String, parameters: [SourceKitRepresentable]) -> [MethodParameter] {
// Takes the string between `(` and `)`
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? {
@ -174,7 +181,7 @@ public struct Tokenizer {
return MethodParameter(label: parameterLabel, name: name, type: type!, range: range!, nameRange: nameRange!)
default:
fputs("Unknown method parameter kind: \(kind)", stderr)
stderrPrint("Unknown method parameter. Dictionary: \(dictionary) \(file.path ?? "")")
return nil
}
}

View File

@ -13,6 +13,4 @@ public enum Kinds: String {
case ClassDeclaration = "source.lang.swift.decl.class"
case ExtensionDeclaration = "source.lang.swift.decl.extension"
case InstanceVariable = "source.lang.swift.decl.var.instance"
case Mark = "source.lang.swift.syntaxtype.comment.mark"
}

View File

@ -23,8 +23,9 @@ public struct GenerateMocksCommand: CommandProtocol {
public let function = "Generates mock files"
public func run(_ options: Options) -> Result<Void, CuckooGeneratorError> {
let inputFiles = Array(Set(options.files.map { Path($0).standardRawValue }))
let tokens = inputFiles.flatMap { File(path: $0) }.map { Tokenizer(sourceFile: $0).tokenize() }
let inputPathValues = Array(Set(options.files.map { Path($0).standardRawValue }))
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 headers = parsedFiles.map { options.noHeader ? "" : FileHeaderHandler.getHeader(of: $0, includeTimestamp: !options.noTimestamp) }
@ -36,7 +37,7 @@ public struct GenerateMocksCommand: CommandProtocol {
do {
if outputPath.isDirectory {
let inputPaths = inputFiles.map { Path($0) }
let inputPaths = inputPathValues.map { Path($0) }
for (inputPath, outputText) in zip(inputPaths, mergedFiles) {
let fileName = options.filePrefix + inputPath.fileName
let outputFile = TextFile(path: outputPath + fileName)
@ -51,7 +52,9 @@ public struct GenerateMocksCommand: CommandProtocol {
} catch let 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] {

View File

@ -17,5 +17,10 @@ let helpCommand = HelpCommand(registry: registry)
registry.register(helpCommand)
registry.main(defaultVerb: helpCommand.verb) { error in
fputs(error.description + "\n", stderr)
switch error {
case .stderrUsed:
break
default:
fputs(error.description + "\n", stderr)
}
}

View File

@ -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
}

View File

@ -40,12 +40,25 @@ Feature: Generate command
# 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
When I run `runcuckoo generate non_existing_file.swift`
Then the output should contain:
"""
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.)

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
FILE_NAME="cuckoo_generator.app"
if [[ "$1" != "--no-build" || ! -f "$FILE_NAME" ]]; then
if [[ "$1" == "--clean" || ! -d "$FILE_NAME" ]]; then
rm -rf Build
mkdir Build
xcodebuild -project 'CuckooGenerator.xcodeproj' -scheme 'CuckooGenerator' -configuration 'Release' CONFIGURATION_BUILD_DIR="$PWD/Build" clean build

View File

@ -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:
* inheritance (grandparent methods)
* generics
* type inference for instance variables (you need to write it explicitly, otherwise it will be replaced with "__UnknownType")
## What will not be supported