mirror of https://github.com/mtshiba/pylyzer
feat: support arguments expansion
This commit is contained in:
parent
d448aaf974
commit
f5503d6f9e
|
|
@ -6,7 +6,7 @@ use erg_common::dict::Dict as HashMap;
|
||||||
use erg_common::fresh::FRESH_GEN;
|
use erg_common::fresh::FRESH_GEN;
|
||||||
use erg_common::set::Set as HashSet;
|
use erg_common::set::Set as HashSet;
|
||||||
use erg_common::traits::{Locational, Stream};
|
use erg_common::traits::{Locational, Stream};
|
||||||
use erg_common::{log, set};
|
use erg_common::{fmt_vec, log, set};
|
||||||
use erg_compiler::artifact::IncompleteArtifact;
|
use erg_compiler::artifact::IncompleteArtifact;
|
||||||
use erg_compiler::erg_parser::ast::{
|
use erg_compiler::erg_parser::ast::{
|
||||||
Accessor, Args, BinOp, Block, ClassAttr, ClassAttrs, ClassDef, ConstAccessor, ConstArgs,
|
Accessor, Args, BinOp, Block, ClassAttr, ClassAttrs, ClassDef, ConstAccessor, ConstArgs,
|
||||||
|
|
@ -228,9 +228,20 @@ pub struct TypeVarInfo {
|
||||||
impl fmt::Display for TypeVarInfo {
|
impl fmt::Display for TypeVarInfo {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if let Some(bound) = &self.bound {
|
if let Some(bound) = &self.bound {
|
||||||
write!(f, "TypeVarInfo({} bound={})", self.name, bound)
|
write!(
|
||||||
|
f,
|
||||||
|
"TypeVarInfo({}, [{}], bound={})",
|
||||||
|
self.name,
|
||||||
|
fmt_vec(&self.constraints),
|
||||||
|
bound
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
write!(f, "TypeVarInfo({})", self.name)
|
write!(
|
||||||
|
f,
|
||||||
|
"TypeVarInfo({}, [{}])",
|
||||||
|
self.name,
|
||||||
|
fmt_vec(&self.constraints)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1156,21 +1167,40 @@ impl ASTConverter {
|
||||||
py_ast::Expr::Call(call) => {
|
py_ast::Expr::Call(call) => {
|
||||||
let loc = call.location();
|
let loc = call.location();
|
||||||
let function = self.convert_expr(*call.func);
|
let function = self.convert_expr(*call.func);
|
||||||
let pos_args = call
|
let (pos_args, var_args): (Vec<_>, _) = call
|
||||||
.args
|
.args
|
||||||
|
.into_iter()
|
||||||
|
.partition(|arg| !arg.is_starred_expr());
|
||||||
|
let pos_args = pos_args
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ex| PosArg::new(self.convert_expr(ex)))
|
.map(|ex| PosArg::new(self.convert_expr(ex)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let kw_args = call
|
let var_args = var_args
|
||||||
.keywords
|
.into_iter()
|
||||||
|
.map(|ex| {
|
||||||
|
let py_ast::Expr::Starred(star) = ex else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
PosArg::new(self.convert_expr(*star.value))
|
||||||
|
})
|
||||||
|
.next();
|
||||||
|
let (kw_args, kw_var): (Vec<_>, _) =
|
||||||
|
call.keywords.into_iter().partition(|kw| kw.arg.is_some());
|
||||||
|
let kw_args = kw_args
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|Keyword { arg, value, range }| {
|
.map(|Keyword { arg, value, range }| {
|
||||||
let name = arg.unwrap_or(rustpython_ast::Identifier::new("_"));
|
let name = Token::symbol_with_loc(
|
||||||
let name = Token::symbol_with_loc(name.to_string(), pyloc_to_ergloc(range));
|
arg.unwrap().to_string(),
|
||||||
|
pyloc_to_ergloc(range),
|
||||||
|
);
|
||||||
let ex = self.convert_expr(value);
|
let ex = self.convert_expr(value);
|
||||||
KwArg::new(name, None, ex)
|
KwArg::new(name, None, ex)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect();
|
||||||
|
let kw_var = kw_var
|
||||||
|
.into_iter()
|
||||||
|
.map(|Keyword { value, .. }| PosArg::new(self.convert_expr(value)))
|
||||||
|
.next();
|
||||||
let last_col = pos_args
|
let last_col = pos_args
|
||||||
.last()
|
.last()
|
||||||
.and_then(|last| last.col_end())
|
.and_then(|last| last.col_end())
|
||||||
|
|
@ -1185,7 +1215,7 @@ impl ASTConverter {
|
||||||
let rp = Token::new(TokenKind::RParen, ")", loc.row.get(), last_col);
|
let rp = Token::new(TokenKind::RParen, ")", loc.row.get(), last_col);
|
||||||
(lp, rp)
|
(lp, rp)
|
||||||
};
|
};
|
||||||
let args = Args::new(pos_args, None, kw_args, None, Some(paren));
|
let args = Args::new(pos_args, var_args, kw_args, kw_var, Some(paren));
|
||||||
function.call_expr(args)
|
function.call_expr(args)
|
||||||
}
|
}
|
||||||
py_ast::Expr::BinOp(bin) => {
|
py_ast::Expr::BinOp(bin) => {
|
||||||
|
|
@ -1667,7 +1697,6 @@ impl ASTConverter {
|
||||||
|
|
||||||
fn get_type_bounds(&mut self, type_params: Vec<TypeParam>) -> TypeBoundSpecs {
|
fn get_type_bounds(&mut self, type_params: Vec<TypeParam>) -> TypeBoundSpecs {
|
||||||
let mut bounds = TypeBoundSpecs::empty();
|
let mut bounds = TypeBoundSpecs::empty();
|
||||||
let mut errs = vec![];
|
|
||||||
if type_params.is_empty() {
|
if type_params.is_empty() {
|
||||||
for ty in self.cur_appeared_type_names() {
|
for ty in self.cur_appeared_type_names() {
|
||||||
let name = VarName::from_str(ty.clone().into());
|
let name = VarName::from_str(ty.clone().into());
|
||||||
|
|
@ -1679,17 +1708,6 @@ impl ASTConverter {
|
||||||
let spec = TypeSpecWithOp::new(op, t_spec, bound.clone());
|
let spec = TypeSpecWithOp::new(op, t_spec, bound.clone());
|
||||||
TypeBoundSpec::non_default(name, spec)
|
TypeBoundSpec::non_default(name, spec)
|
||||||
} else if !tv_info.constraints.is_empty() {
|
} else if !tv_info.constraints.is_empty() {
|
||||||
if tv_info.constraints.len() == 1 {
|
|
||||||
let err = CompileError::syntax_error(
|
|
||||||
self.cfg.input.clone(),
|
|
||||||
line!() as usize,
|
|
||||||
pyloc_to_ergloc(type_params[0].range()),
|
|
||||||
self.cur_namespace(),
|
|
||||||
"TypeVar must have at least two constrained types".into(),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
errs.push(err);
|
|
||||||
}
|
|
||||||
let op = Token::dummy(TokenKind::Colon, ":");
|
let op = Token::dummy(TokenKind::Colon, ":");
|
||||||
let mut elems = vec![];
|
let mut elems = vec![];
|
||||||
for constraint in tv_info.constraints.iter() {
|
for constraint in tv_info.constraints.iter() {
|
||||||
|
|
@ -1738,7 +1756,6 @@ impl ASTConverter {
|
||||||
};
|
};
|
||||||
bounds.push(spec);
|
bounds.push(spec);
|
||||||
}
|
}
|
||||||
self.errs.extend(errs);
|
|
||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1942,6 +1959,18 @@ impl ASTConverter {
|
||||||
constraints.push(constr.clone());
|
constraints.push(constr.clone());
|
||||||
nth += 1;
|
nth += 1;
|
||||||
}
|
}
|
||||||
|
if constraints.len() == 1 {
|
||||||
|
let err = CompileError::syntax_error(
|
||||||
|
self.cfg.input.clone(),
|
||||||
|
line!() as usize,
|
||||||
|
call.args.get_nth(1).unwrap().loc(),
|
||||||
|
self.cur_namespace(),
|
||||||
|
"TypeVar must have at least two constrained types"
|
||||||
|
.into(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
self.errs.push(err);
|
||||||
|
}
|
||||||
let bound = call.args.get_with_key("bound").cloned();
|
let bound = call.args.get_with_key("bound").cloned();
|
||||||
let info = TypeVarInfo::new(arg, constraints, bound);
|
let info = TypeVarInfo::new(arg, constraints, bound);
|
||||||
self.define_type_var(name.id.to_string(), info);
|
self.define_type_var(name.id.to_string(), info);
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,17 @@ def f(x, y=1):
|
||||||
print(f(1, 2)) # OK
|
print(f(1, 2)) # OK
|
||||||
print(f(1)) # OK
|
print(f(1)) # OK
|
||||||
print(f(1, y="a")) # ERR
|
print(f(1, y="a")) # ERR
|
||||||
|
|
||||||
|
def g(first, second):
|
||||||
|
pass
|
||||||
|
|
||||||
|
g(**{"first": "bar", "second": 1}) # OK
|
||||||
|
g(**[1, 2]) # ERR
|
||||||
|
g(1, *[2]) # OK
|
||||||
|
g(*[1, 2]) # OK
|
||||||
|
g(1, 2, *[3, 4]) # ERR
|
||||||
|
g(*1) # ERR
|
||||||
|
g(*[1], **{"second": 1}) # OK
|
||||||
|
|
||||||
|
_ = f(1, *[2]) # OK
|
||||||
|
_ = f(**{"x": 1, "y": 2}) # OK
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ fn exec_collection() -> Result<(), String> {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exec_call() -> Result<(), String> {
|
fn exec_call() -> Result<(), String> {
|
||||||
expect("tests/call.py", 0, 3)
|
expect("tests/call.py", 0, 6)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue