mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Provide caret diagnostics for SyntaxError
visualize syntax error with caret diagnostics in shell, eval, exec, when the error statement and error location are provided.
This commit is contained in:
1 parent
8267ea4624
commit
7f45ed3cca
5 files changed
+57
-14
No files matched your search
@@ -255,6 +255,7 @@ impl<O: OutputStream> Compiler<O> {
|
||||
self.compile_expression(expression)?;
|
||||
} else {
|
||||
return Err(CompileError {
|
||||
statement: None,
|
||||
error: CompileErrorType::ExpectExpr,
|
||||
location: statement.location.clone(),
|
||||
});
|
||||
@@ -533,6 +534,7 @@ impl<O: OutputStream> Compiler<O> {
|
||||
Break => {
|
||||
if !self.ctx.in_loop {
|
||||
return Err(CompileError {
|
||||
statement: None,
|
||||
error: CompileErrorType::InvalidBreak,
|
||||
location: statement.location.clone(),
|
||||
});
|
||||
@@ -542,6 +544,7 @@ impl<O: OutputStream> Compiler<O> {
|
||||
Continue => {
|
||||
if !self.ctx.in_loop {
|
||||
return Err(CompileError {
|
||||
statement: None,
|
||||
error: CompileErrorType::InvalidContinue,
|
||||
location: statement.location.clone(),
|
||||
});
|
||||
@@ -551,6 +554,7 @@ impl<O: OutputStream> Compiler<O> {
|
||||
Return { value } => {
|
||||
if !self.ctx.in_func() {
|
||||
return Err(CompileError {
|
||||
statement: None,
|
||||
error: CompileErrorType::InvalidReturn,
|
||||
location: statement.location.clone(),
|
||||
});
|
||||
@@ -628,6 +632,7 @@ impl<O: OutputStream> Compiler<O> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CompileError {
|
||||
statement: None,
|
||||
error: CompileErrorType::Delete(expression.name()),
|
||||
location: self.current_source_location.clone(),
|
||||
});
|
||||
@@ -1331,6 +1336,7 @@ impl<O: OutputStream> Compiler<O> {
|
||||
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<O: OutputStream> Compiler<O> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CompileError {
|
||||
statement: None,
|
||||
error: CompileErrorType::Assign(target.name()),
|
||||
location: self.current_source_location.clone(),
|
||||
});
|
||||
@@ -1644,6 +1651,7 @@ impl<O: OutputStream> Compiler<O> {
|
||||
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<O: OutputStream> Compiler<O> {
|
||||
}
|
||||
Starred { .. } => {
|
||||
return Err(CompileError {
|
||||
statement: Option::None,
|
||||
error: CompileErrorType::SyntaxError(std::string::String::from(
|
||||
"Invalid starred expression",
|
||||
)),
|
||||
|
||||
+32
-14
@@ -7,13 +7,21 @@ use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompileError {
|
||||
pub statement: Option<String>,
|
||||
pub error: CompileErrorType,
|
||||
pub location: Location,
|
||||
}
|
||||
|
||||
impl CompileError {
|
||||
pub fn update_statement_info(&mut self, statement: String) {
|
||||
self.statement = Some(statement);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ pub struct SymbolTableError {
|
||||
impl From<SymbolTableError> for CompileError {
|
||||
fn from(error: SymbolTableError) -> Self {
|
||||
CompileError {
|
||||
statement: None,
|
||||
error: CompileErrorType::SyntaxError(error.error),
|
||||
location: error.location,
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -1007,6 +1007,10 @@ impl VirtualMachine {
|
||||
) -> Result<PyCodeRef, CompileError> {
|
||||
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 {
|
||||
|
||||
Reference in new issue
Block a user