diff --git a/src/shell.rs b/src/shell.rs index 87680bf302..52d06b5ad4 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1,6 +1,9 @@ mod helper; -use rustpython_parser::error::{LexicalErrorType, ParseErrorType}; +use rustpython_parser::{ + error::{LexicalErrorType, ParseErrorType}, + token::Tok, +}; use rustpython_vm::{ builtins::PyBaseExceptionRef, compiler::{self, CompileError, CompileErrorBody, CompileErrorType}, @@ -19,7 +22,6 @@ fn shell_exec( vm: &VirtualMachine, source: &str, scope: Scope, - last_row: &mut usize, empty_line_given: bool, continuing: bool, ) -> ShellExecResult { @@ -27,7 +29,6 @@ fn shell_exec( Ok(code) => { if empty_line_given || !continuing { // We want to execute the full code - *last_row = 0; match vm.run_code_obj(code, scope) { Ok(_val) => ShellExecResult::Ok, Err(err) => ShellExecResult::PyErr(err), @@ -54,28 +55,30 @@ fn shell_exec( .. }) => ShellExecResult::Continue, Err(err) => { - // Indent error or something else? - let indent_error = match err.body.error { - CompileErrorType::Parse(ref p) => p.is_indentation_error(), - _ => false, + // bad_error == true if we are handling an error that should be thrown even if we are continuing + // if its an indentation error, set to true if we are continuing and the error is on column 0, + // since indentations errors on columns other than 0 should be ignored. + // if its an unrecognized token for dedent, set to false + + let bad_error = match err.body.error { + CompileErrorType::Parse(ref p) => { + if matches!( + p, + ParseErrorType::Lexical(LexicalErrorType::IndentationError) + ) { + continuing && err.body.location.column() != 0 + } else { + !matches!(p, ParseErrorType::UnrecognizedToken(Tok::Dedent, _)) + } + } + _ => true, // It is a bad error for everything else }; - if indent_error && !empty_line_given { - // The input line is not empty and it threw an indentation error - let l = err.body.location; - - // This is how we can mask unnecesary errors - if l.row() > *last_row { - *last_row = l.row(); - ShellExecResult::Continue - } else { - *last_row = 0; - ShellExecResult::PyErr(vm.new_syntax_error(&err)) - } - } else { - // Throw the error for all other cases - *last_row = 0; + // If we are handling an error on an empty line or an error worthy of throwing + if empty_line_given || bad_error { ShellExecResult::PyErr(vm.new_syntax_error(&err)) + } else { + ShellExecResult::Continue } } } @@ -100,7 +103,6 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { } let mut continuing = false; - let mut last_row: usize = 0; loop { let prompt_name = if continuing { "ps2" } else { "ps1" }; @@ -128,14 +130,7 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { } full_input.push('\n'); - match shell_exec( - vm, - &full_input, - scope.clone(), - &mut last_row, - empty_line_given, - continuing, - ) { + match shell_exec(vm, &full_input, scope.clone(), empty_line_given, continuing) { ShellExecResult::Ok => { if continuing { if empty_line_given { @@ -150,7 +145,6 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { } } else { // We aren't in continue mode so proceed normally - last_row = 0; continuing = false; full_input.clear(); Ok(()) @@ -168,7 +162,6 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { } } ReadlineResult::Interrupt => { - last_row = 0; continuing = false; full_input.clear(); let keyboard_interrupt =