| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use crate::compiler::CompilerError; |
| use crate::debugger; |
| use crate::dependency_graph::DependencyError; |
| use crate::offline_debugger; |
| use crate::parser_common::{BindParserError, CompoundIdentifier}; |
| use crate::test; |
| use std::fmt; |
| |
| pub struct UserError { |
| index: String, |
| message: String, |
| span: Option<String>, |
| is_compiler_bug: bool, |
| } |
| |
| impl UserError { |
| fn new(index: &str, message: &str, span: Option<String>, is_compiler_bug: bool) -> Self { |
| UserError { index: index.to_string(), message: message.to_string(), span, is_compiler_bug } |
| } |
| } |
| |
| impl fmt::Display for UserError { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| writeln!(f, "[{}]: {}", self.index, self.message)?; |
| if self.is_compiler_bug { |
| writeln!(f, "This is a bind compiler bug, please report it!")?; |
| } |
| if let Some(span) = &self.span { |
| writeln!(f, "{}", span)?; |
| } |
| Ok(()) |
| } |
| } |
| |
| impl From<BindParserError> for UserError { |
| fn from(error: BindParserError) -> Self { |
| match error { |
| BindParserError::Type(span) => { |
| UserError::new("E001", "Expected a type keyword.", Some(span), false) |
| } |
| BindParserError::StringLiteral(span) => { |
| UserError::new("E002", "Expected a string literal.", Some(span), false) |
| } |
| BindParserError::NumericLiteral(span) => { |
| UserError::new("E003", "Expected a numerical literal.", Some(span), false) |
| } |
| BindParserError::BoolLiteral(span) => { |
| UserError::new("E004", "Expected a boolean literal.", Some(span), false) |
| } |
| BindParserError::Identifier(span) => { |
| UserError::new("E005", "Expected an identifier.", Some(span), false) |
| } |
| BindParserError::Semicolon(span) => { |
| UserError::new("E006", "Missing semicolon (;).", Some(span), false) |
| } |
| BindParserError::Assignment(span) => UserError::new( |
| "E007", |
| "Expected an assignment. Are you missing a '='?", |
| Some(span), |
| false, |
| ), |
| BindParserError::ListStart(span) => { |
| UserError::new("E008", "Expected a list. Are you missing a '{'?", Some(span), false) |
| } |
| BindParserError::ListEnd(span) => UserError::new( |
| "E009", |
| "List does not terminate. Are you missing a '}'?", |
| Some(span), |
| false, |
| ), |
| BindParserError::ListSeparator(span) => UserError::new( |
| "E010", |
| "Expected a list separator. Are you missing a ','?", |
| Some(span), |
| false, |
| ), |
| BindParserError::LibraryKeyword(span) => { |
| UserError::new("E011", "Expected 'library' keyword.", Some(span), false) |
| } |
| BindParserError::UsingKeyword(span) => { |
| UserError::new("E012", "Expected 'using' keyword.", Some(span), false) |
| } |
| BindParserError::AsKeyword(span) => { |
| UserError::new("E013", "Expected 'as' keyword", Some(span), false) |
| } |
| BindParserError::IfBlockStart(span) => UserError::new( |
| "E014", |
| "Expected '{' to begin if statement block.", |
| Some(span), |
| false, |
| ), |
| BindParserError::IfBlockEnd(span) => { |
| UserError::new("E015", "Expected '}' to end if statement block.", Some(span), false) |
| } |
| BindParserError::IfKeyword(span) => { |
| UserError::new("E016", "Expected 'if' keyword.", Some(span), false) |
| } |
| BindParserError::ElseKeyword(span) => { |
| UserError::new("E017", "Expected 'else' keyword", Some(span), false) |
| } |
| BindParserError::ConditionOp(span) => UserError::new( |
| "E018", |
| "Expected a condition operation ('==' or '!='.)", |
| Some(span), |
| false, |
| ), |
| BindParserError::ConditionValue(span) => UserError::new( |
| "E019", |
| "Expected a condition value: string, number, boolean, or identifier", |
| Some(span), |
| false, |
| ), |
| BindParserError::AcceptKeyword(span) => { |
| UserError::new("E020", "Expected 'accept' keyword.", Some(span), false) |
| } |
| BindParserError::AbortKeyword(span) => { |
| UserError::new("E024", "Expected 'abort' keyword.", Some(span), false) |
| } |
| BindParserError::NoStatements(span) => UserError::new( |
| "E021", |
| "Bind programs must contain at least one statement.", |
| Some(span), |
| false, |
| ), |
| BindParserError::Eof(span) => { |
| UserError::new("E025", "Expected end of file.", Some(span), false) |
| } |
| BindParserError::UnterminatedComment => { |
| UserError::new("E023", "Found an unterminated multiline comment.", None, false) |
| } |
| BindParserError::Unknown(span, kind) => UserError::new( |
| "E022", |
| &format!("Unexpected parser error: {:?}.", kind), |
| Some(span), |
| true, |
| ), |
| } |
| } |
| } |
| |
| impl From<CompilerError> for UserError { |
| fn from(error: CompilerError) -> Self { |
| match error { |
| CompilerError::BindParserError(error) => UserError::from(error), |
| CompilerError::DependencyError(error) => UserError::from(error), |
| CompilerError::DuplicateIdentifier(identifier) => UserError::new( |
| "E102", |
| &format!("The identifier `{}` is defined multiple times.", identifier), |
| None, |
| false, |
| ), |
| CompilerError::TypeMismatch(identifier) => UserError::new( |
| "E103", |
| &format!("The identifier `{}` is extended with a mismatched type.", identifier), |
| None, |
| false, |
| ), |
| CompilerError::UnresolvedQualification(identifier) => UserError::new( |
| "E104", |
| &format!("Could not resolve the qualifier on `{}`.", identifier), |
| None, |
| false, |
| ), |
| CompilerError::UndeclaredKey(identifier) => UserError::new( |
| "E105", |
| &format!("Could not find previous definition of extended key `{}`.", identifier), |
| None, |
| false, |
| ), |
| CompilerError::MissingExtendsKeyword(identifier) => UserError::new( |
| "E106", |
| &format!( |
| "Cannot define a key with a namespace: `{}`. Are you missing an `extend`?", |
| identifier |
| ), |
| None, |
| false, |
| ), |
| CompilerError::InvalidExtendsKeyword(identifier) => UserError::new( |
| "E107", |
| &format!("Cannot extend a key with no namespace: `{}`.", identifier), |
| None, |
| false, |
| ), |
| CompilerError::UnknownKey(identifier) => UserError::new( |
| "E108", |
| &format!("Bind program refers to undefined identifier: `{}`.", identifier), |
| None, |
| false, |
| ), |
| CompilerError::IfStatementMustBeTerminal => UserError::new( |
| "E109", |
| "If statements must be the last statement in a block", |
| None, |
| false, |
| ), |
| } |
| } |
| } |
| |
| impl From<DependencyError<CompoundIdentifier>> for UserError { |
| fn from(error: DependencyError<CompoundIdentifier>) -> Self { |
| match error { |
| DependencyError::MissingDependency(library) => { |
| UserError::new("E200", &format!("Missing dependency: {}", library), None, false) |
| } |
| DependencyError::CircularDependency => { |
| UserError::new("E201", "Cicular dependency", None, false) |
| } |
| } |
| } |
| } |
| |
| impl From<offline_debugger::DebuggerError> for UserError { |
| fn from(error: offline_debugger::DebuggerError) -> Self { |
| match error { |
| offline_debugger::DebuggerError::BindParserError(error) => UserError::from(error), |
| offline_debugger::DebuggerError::CompilerError(error) => UserError::from(error), |
| offline_debugger::DebuggerError::DuplicateKey(identifier) => UserError::new( |
| "E300", |
| &format!( |
| "The key `{}` appears multiple times in the device specification.", |
| identifier |
| ), |
| None, |
| false, |
| ), |
| offline_debugger::DebuggerError::MissingLabel => UserError::new( |
| "E301", |
| "Missing label in the bind program symbolic instructions.", |
| None, |
| true, |
| ), |
| offline_debugger::DebuggerError::NoOutcome => UserError::new( |
| "E302", |
| "Reached the end of the symbolic instructions without binding or aborting.", |
| None, |
| true, |
| ), |
| offline_debugger::DebuggerError::IncorrectAstLocation => UserError::new( |
| "E303", |
| "Symbolic instruction has an incorrect AST location.", |
| None, |
| true, |
| ), |
| offline_debugger::DebuggerError::InvalidAstLocation => UserError::new( |
| "E304", |
| "AST location contains an incorrect statement type.", |
| None, |
| true, |
| ), |
| offline_debugger::DebuggerError::UnknownKey(identifier) => UserError::new( |
| "E305", |
| &format!("Statement contains an undefined identifier: `{}`.", identifier), |
| None, |
| true, |
| ), |
| offline_debugger::DebuggerError::InvalidValueSymbol(symbol) => UserError::new( |
| "E306", |
| &format!("Symbol is not a valid value type: `{:?}`.", symbol), |
| None, |
| true, |
| ), |
| } |
| } |
| } |
| |
| impl From<debugger::DebuggerError> for UserError { |
| fn from(error: debugger::DebuggerError) -> Self { |
| match error { |
| debugger::DebuggerError::BindFlagsNotSupported => { |
| UserError::new("E001", "The BIND_FLAGS property is not supported.", None, false) |
| } |
| debugger::DebuggerError::InvalidCondition(condition) => UserError::new( |
| "E002", |
| &format!( |
| "A bind program instruction contained an invalid condition: {}.", |
| condition |
| ), |
| None, |
| true, |
| ), |
| debugger::DebuggerError::InvalidOperation(operation) => UserError::new( |
| "E003", |
| &format!( |
| "A bind program instruction contained an invalid operation: {}.", |
| operation |
| ), |
| None, |
| true, |
| ), |
| debugger::DebuggerError::InvalidAstLocation(ast_location) => UserError::new( |
| "E004", |
| &format!( |
| "A bind program instruction contained an invalid AST location: {}.", |
| ast_location |
| ), |
| None, |
| true, |
| ), |
| debugger::DebuggerError::IncorrectAstLocation => UserError::new( |
| "E005", |
| "The debugger only works with bind programs written in the new bind language.", |
| None, |
| false, |
| ), |
| debugger::DebuggerError::MissingLabel => UserError::new( |
| "E006", |
| "The bind program contained a GOTO with no matching LABEL.", |
| None, |
| true, |
| ), |
| debugger::DebuggerError::MissingBindProtocol => UserError::new( |
| "E007", |
| concat!( |
| "Device doesn't have a BIND_PROTOCOL property. ", |
| "The outcome of the bind program would depend on the device's protocol_id." |
| ), |
| None, |
| false, |
| ), |
| debugger::DebuggerError::NoOutcome => UserError::new( |
| "E008", |
| "Reached the end of the instructions without binding or aborting.", |
| None, |
| true, |
| ), |
| debugger::DebuggerError::DuplicateKey(key) => UserError::new( |
| "E009", |
| &format!("The device has multiple values for the key {:#06x}.", key), |
| None, |
| false, |
| ), |
| debugger::DebuggerError::IncorrectCondition => { |
| UserError::new("E010", "An incorrect condition type was encountered.", None, true) |
| } |
| debugger::DebuggerError::InvalidDeprecatedKey(key) => UserError::new( |
| "E011", |
| &format!("The bind program contained an invalid deprecated key: {:#06x}.", key), |
| None, |
| true, |
| ), |
| } |
| } |
| } |
| |
| impl From<test::TestError> for UserError { |
| fn from(error: test::TestError) -> Self { |
| match error { |
| test::TestError::BindParserError(error) => UserError::from(error), |
| test::TestError::DeviceSpecParserError(error) => UserError::from(error), |
| test::TestError::DebuggerError(error) => UserError::from(error), |
| test::TestError::CompilerError(error) => UserError::from(error), |
| test::TestError::InvalidSchema => { |
| UserError::new("E401", "The test specification JSON schema is invalid.", None, true) |
| } |
| test::TestError::InvalidJsonError => UserError::new( |
| "E402", |
| "The test specification is invalid according to the schema.", |
| None, |
| false, |
| ), |
| test::TestError::JsonParserError(error) => { |
| UserError::new("E403", &format!("Failed to parse JSON: {}.", error), None, false) |
| } |
| } |
| } |
| } |