Implement some errors

This commit is contained in:
Shunsuke Shibayama 2022-12-24 11:57:12 +09:00
parent 957136085f
commit b755ceb274
5 changed files with 120 additions and 23 deletions

View File

@ -1,7 +1,7 @@
use erg_common::config::ErgConfig; use erg_common::config::ErgConfig;
use erg_common::fresh::fresh_varname; use erg_common::fresh::fresh_varname;
use erg_common::traits::{Locational, Stream}; use erg_common::traits::{Locational, Stream};
use erg_compiler::artifact::CompleteArtifact; use erg_compiler::artifact::{IncompleteArtifact};
use rustpython_parser::ast::{StatementType, ExpressionType, Located, Program, Number, StringGroup, Operator, BooleanOperator, UnaryOperator, Suite, Parameters, Parameter, Comparison}; use rustpython_parser::ast::{StatementType, ExpressionType, Located, Program, Number, StringGroup, Operator, BooleanOperator, UnaryOperator, Suite, Parameters, Parameter, Comparison};
use rustpython_parser::ast::Location as PyLocation; use rustpython_parser::ast::Location as PyLocation;
@ -181,6 +181,7 @@ pub struct ASTConverter {
/// Erg does not allow variables to be defined multiple times, so rename them using this /// Erg does not allow variables to be defined multiple times, so rename them using this
names: HashMap<String, NameInfo>, names: HashMap<String, NameInfo>,
warns: CompileErrors, warns: CompileErrors,
errs: CompileErrors,
} }
impl ASTConverter { impl ASTConverter {
@ -191,6 +192,7 @@ impl ASTConverter {
namespace: vec![String::from("<module>")], namespace: vec![String::from("<module>")],
names: HashMap::new(), names: HashMap::new(),
warns: CompileErrors::empty(), warns: CompileErrors::empty(),
errs: CompileErrors::empty(),
} }
} }
@ -523,35 +525,63 @@ impl ASTConverter {
Block::new(new_block) Block::new(new_block)
} }
// def __init__(self, x: Int, y: Int): fn find_init_self(&mut self, init_def: &Def) {
match &init_def.sig {
Signature::Subr(subr) => {
if let Some(first) = subr.params.non_defaults.get(0) {
if first.inspect().map(|s| &s[..]) == Some("self") {
return;
}
}
self.errs.push(self_not_found_error(
self.cfg.input.clone(),
subr.loc(),
self.namespace.join("."),
));
}
Signature::Var(var) => {
self.errs.push(init_var_error(
self.cfg.input.clone(),
var.loc(),
self.namespace.join("."),
));
}
}
}
// def __init__(self, x: Int, y: Int, z):
// self.x = x // self.x = x
// self.y = y // self.y = y
// self.z = z
// ↓ // ↓
// {x: Int, y: Int}, .__call__(x: Int, y: Int): Self = .unreachable() // {x: Int, y: Int, z: Never}, .__call__(x: Int, y: Int, z: Obj): Self = .unreachable()
fn extract_init(&self, def: Def) -> (Expr, Def) { fn extract_init(&mut self, init_def: Def) -> (Expr, Def) {
let l_brace = Token::new(TokenKind::LBrace, "{", def.ln_begin().unwrap_or(0), def.col_begin().unwrap_or(0)); self.find_init_self(&init_def);
let r_brace = Token::new(TokenKind::RBrace, "}", def.ln_end().unwrap_or(0), def.col_end().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 Signature::Subr(sig) = def.sig else { unreachable!() }; 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 mut fields = vec![]; let mut fields = vec![];
let mut params = vec![]; let mut params = vec![];
for chunk in def.body.block { for chunk in init_def.body.block {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match chunk { match chunk {
Expr::AttrDef(adef) => { Expr::AttrDef(adef) => {
let Accessor::Attr(attr) = adef.attr else { break; }; let Accessor::Attr(attr) = adef.attr else { break; };
if attr.obj.get_name().map(|s| &s[..]) == Some("self") { if attr.obj.get_name().map(|s| &s[..]) == Some("self") {
let typ_name = if let Some(t_spec_op) = sig.params.non_defaults.iter() let (param_typ_name, arg_typ_name) = if let Some(t_spec_op) = sig.params.non_defaults.iter()
.find(|&param| param.inspect() == Some(attr.ident.inspect())) .find(|&param| param.inspect() == Some(attr.ident.inspect()))
.and_then(|param| param.t_spec.as_ref()) { .and_then(|param| param.t_spec.as_ref()) {
t_spec_op.t_spec.to_string().replace('.', "") let typ_name = t_spec_op.t_spec.to_string().replace('.', "");
(typ_name.clone(), typ_name)
} else { } else {
"Never".to_string() ("Obj".to_string(), "Never".to_string()) // accept any type, can be any type
}; };
let typ_ident = Identifier::public_with_line(DOT, typ_name.into(), attr.obj.ln_begin().unwrap_or(0)); let param_typ_ident = Identifier::public_with_line(DOT, param_typ_name.into(), attr.obj.ln_begin().unwrap_or(0));
let typ_spec = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(typ_ident.clone(), ConstArgs::empty()))); let param_typ_spec = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(param_typ_ident.clone(), ConstArgs::empty())));
let typ_spec = TypeSpecWithOp::new(COLON, typ_spec); let param_typ_spec = TypeSpecWithOp::new(COLON, param_typ_spec);
params.push(NonDefaultParamSignature::new(ParamPattern::VarName(attr.ident.name.clone()), Some(typ_spec))); let arg_typ_ident = Identifier::public_with_line(DOT, arg_typ_name.into(), attr.obj.ln_begin().unwrap_or(0));
let typ = Expr::Accessor(Accessor::Ident(typ_ident)); params.push(NonDefaultParamSignature::new(ParamPattern::VarName(attr.ident.name.clone()), Some(param_typ_spec)));
let typ = Expr::Accessor(Accessor::Ident(arg_typ_ident));
let sig = Signature::Var(VarSignature::new(VarPattern::Ident(attr.ident), None)); let sig = Signature::Var(VarSignature::new(VarPattern::Ident(attr.ident), None));
let body = DefBody::new(EQUAL, Block::new(vec![typ]), DefId(0)); let body = DefBody::new(EQUAL, Block::new(vec![typ]), DefId(0));
let field_type_def = Def::new(sig, body); let field_type_def = Def::new(sig, body);
@ -964,10 +994,10 @@ impl ASTConverter {
} }
} }
pub fn convert_program(mut self, program: Program) -> CompleteArtifact<Module> { pub fn convert_program(mut self, program: Program) -> IncompleteArtifact<Module> {
let program = program.statements.into_iter() let program = program.statements.into_iter()
.map(|stmt| self.convert_statement(stmt, true)) .map(|stmt| self.convert_statement(stmt, true))
.collect(); .collect();
CompleteArtifact::new(Module::new(program), self.warns) IncompleteArtifact::new(Some(Module::new(program)), self.errs, self.warns)
} }
} }

View File

@ -26,3 +26,49 @@ pub(crate) fn reassign_func_error(
caused_by, caused_by,
) )
} }
pub(crate) fn self_not_found_error(
input: Input,
loc: Location,
caused_by: String,
) -> CompileError {
CompileError::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("このメソッドは第一引数にselfを取るべきですが、見つかりませんでした"),
"simplified_chinese" => format!("该方法应该有第一个参数self但是没有找到"),
"traditional_chinese" => format!("該方法應該有第一個參數self但是沒有找到"),
"english" => format!("This method should have the first parameter `self`, but it was not found"),
),
2,
ErrorKind::NameError,
loc,
),
input,
caused_by,
)
}
pub(crate) fn init_var_error(
input: Input,
loc: Location,
caused_by: String,
) -> CompileError {
CompileError::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("__init__はメソッドです。メンバ変数として宣言することはできません"),
"simplified_chinese" => format!("__init__是方法。不能声明为变量"),
"traditional_chinese" => format!("__init__是方法。不能宣告為變量"),
"english" => format!("__init__ is a method. It cannot be declared as a member variable"),
),
3,
ErrorKind::NameError,
loc,
),
input,
caused_by,
)
}

