mirror of https://github.com/mtshiba/pylyzer
Implement some errors
This commit is contained in:
parent
957136085f
commit
b755ceb274
|
|
@ -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(|¶m| 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue