Add tests for errors/warns

This commit is contained in:
Shunsuke Shibayama 2022-12-24 12:14:31 +09:00
parent b755ceb274
commit 6dd9af7d09
5 changed files with 49 additions and 18 deletions

View File

@ -525,12 +525,12 @@ impl ASTConverter {
Block::new(new_block) Block::new(new_block)
} }
fn find_init_self(&mut self, init_def: &Def) { fn check_init_sig(&mut self, sig: &Signature) -> Option<()> {
match &init_def.sig { match sig {
Signature::Subr(subr) => { Signature::Subr(subr) => {
if let Some(first) = subr.params.non_defaults.get(0) { if let Some(first) = subr.params.non_defaults.get(0) {
if first.inspect().map(|s| &s[..]) == Some("self") { if first.inspect().map(|s| &s[..]) == Some("self") {
return; return Some(());
} }
} }
self.errs.push(self_not_found_error( self.errs.push(self_not_found_error(
@ -538,6 +538,7 @@ impl ASTConverter {
subr.loc(), subr.loc(),
self.namespace.join("."), self.namespace.join("."),
)); ));
Some(())
} }
Signature::Var(var) => { Signature::Var(var) => {
self.errs.push(init_var_error( self.errs.push(init_var_error(
@ -545,6 +546,7 @@ impl ASTConverter {
var.loc(), var.loc(),
self.namespace.join("."), self.namespace.join("."),
)); ));
None
} }
} }
} }
@ -555,8 +557,8 @@ impl ASTConverter {
// self.z = z // self.z = z
// ↓ // ↓
// {x: Int, y: Int, z: Never}, .__call__(x: Int, y: Int, z: Obj): Self = .unreachable() // {x: Int, y: Int, z: Never}, .__call__(x: Int, y: Int, z: Obj): Self = .unreachable()
fn extract_init(&mut self, init_def: Def) -> (Expr, Def) { fn extract_init(&mut self, init_def: Def) -> Option<(Expr, Def)> {
self.find_init_self(&init_def); self.check_init_sig(&init_def.sig)?;
let l_brace = Token::new(TokenKind::LBrace, "{", init_def.ln_begin().unwrap_or(0), init_def.col_begin().unwrap_or(0)); let l_brace = Token::new(TokenKind::LBrace, "{", init_def.ln_begin().unwrap_or(0), init_def.col_begin().unwrap_or(0));
let r_brace = Token::new(TokenKind::RBrace, "}", init_def.ln_end().unwrap_or(0), init_def.col_end().unwrap_or(0)); let r_brace = Token::new(TokenKind::RBrace, "}", init_def.ln_end().unwrap_or(0), init_def.col_end().unwrap_or(0));
let Signature::Subr(sig) = init_def.sig else { unreachable!() }; let Signature::Subr(sig) = init_def.sig else { unreachable!() };
@ -601,7 +603,7 @@ impl ASTConverter {
let body = Expr::Accessor(Accessor::Ident(unreachable_acc)).call_expr(Args::empty()); let body = Expr::Accessor(Accessor::Ident(unreachable_acc)).call_expr(Args::empty());
let body = DefBody::new(EQUAL, Block::new(vec![body]), DefId(0)); let body = DefBody::new(EQUAL, Block::new(vec![body]), DefId(0));
let def = Def::new(sig, body); let def = Def::new(sig, body);
(Expr::Record(record), def) Some((Expr::Record(record), def))
} }
fn gen_default_init(&self, line: usize) -> Def { fn gen_default_init(&self, line: usize) -> Def {
@ -624,11 +626,12 @@ impl ASTConverter {
let chunk = self.convert_statement(stmt, true); let chunk = self.convert_statement(stmt, true);
match chunk { match chunk {
Expr::Def(def) => { Expr::Def(def) => {
if def.is_subr() && &def.sig.ident().unwrap().inspect()[..] == "__init__" { if def.sig.ident().map(|id| &id.inspect()[..] == "__init__").unwrap_or(false) {
let (base_t, init_def) = self.extract_init(def); if let Some((base_t, init_def)) = self.extract_init(def) {
base_type = Some(base_t); base_type = Some(base_t);
attrs.push(ClassAttr::Def(init_def)); attrs.push(ClassAttr::Def(init_def));
init_is_defined = true; init_is_defined = true;
}
} else { } else {
attrs.push(ClassAttr::Def(def)); attrs.push(ClassAttr::Def(def));
} }
@ -833,13 +836,13 @@ impl ASTConverter {
} => { } => {
// if reassigning of a function referenced by other functions is occurred, it is an error // if reassigning of a function referenced by other functions is occurred, it is an error
if self.get_name(&name).map(|info| info.defined_times > 0 && !info.referenced.difference(&set!{name.clone()}).is_empty()).unwrap_or(false) { if self.get_name(&name).map(|info| info.defined_times > 0 && !info.referenced.difference(&set!{name.clone()}).is_empty()).unwrap_or(false) {
let warn = reassign_func_error( let err = reassign_func_error(
self.cfg.input.clone(), self.cfg.input.clone(),
pyloc_to_ergloc(stmt.location, name.len()), pyloc_to_ergloc(stmt.location, name.len()),
self.namespace.join("."), self.namespace.join("."),
&name &name
); );
self.warns.push(warn); self.errs.push(err);
Expr::Dummy(Dummy::empty()) Expr::Dummy(Dummy::empty())
} else { } else {
let decos = decorator_list.into_iter().map(|ex| Decorator(self.convert_expr(ex))).collect::<HashSet<_>>(); let decos = decorator_list.into_iter().map(|ex| Decorator(self.convert_expr(ex))).collect::<HashSet<_>>();

View File

@ -59,10 +59,10 @@ pub(crate) fn init_var_error(
ErrorCore::new( ErrorCore::new(
vec![SubMessage::only_loc(loc)], vec![SubMessage::only_loc(loc)],
switch_lang!( switch_lang!(
"japanese" => format!("__init__はメソッドです。メンバ変数として宣言することはできません"), "japanese" => format!("`__init__`はメソッドです。メンバ変数として宣言するべきではありません"),
"simplified_chinese" => format!("__init__是方法。不能声明为变量"), "simplified_chinese" => format!("__init__是方法。不能宣告为变量"),
"traditional_chinese" => format!("__init__是方法。不能宣告為變量"), "traditional_chinese" => format!("__init__是方法。不能宣告為變量"),
"english" => format!("__init__ is a method. It cannot be declared as a member variable"), "english" => format!("`__init__` should be a method. It should not be defined as a member variable"),
), ),
3, 3,
ErrorKind::NameError, ErrorKind::NameError,

View File

@ -1,3 +1,5 @@
# E0001
def a(): return 1 def a(): return 1
def a(): return "a" # OK def a(): return "a" # OK
@ -7,3 +9,15 @@ def g(): return f()
def f(): return 1 def f(): return 1
def f(): return "a" # E0001: Reassignment of a function referenced by other functions def f(): return "a" # E0001: Reassignment of a function referenced by other functions
# E0002
class C:
def __init__(self): pass # OK
class C:
def __init__(a): pass # ERR
# E0003
class C:
__init__ = 1 # ERR

View File

@ -46,6 +46,11 @@ fn exec_class() {
} }
#[test] #[test]
fn exec_e0001() { fn exec_errors() {
expect("tests/e0001.py", 1, 0); expect("tests/errors.py", 0, 3);
}
#[test]
fn exec_warns() {
expect("tests/warns.py", 2, 0);
} }

9
tests/warns.py Normal file
View File

@ -0,0 +1,9 @@
# W0188: unused value
1 # Warn
def f(): return "a"
f() # Warn
def f(): return None
f() # OK