View File

@ -10,3 +10,17 @@ def f(): return "a" # E0001: Reassignment of a function referenced by other func
print(g()) print(g())
``` ```
## E0002: `__init__` doesn't have a first parameter named `self`
```python
class C:
def __init__(a): pass # E0002
```
## E0003: `__init__` as a member variable
```python
class C:
__init__ = 1 # E0003
```

View File

@ -87,17 +87,22 @@ impl PythonAnalyzer {
IncompleteArtifact::new(None, CompileErrors::from(err), CompileErrors::empty()) IncompleteArtifact::new(None, CompileErrors::from(err), CompileErrors::empty())
})?; })?;
let converter = py2erg::ASTConverter::new(self.cfg.copy(), ShadowingMode::Invisible); let converter = py2erg::ASTConverter::new(self.cfg.copy(), ShadowingMode::Invisible);
let CompleteArtifact{ object: erg_module, mut warns } = converter.convert_program(py_program); let IncompleteArtifact{ object: Some(erg_module), mut errors, mut warns } = converter.convert_program(py_program) else { unreachable!() };
let erg_ast = AST::new(erg_common::Str::rc(filename), erg_module); let erg_ast = AST::new(erg_common::Str::rc(filename), erg_module);
erg_common::log!("AST: {erg_ast}"); erg_common::log!("AST: {erg_ast}");
match self.checker.lower(erg_ast, mode) { match self.checker.lower(erg_ast, mode) {
Ok(mut artifact) => { Ok(mut artifact) => {
artifact.warns.extend(warns); artifact.warns.extend(warns);
artifact.warns = handle_err::filter_errors(self.checker.get_mod_ctx(), artifact.warns); artifact.warns = handle_err::filter_errors(self.checker.get_mod_ctx(), artifact.warns);
if errors.is_empty() {
Ok(artifact) Ok(artifact)
} else {
Err(IncompleteArtifact::new(Some(artifact.object), errors, artifact.warns))
}
} }
Err(iart) => { Err(iart) => {
let errors = handle_err::filter_errors(self.checker.get_mod_ctx(), iart.errors); errors.extend(iart.errors);
let errors = handle_err::filter_errors(self.checker.get_mod_ctx(), errors);
warns.extend(iart.warns); warns.extend(iart.warns);
let warns = handle_err::filter_errors(self.checker.get_mod_ctx(), warns); let warns = handle_err::filter_errors(self.checker.get_mod_ctx(), warns);
Err(IncompleteArtifact::new(iart.object, errors, warns)) Err(IncompleteArtifact::new(iart.object, errors, warns))

View File

@ -8,13 +8,15 @@ class x(): pass
y = x() y = x()
class C: class C:
def __init__(x: int): def __init__(self, x: int, y): # y: Obj
self.x = x self.x = x
self.y = y # y: Never
def method(self): def method(self):
return self.x return self.x
c = C(1) c = C(1, 2)
assert c.x == 1 assert c.x == 1
assert c.y == 2 # OK, c.y == "a" is also OK
a = c.method() # OK a = c.method() # OK
_: int = a + 1 _: int = a + 1
b = C("a").method() # ERR b = C("a").method() # ERR