Improve def parse errors (#3681)

This commit is contained in:
JT 2021-06-25 17:18:38 +12:00 committed by GitHub
parent a3f119e0bd
commit cac2875c96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 68 deletions

View File

@ -32,7 +32,10 @@ pub enum ParseErrorReason {
Unclosed { delimiter: String, span: Span }, Unclosed { delimiter: String, span: Span },
/// An unexpected internal error has occurred /// An unexpected internal error has occurred
InternalError { message: Spanned<String> }, GeneralError {
message: String,
label: Spanned<String>,
},
/// The parser tried to parse an argument for a command, but it failed for /// The parser tried to parse an argument for a command, but it failed for
/// some reason /// some reason
@ -83,11 +86,15 @@ impl ParseError {
} }
} }
/// Construct a [ParseErrorReason::InternalError](ParseErrorReason::InternalError) /// Construct a [ParseErrorReason::GeneralError](ParseErrorReason::GeneralError)
pub fn internal_error(message: Spanned<impl Into<String>>) -> ParseError { pub fn general_error(
message: impl Into<String>,
label: Spanned<impl Into<String>>,
) -> ParseError {
ParseError { ParseError {
reason: ParseErrorReason::InternalError { reason: ParseErrorReason::GeneralError {
message: message.item.into().spanned(message.span), message: message.into(),
label: label.item.into().spanned(label.span),
}, },
} }
} }
@ -119,11 +126,9 @@ impl From<ParseError> for ShellError {
ParseErrorReason::Mismatch { actual, expected } => { ParseErrorReason::Mismatch { actual, expected } => {
ShellError::type_error(expected, actual) ShellError::type_error(expected, actual)
} }
ParseErrorReason::InternalError { message } => ShellError::labeled_error( ParseErrorReason::GeneralError { message, label } => {
format!("Internal error: {}", message.item), ShellError::labeled_error(&message, &label.item, &label.span)
&message.item, }
&message.span,
),
ParseErrorReason::ArgumentError { command, error } => { ParseErrorReason::ArgumentError { command, error } => {
ShellError::argument_error(command, error) ShellError::argument_error(command, error)
} }

View File

@ -1853,13 +1853,8 @@ fn parse_call(
// Check if it's an internal command // Check if it's an internal command
if let Some(signature) = scope.get_signature(&lite_cmd.parts[0].item) { if let Some(signature) = scope.get_signature(&lite_cmd.parts[0].item) {
if lite_cmd.parts[0].item == "def" { if lite_cmd.parts[0].item == "def" {
let error = parse_definition(&lite_cmd, scope); let err = parse_definition(&lite_cmd, scope);
if error.is_some() { error = error.or(err);
return (
Some(ClassifiedCommand::Expr(Box::new(garbage(lite_cmd.span())))),
error,
);
}
} }
let (mut internal_command, err) = parse_internal_command(&lite_cmd, scope, &signature, 0); let (mut internal_command, err) = parse_internal_command(&lite_cmd, scope, &signature, 0);

View File

@ -8,7 +8,7 @@ use crate::{
use indexmap::IndexMap; use indexmap::IndexMap;
use nu_errors::ParseError; use nu_errors::ParseError;
use nu_protocol::hir::Block; use nu_protocol::hir::Block;
use nu_source::{HasSpan, SpannedItem}; use nu_source::{HasSpan, Span, SpannedItem};
//use crate::errors::{ParseError, ParseResult}; //use crate::errors::{ParseError, ParseResult};
use crate::lex::lexer::{lex, parse_block}; use crate::lex::lexer::{lex, parse_block};
@ -28,7 +28,8 @@ pub(crate) fn parse_definition(call: &LiteCommand, scope: &dyn ParserScope) -> O
// So our main goal here is to parse the block now that the names and // So our main goal here is to parse the block now that the names and
// prototypes of adjacent commands are also available // prototypes of adjacent commands are also available
if call.parts.len() == 4 { match call.parts.len() {
4 => {
if call.parts[0].item != "def" { if call.parts[0].item != "def" {
return Some(ParseError::mismatch("definition", call.parts[0].clone())); return Some(ParseError::mismatch("definition", call.parts[0].clone()));
} }
@ -63,7 +64,8 @@ pub(crate) fn parse_definition(call: &LiteCommand, scope: &dyn ParserScope) -> O
let (mut block, err) = classify_block(&lite_block, scope); let (mut block, err) = classify_block(&lite_block, scope);
scope.exit_scope(); scope.exit_scope();
if let Some(block) = std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut block) if let Some(block) =
std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut block)
{ {
block.params = signature; block.params = signature;
block.params.name = name; block.params.name = name;
@ -75,12 +77,51 @@ pub(crate) fn parse_definition(call: &LiteCommand, scope: &dyn ParserScope) -> O
} }
_ => Some(ParseError::mismatch("body", call.parts[3].clone())), _ => Some(ParseError::mismatch("body", call.parts[3].clone())),
} }
} else { }
Some(ParseError::internal_error(
"Wrong shape. Expected def name [signature] {body}." 3 => Some(ParseError::general_error(
"wrong shape. Expected: def name [signature] {body}",
"expected definition body".to_string().spanned(Span::new(
call.parts[2].span.end(),
call.parts[2].span.end(),
)),
)),
2 => Some(ParseError::general_error(
"wrong shape. Expected: def name [signature] {body}",
"expected definition parameters"
.to_string() .to_string()
.spanned(call.span()), .spanned(Span::new(
)) call.parts[1].span.end(),
call.parts[1].span.end(),
)),
)),
1 => Some(ParseError::general_error(
"wrong shape. Expected: def name [signature] {body}",
"expected definition name".to_string().spanned(Span::new(
call.parts[0].span.end(),
call.parts[0].span.end(),
)),
)),
0 => Some(ParseError::general_error(
"wrong shape. Expected: def name [signature] {body}",
"expected 'def' keyword'".to_string().spanned(call.span()),
)),
x if x < 4 => Some(ParseError::general_error(
"wrong shape. Expected: def name [signature] {body}",
"expected: def name [signature] {body}"
.to_string()
.spanned(Span::new(
call.parts[x - 1].span.end(),
call.parts[x - 1].span.end(),
)),
)),
_ => Some(ParseError::general_error(
"extra arguments given. Expected: def name [signature] {body}.",
"extra argument given"
.to_string()
.spanned(call.parts[4].span()),
)),
} }
} }