use core::ops::Range; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use nu_parser::ParseError; use nu_protocol::{engine::StateWorkingSet, ShellError, Span}; fn convert_span_to_diag( working_set: &StateWorkingSet, span: &Span, ) -> Result<(usize, Range), Box> { for (file_id, (_, start, end)) in working_set.files().enumerate() { if span.start >= *start && span.end <= *end { let new_start = span.start - start; let new_end = span.end - start; return Ok((file_id, new_start..new_end)); } } panic!("internal error: can't find span in parser state") } pub fn report_parsing_error( working_set: &StateWorkingSet, error: &ParseError, ) -> Result<(), Box> { let writer = StandardStream::stderr(ColorChoice::Always); let config = codespan_reporting::term::Config::default(); let diagnostic = match error { ParseError::Mismatch(expected, found, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Type mismatch during operation") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("expected {}, found {}", expected, found))]) } ParseError::ExtraTokens(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Extra tokens in code") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("extra tokens") ]) } ParseError::ExtraPositional(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Extra positional argument") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message("extra positional argument")]) } ParseError::UnexpectedEof(s, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Unexpected end of code") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("expected {}", s))]) } ParseError::Unclosed(delim, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Unclosed delimiter") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("unclosed {}", delim))]) } ParseError::UnknownStatement(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Unknown statement") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("unknown statement") ]) } ParseError::MultipleRestParams(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Multiple rest params") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message("multiple rest params")]) } ParseError::VariableNotFound(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Variable not found") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("variable not found") ]) } ParseError::UnknownCommand(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Unknown command") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("unknown command") ]) } ParseError::UnknownFlag(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Unknown flag") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("unknown flag") ]) } ParseError::UnknownType(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Unknown type") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("unknown type") ]) } ParseError::MissingFlagParam(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Missing flag param") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message("flag missing parameter")]) } ParseError::ShortFlagBatchCantTakeArg(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Batches of short flags can't take arguments") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message("short flag batches can't take args")]) } ParseError::KeywordMissingArgument(name, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message(format!("Missing argument to {}", name)) .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("missing value that follows {}", name))]) } ParseError::MissingPositional(name, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Missing required positional arg") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("missing {}", name))]) } ParseError::MissingType(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Missing type") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("expected type") ]) } ParseError::TypeMismatch(expected, found, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Type mismatch") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("expected {:?}, found {:?}", expected, found))]) } ParseError::MissingRequiredFlag(name, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Missing required flag") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("missing required flag {}", name))]) } ParseError::IncompleteMathExpression(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Incomplete math expresssion") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message("incomplete math expression")]) } ParseError::UnknownState(name, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Unknown state") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("unknown state {}", name))]) } ParseError::NonUtf8(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Non-UTF8 code") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("non-UTF8 code") ]) } ParseError::Expected(expected, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Parse mismatch during operation") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("expected {}", expected))]) } ParseError::UnsupportedOperation(op_span, lhs_span, lhs_ty, rhs_span, rhs_ty) => { let (lhs_file_id, lhs_range) = convert_span_to_diag(working_set, lhs_span)?; let (rhs_file_id, rhs_range) = convert_span_to_diag(working_set, rhs_span)?; let (op_file_id, op_range) = convert_span_to_diag(working_set, op_span)?; Diagnostic::error() .with_message("Unsupported operation") .with_labels(vec![ Label::primary(op_file_id, op_range) .with_message("doesn't support these values"), Label::secondary(lhs_file_id, lhs_range).with_message(lhs_ty.to_string()), Label::secondary(rhs_file_id, rhs_range).with_message(rhs_ty.to_string()), ]) } ParseError::ExpectedKeyword(expected, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Expected keyword") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("expected {}", expected))]) } ParseError::IncompleteParser(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Parser incomplete") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message("parser support missing for this expression")]) } ParseError::RestNeedsName(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Rest parameter needs a name") .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message("needs a parameter name")]) } }; // println!("DIAG"); // println!("{:?}", diagnostic); codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?; Ok(()) } pub fn report_shell_error( working_set: &StateWorkingSet, error: &ShellError, ) -> Result<(), Box> { let writer = StandardStream::stderr(ColorChoice::Always); let config = codespan_reporting::term::Config::default(); let diagnostic = match error { ShellError::OperatorMismatch { op_span, lhs_ty, lhs_span, rhs_ty, rhs_span, } => { let (lhs_file_id, lhs_range) = convert_span_to_diag(working_set, lhs_span)?; let (rhs_file_id, rhs_range) = convert_span_to_diag(working_set, rhs_span)?; let (op_file_id, op_range) = convert_span_to_diag(working_set, op_span)?; Diagnostic::error() .with_message("Type mismatch during operation") .with_labels(vec![ Label::primary(op_file_id, op_range).with_message("type mismatch for operator"), Label::secondary(lhs_file_id, lhs_range).with_message(lhs_ty.to_string()), Label::secondary(rhs_file_id, rhs_range).with_message(rhs_ty.to_string()), ]) } ShellError::Unsupported(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Unsupported operation") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("unsupported operation") ]) } ShellError::InternalError(s) => { Diagnostic::error().with_message(format!("Internal error: {}", s)) } ShellError::VariableNotFound(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Variable not found") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("variable not found") ]) } ShellError::CantConvert(s, span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message(format!("Can't convert to {}", s)) .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("can't convert to {}", s))]) } ShellError::DivisionByZero(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Division by zero") .with_labels(vec![ Label::primary(diag_file_id, diag_range).with_message("division by zero") ]) } }; // println!("DIAG"); // println!("{:?}", diagnostic); codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?; Ok(()) }