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::fresh::fresh_varname;
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::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
names: HashMap<String, NameInfo>,
warns: CompileErrors,
errs: CompileErrors,
}
impl ASTConverter {
@ -191,6 +192,7 @@ impl ASTConverter {
namespace: vec![String::from("<module>")],
names: HashMap::new(),
warns: CompileErrors::empty(),
errs: CompileErrors::empty(),
}
}
@ -523,35 +525,63 @@ impl ASTConverter {
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.y = y
// self.z = z
// ↓
// {x: Int, y: Int}, .__call__(x: Int, y: Int): Self = .unreachable()
fn extract_init(&self, def: Def) -> (Expr, Def) {
let l_brace = Token::new(TokenKind::LBrace, "{", def.ln_begin().unwrap_or(0), def.col_begin().unwrap_or(0));
let r_brace = Token::new(TokenKind::RBrace, "}", def.ln_end().unwrap_or(0), def.col_end().unwrap_or(0));
let Signature::Subr(sig) = def.sig else { 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) {
self.find_init_self(&init_def);
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 Signature::Subr(sig) = init_def.sig else { unreachable!() };
let mut fields = vec![];
let mut params = vec![];
for chunk in def.body.block {
for chunk in init_def.body.block {
#[allow(clippy::single_match)]
match chunk {
Expr::AttrDef(adef) => {
let Accessor::Attr(attr) = adef.attr else { break; };
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()))
.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 {
"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 typ_spec = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(typ_ident.clone(), ConstArgs::empty())));
let typ_spec = TypeSpecWithOp::new(COLON, typ_spec);
params.push(NonDefaultParamSignature::new(ParamPattern::VarName(attr.ident.name.clone()), Some(typ_spec)));
let typ = Expr::Accessor(Accessor::Ident(typ_ident));
let param_typ_ident = Identifier::public_with_line(DOT, param_typ_name.into(), attr.obj.ln_begin().unwrap_or(0));
let param_typ_spec = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(param_typ_ident.clone(), ConstArgs::empty())));
let param_typ_spec = TypeSpecWithOp::new(COLON, param_typ_spec);
let arg_typ_ident = Identifier::public_with_line(DOT, arg_typ_name.into(), attr.obj.ln_begin().unwrap_or(0));
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 body = DefBody::new(EQUAL, Block::new(vec![typ]), DefId(0));
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()
.map(|stmt| self.convert_statement(stmt, true))
.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,
)
}
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())
```
## 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())
})?;
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);
erg_common::log!("AST: {erg_ast}");
match self.checker.lower(erg_ast, mode) {
Ok(mut artifact) => {
artifact.warns.extend(warns);
artifact.warns = handle_err::filter_errors(self.checker.get_mod_ctx(), artifact.warns);
Ok(artifact)
if errors.is_empty() {
Ok(artifact)
} else {
Err(IncompleteArtifact::new(Some(artifact.object), errors, artifact.warns))
}
}
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);
let warns = handle_err::filter_errors(self.checker.get_mod_ctx(), warns);
Err(IncompleteArtifact::new(iart.object, errors, warns))

View File

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