use core::ops::Range; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use nu_engine::ShellError; use nu_parser::{ParseError, ParserWorkingSet, Span}; fn convert_span_to_diag( working_set: &ParserWorkingSet, 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: &ParserWorkingSet, error: &ParseError, ) -> Result<(), Box> { let writer = StandardStream::stderr(ColorChoice::Always); let config = codespan_reporting::term::Config::default(); let diagnostic = match error { ParseError::Mismatch(missing, 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 {}", missing))]) } 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::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(ty, 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 {:?}", ty))]) } 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") ]) } }; // println!("DIAG"); // println!("{:?}", diagnostic); codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?; Ok(()) } pub fn report_shell_error( working_set: &ParserWorkingSet, 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))]) } }; // println!("DIAG"); // println!("{:?}", diagnostic); codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?; Ok(()) }