mirror of
https://github.com/nushell/nushell.git
synced 2025-05-24 00:21:17 +00:00
293 lines
14 KiB
Rust
293 lines
14 KiB
Rust
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<usize>), Box<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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::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")])
|
|
}
|
|
};
|
|
|
|
// 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<dyn std::error::Error>> {
|
|
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(())
|
|
}
|