reporting error with its parent context
This commit is contained in:
@@ -19,15 +19,56 @@ public class TemplateDoesNotExist: Error, CustomStringConvertible {
|
||||
}
|
||||
|
||||
public struct TemplateSyntaxError : Error, Equatable, CustomStringConvertible {
|
||||
public let description:String
|
||||
var lexeme: Lexeme?
|
||||
public let reason: String
|
||||
public private(set) var description: String
|
||||
|
||||
public internal(set) var template: Template?
|
||||
public internal(set) var parentError: Error?
|
||||
|
||||
var lexeme: Lexeme? {
|
||||
didSet {
|
||||
description = TemplateSyntaxError.description(reason: reason, lexeme: lexeme, template: template)
|
||||
}
|
||||
}
|
||||
|
||||
public init(_ description:String) {
|
||||
self.description = description
|
||||
static func description(reason: String, lexeme: Lexeme?, template: Template?) -> String {
|
||||
if let template = template, let range = lexeme?.range {
|
||||
let templateName = template.name.map({ "\($0):" }) ?? ""
|
||||
let tokenContent = template.templateString.substring(with: range)
|
||||
let line = template.templateString.rangeLine(range)
|
||||
let highlight = "\(String(Array(repeating: " ", count: line.offset)))^\(String(Array(repeating: "~", count: max(tokenContent.length - 1, 0))))"
|
||||
|
||||
return "\(templateName)\(line.number):\(line.offset): error: \(reason)\n"
|
||||
+ "\(line.content)\n"
|
||||
+ "\(highlight)\n"
|
||||
} else {
|
||||
return reason
|
||||
}
|
||||
}
|
||||
|
||||
init(reason: String, lexeme: Lexeme? = nil, template: Template? = nil, parentError: Error? = nil) {
|
||||
self.reason = reason
|
||||
self.parentError = parentError
|
||||
self.template = template
|
||||
self.lexeme = lexeme
|
||||
self.description = TemplateSyntaxError.description(reason: reason, lexeme: lexeme, template: template)
|
||||
}
|
||||
|
||||
public init(_ description: String) {
|
||||
self.init(reason: description)
|
||||
}
|
||||
|
||||
public static func ==(lhs:TemplateSyntaxError, rhs:TemplateSyntaxError) -> Bool {
|
||||
return lhs.description == rhs.description
|
||||
guard lhs.description == rhs.description else { return false }
|
||||
|
||||
switch (lhs.parentError, rhs.parentError) {
|
||||
case let (lhsParent? as TemplateSyntaxError?, rhsParent? as TemplateSyntaxError?):
|
||||
return lhsParent == rhsParent
|
||||
case let (lhsParent?, rhsParent?):
|
||||
return String(describing: lhsParent) == String(describing: rhsParent)
|
||||
default:
|
||||
return lhs.parentError == nil && rhs.parentError == nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,7 +88,7 @@ public class ErrorReporterContext {
|
||||
public protocol ErrorReporter: class {
|
||||
var context: ErrorReporterContext! { get set }
|
||||
func reportError(_ error: Error) -> Error
|
||||
func contextAwareError(_ error: Error, at range: Range<String.Index>?, context: ErrorReporterContext) -> Error?
|
||||
func renderError(_ error: Error) -> String
|
||||
}
|
||||
|
||||
open class SimpleErrorReporter: ErrorReporter {
|
||||
@@ -55,21 +96,28 @@ open class SimpleErrorReporter: ErrorReporter {
|
||||
|
||||
open func reportError(_ error: Error) -> Error {
|
||||
guard let context = context else { return error }
|
||||
return contextAwareError(error, at: (error as? TemplateSyntaxError)?.lexeme?.range, context: context) ?? error
|
||||
|
||||
return TemplateSyntaxError(reason: (error as? TemplateSyntaxError)?.reason ?? "\(error)",
|
||||
lexeme: (error as? TemplateSyntaxError)?.lexeme,
|
||||
template: context.template,
|
||||
parentError: (error as? TemplateSyntaxError)?.parentError
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: add stack trace using parent context
|
||||
open func contextAwareError(_ error: Error, at range: Range<String.Index>?, context: ErrorReporterContext) -> Error? {
|
||||
guard let range = range, range != .unknown else { return nil }
|
||||
let templateName = context.template.name.map({ "\($0):" }) ?? ""
|
||||
let tokenContent = context.template.templateString.substring(with: range)
|
||||
let line = context.template.templateString.rangeLine(range)
|
||||
let highlight = "\(String(Array(repeating: " ", count: line.offset)))^\(String(Array(repeating: "~", count: max(tokenContent.length - 1, 0))))"
|
||||
let description = "\(templateName)\(line.number):\(line.offset): error: \(error)\n\(line.content)\n\(highlight)"
|
||||
let error = TemplateSyntaxError(description)
|
||||
return error
|
||||
open func renderError(_ error: Error) -> String {
|
||||
guard let templateError = error as? TemplateSyntaxError else { return error.localizedDescription }
|
||||
|
||||
var descriptions = [templateError.description]
|
||||
|
||||
var currentError: TemplateSyntaxError? = templateError
|
||||
while let parentError = currentError?.parentError {
|
||||
descriptions.append(renderError(parentError))
|
||||
currentError = parentError as? TemplateSyntaxError
|
||||
}
|
||||
|
||||
return descriptions.reversed().joined(separator: "\n")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension Range where Bound == String.Index {
|
||||
|
||||
Reference in New Issue
Block a user