Skip to content

Err on unused arguments #207

@sebffischer

Description

@sebffischer

The Callable::match_args() (

R/src/callable/core.rs

Lines 39 to 104 in 79d788a

fn match_args(&self, args: List, stack: &mut CallStack) -> Result<(List, List), Signal> {
let mut formals = self.formals();
let ellipsis: List = List::new();
let matched_args: List = List::new();
// assign named args to corresponding formals
let mut indices: Vec<i32> = Vec::new();
for (i, (maybe_name, value)) in args.pairs_ref().iter().enumerate() {
if let Character::Some(name) = maybe_name {
if let Some((Some(_), _)) = formals.remove_named(name) {
matched_args.push_named(Character::Some(name.clone()), value.clone());
continue;
}
}
indices.push(i as i32);
}
let indices: Vec<Integer> = indices.into_iter().map(Integer::Some).collect();
let subset = Subset::Indices(indices.into());
let args = args.subset(subset).materialize();
// TODO(bug): need to evaluate trailing unassigned params that have
// a default value before popping off remaining trailing params
// remove any Ellipsis param, and any trailing unassigned params
let remainder = formals.pop_trailing();
// backfill unnamed args, populating ellipsis with overflow
for (key, value) in args.iter_pairs() {
match key {
// named args go directly to ellipsis, they did not match a formal
Character::Some(arg) => ellipsis.push_named(Character::Some(arg), value),
// unnamed args populate next formal, or ellipsis if formals exhausted
Character::NA => {
let next_unassigned_formal = formals.remove(0);
if let Some((Some(param), _)) = next_unassigned_formal {
matched_args.push_named(Character::Some(param), value);
} else {
ellipsis.push_named(Character::NA, value);
}
}
}
}
// add back in parameter defaults that weren't filled with args
for (param, default) in formals.into_iter() {
matched_args.push_named(
param.into(),
Obj::Promise(None, default, stack.last_frame().env().clone()),
)
}
if let Some(Expr::Ellipsis(Some(name))) = remainder.get(0) {
matched_args.push_named(Character::Some(name), Obj::List(ellipsis.clone()));
} else if !remainder.is_empty() {
matched_args.push_named(
Character::Some("...".to_string()),
Obj::List(ellipsis.clone()),
);
}
Ok((matched_args, ellipsis))
}
) function always puts unmatched arguments into the ellipsis. But there are functions where no such arguments exist and they should not be matched.

E.g.

f = fn(x) x
f(x = 1, 2)

should throw an error because the second argument 2 is unused.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions