From 7f45ed3cca9abf5d6c1962fb727dda52f2afeadb Mon Sep 17 00:00:00 2001 From: yanganto Date: Thu, 21 Nov 2019 17:04:32 +0800 Subject: [PATCH] Provide caret diagnostics for SyntaxError visualize syntax error with caret diagnostics in shell, eval, exec, when the error statement and error location are provided. --- compiler/src/compile.rs | 9 ++++++++ compiler/src/error.rs | 46 ++++++++++++++++++++++++++----------- compiler/src/symboltable.rs | 1 + parser/src/location.rs | 11 +++++++++ vm/src/vm.rs | 4 ++++ 5 files changed, 57 insertions(+), 14 deletions(-) diff --git a/compiler/src/compile.rs b/compiler/src/compile.rs index c2400d6b3..e4bf20844 100644 --- a/compiler/src/compile.rs +++ b/compiler/src/compile.rs @@ -255,6 +255,7 @@ impl Compiler { self.compile_expression(expression)?; } else { return Err(CompileError { + statement: None, error: CompileErrorType::ExpectExpr, location: statement.location.clone(), }); @@ -533,6 +534,7 @@ impl Compiler { Break => { if !self.ctx.in_loop { return Err(CompileError { + statement: None, error: CompileErrorType::InvalidBreak, location: statement.location.clone(), }); @@ -542,6 +544,7 @@ impl Compiler { Continue => { if !self.ctx.in_loop { return Err(CompileError { + statement: None, error: CompileErrorType::InvalidContinue, location: statement.location.clone(), }); @@ -551,6 +554,7 @@ impl Compiler { Return { value } => { if !self.ctx.in_func() { return Err(CompileError { + statement: None, error: CompileErrorType::InvalidReturn, location: statement.location.clone(), }); @@ -628,6 +632,7 @@ impl Compiler { } _ => { return Err(CompileError { + statement: None, error: CompileErrorType::Delete(expression.name()), location: self.current_source_location.clone(), }); @@ -1331,6 +1336,7 @@ impl Compiler { if let ast::ExpressionType::Starred { .. } = &element.node { if seen_star { return Err(CompileError { + statement: None, error: CompileErrorType::StarArgs, location: self.current_source_location.clone(), }); @@ -1360,6 +1366,7 @@ impl Compiler { } _ => { return Err(CompileError { + statement: None, error: CompileErrorType::Assign(target.name()), location: self.current_source_location.clone(), }); @@ -1644,6 +1651,7 @@ impl Compiler { Yield { value } => { if !self.ctx.in_func() { return Err(CompileError { + statement: Option::None, error: CompileErrorType::InvalidYield, location: self.current_source_location.clone(), }); @@ -1738,6 +1746,7 @@ impl Compiler { } Starred { .. } => { return Err(CompileError { + statement: Option::None, error: CompileErrorType::SyntaxError(std::string::String::from( "Invalid starred expression", )), diff --git a/compiler/src/error.rs b/compiler/src/error.rs index 60ea73396..346ff3ac3 100644 --- a/compiler/src/error.rs +++ b/compiler/src/error.rs @@ -7,13 +7,21 @@ use std::fmt; #[derive(Debug)] pub struct CompileError { + pub statement: Option, pub error: CompileErrorType, pub location: Location, } +impl CompileError { + pub fn update_statement_info(&mut self, statement: String) { + self.statement = Some(statement); + } +} + impl From for CompileError { fn from(error: ParseError) -> Self { CompileError { + statement: None, error: CompileErrorType::Parse(error.error), location: error.location, } @@ -70,21 +78,31 @@ impl CompileError { impl fmt::Display for CompileError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match &self.error { - CompileErrorType::Assign(target) => write!(f, "can't assign to {}", target), - CompileErrorType::Delete(target) => write!(f, "can't delete {}", target), - CompileErrorType::ExpectExpr => write!(f, "Expecting expression, got statement"), - CompileErrorType::Parse(err) => write!(f, "{}", err), - CompileErrorType::SyntaxError(err) => write!(f, "{}", err), - CompileErrorType::StarArgs => write!(f, "Two starred expressions in assignment"), - CompileErrorType::InvalidBreak => write!(f, "'break' outside loop"), - CompileErrorType::InvalidContinue => write!(f, "'continue' outside loop"), - CompileErrorType::InvalidReturn => write!(f, "'return' outside function"), - CompileErrorType::InvalidYield => write!(f, "'yield' outside function"), - }?; + let error_desc = match &self.error { + CompileErrorType::Assign(target) => format!("can't assign to {}", target), + CompileErrorType::Delete(target) => format!("can't delete {}", target), + CompileErrorType::ExpectExpr => "Expecting expression, got statement".to_string(), + CompileErrorType::Parse(err) => err.to_string(), + CompileErrorType::SyntaxError(err) => err.to_string(), + CompileErrorType::StarArgs => "Two starred expressions in assignment".to_string(), + CompileErrorType::InvalidBreak => "'break' outside loop".to_string(), + CompileErrorType::InvalidContinue => "'continue' outside loop".to_string(), + CompileErrorType::InvalidReturn => "'return' outside function".to_string(), + CompileErrorType::InvalidYield => "'yield' outside function".to_string(), + }; - // Print line number: - write!(f, " at {}", self.location) + if self.statement.is_some() && self.location.column() > 0 { + // visualize the error, when location and statement are provided + write!( + f, + "\n{}\n{}", + self.statement.clone().unwrap(), + self.location.visualize(&error_desc) + ) + } else { + // print line number + write!(f, "{} at {}", error_desc, self.location) + } } } diff --git a/compiler/src/symboltable.rs b/compiler/src/symboltable.rs index 61a76ddfa..217265b46 100644 --- a/compiler/src/symboltable.rs +++ b/compiler/src/symboltable.rs @@ -142,6 +142,7 @@ pub struct SymbolTableError { impl From for CompileError { fn from(error: SymbolTableError) -> Self { CompileError { + statement: None, error: CompileErrorType::SyntaxError(error.error), location: error.location, } diff --git a/parser/src/location.rs b/parser/src/location.rs index 463c8f555..c1f8c158a 100644 --- a/parser/src/location.rs +++ b/parser/src/location.rs @@ -15,6 +15,17 @@ impl fmt::Display for Location { } } +impl Location { + pub fn visualize(&self, desc: &str) -> String { + format!( + "{}↑\n{}{}", + " ".repeat(self.column - 1), + " ".repeat(self.column - 1), + desc + ) + } +} + impl Location { pub fn new(row: usize, column: usize) -> Self { Location { row, column } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 85f50e820..052383361 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -1007,6 +1007,10 @@ impl VirtualMachine { ) -> Result { compile::compile(source, mode, source_path, self.settings.optimize) .map(|codeobj| PyCode::new(codeobj).into_ref(self)) + .map_err(|mut compile_error| { + compile_error.update_statement_info(source.trim_end().to_string()); + compile_error + }) } pub fn _sub(&self, a: PyObjectRef, b: PyObjectRef) -> PyResult {