diff --git a/compiler/src/error.rs b/compiler/src/error.rs index 679a914bf..62546ddb4 100644 --- a/compiler/src/error.rs +++ b/compiler/src/error.rs @@ -1,4 +1,4 @@ -use rustpython_parser::error::{ParseError, ParseErrorType}; +use rustpython_parser::error::{LexicalErrorType, ParseError, ParseErrorType}; use rustpython_parser::location::Location; use std::error::Error; @@ -40,6 +40,19 @@ pub enum CompileErrorType { InvalidYield, } +impl CompileError { + pub fn is_tab_error(&self) -> bool { + if let CompileErrorType::Parse(parse) = &self.error { + if let ParseErrorType::Lexical(lex) = parse { + if let LexicalErrorType::TabError = lex { + return true; + } + } + } + false + } +} + impl fmt::Display for CompileError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self.error { diff --git a/parser/src/error.rs b/parser/src/error.rs index cc34abac0..febef7c33 100644 --- a/parser/src/error.rs +++ b/parser/src/error.rs @@ -20,6 +20,7 @@ pub enum LexicalErrorType { StringError, UnicodeError, NestingError, + TabError, DefaultArgumentError, PositionalArgumentError, DuplicateKeywordArgumentError, @@ -35,6 +36,9 @@ impl fmt::Display for LexicalErrorType { LexicalErrorType::FStringError(error) => write!(f, "Got error in f-string: {}", error), LexicalErrorType::UnicodeError => write!(f, "Got unexpected unicode"), LexicalErrorType::NestingError => write!(f, "Got unexpected nesting"), + LexicalErrorType::TabError => { + write!(f, "inconsistent use of tabs and spaces in indentation") + } LexicalErrorType::DefaultArgumentError => { write!(f, "non-default argument follows default argument") } diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 5d11514dc..86ff86400 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -38,9 +38,7 @@ impl IndentationLevel { } else { Err(LexicalError { location, - error: LexicalErrorType::OtherError( - "inconsistent use of tabs and spaces in indentation".to_string(), - ), + error: LexicalErrorType::TabError, }) } } else if self.tabs > other.tabs { @@ -49,9 +47,7 @@ impl IndentationLevel { } else { Err(LexicalError { location, - error: LexicalErrorType::OtherError( - "inconsistent use of tabs and spaces in indentation".to_string(), - ), + error: LexicalErrorType::TabError, }) } } else { diff --git a/tests/snippets/invalid_syntax.py b/tests/snippets/invalid_syntax.py index 8a4c8a738..faa3d2474 100644 --- a/tests/snippets/invalid_syntax.py +++ b/tests/snippets/invalid_syntax.py @@ -14,5 +14,14 @@ except SyntaxError as ex: else: raise AssertionError("Must throw syntax error") +src = """ +if True: + pass +\tpass +""" + +with assert_raises(TabError): + compile(src, '', 'exec') + with assert_raises(SyntaxError): compile('0xX', 'test.py', 'exec') diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index c7e11a027..f19753f7d 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -850,6 +850,8 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) { "RuntimeError" => ctx.exceptions.runtime_error.clone(), "ReferenceError" => ctx.exceptions.reference_error.clone(), "SyntaxError" => ctx.exceptions.syntax_error.clone(), + "IndentationError" => ctx.exceptions.indentation_error.clone(), + "TabError" => ctx.exceptions.tab_error.clone(), "NotImplementedError" => ctx.exceptions.not_implemented_error.clone(), "TypeError" => ctx.exceptions.type_error.clone(), "ValueError" => ctx.exceptions.value_error.clone(), diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index b6d9f33a2..7193cd036 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -229,6 +229,8 @@ pub struct ExceptionZoo { pub runtime_error: PyClassRef, pub stop_iteration: PyClassRef, pub syntax_error: PyClassRef, + pub indentation_error: PyClassRef, + pub tab_error: PyClassRef, pub system_error: PyClassRef, pub type_error: PyClassRef, pub value_error: PyClassRef, @@ -284,6 +286,8 @@ impl ExceptionZoo { let permission_error = create_type("PermissionError", &type_type, &os_error); let file_exists_error = create_type("FileExistsError", &type_type, &os_error); let eof_error = create_type("EOFError", &type_type, &exception_type); + let indentation_error = create_type("IndentationError", &type_type, &syntax_error); + let tab_error = create_type("TabError", &type_type, &indentation_error); let unicode_error = create_type("UnicodeError", &type_type, &value_error); let unicode_decode_error = create_type("UnicodeDecodeError", &type_type, &unicode_error); let unicode_encode_error = create_type("UnicodeEncodeError", &type_type, &unicode_error); @@ -327,6 +331,8 @@ impl ExceptionZoo { runtime_error, stop_iteration, syntax_error, + indentation_error, + tab_error, system_error, type_error, value_error, diff --git a/vm/src/vm.rs b/vm/src/vm.rs index ebcb0a4c4..b64cd349c 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -397,7 +397,11 @@ impl VirtualMachine { #[cfg(feature = "rustpython-compiler")] pub fn new_syntax_error(&self, error: &CompileError) -> PyObjectRef { - let syntax_error_type = self.ctx.exceptions.syntax_error.clone(); + let syntax_error_type = if error.is_tab_error() { + self.ctx.exceptions.tab_error.clone() + } else { + self.ctx.exceptions.syntax_error.clone() + }; let syntax_error = self.new_exception(syntax_error_type, error.to_string()); let lineno = self.new_int(error.location.row()); self.set_attr(&syntax_error, "lineno", lineno).unwrap();