Skip to content

Commit

Permalink
Disallow optional args after positional args
Browse files Browse the repository at this point in the history
  • Loading branch information
juniperparsnips committed Nov 29, 2021
1 parent 2f6ea2f commit 5659e4e
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 2 deletions.
2 changes: 1 addition & 1 deletion examples/pyo3-pytests/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ fn time_with_fold<'p>(
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&PyTzInfo>,
fold: bool,
tzinfo: Option<&PyTzInfo>,
) -> PyResult<&'p PyTime> {
PyTime::new_with_fold(
py,
Expand Down
2 changes: 1 addition & 1 deletion examples/pyo3-pytests/tests/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def test_time_fold(t):
@pytest.mark.xfail(PYPY, reason="Feature not available on PyPy")
@pytest.mark.parametrize("fold", [False, True])
def test_time_fold(fold):
t = rdt.time_with_fold(0, 0, 0, 0, None, fold)
t = rdt.time_with_fold(0, 0, 0, 0, fold, None)
assert t.fold == fold


Expand Down
8 changes: 8 additions & 0 deletions pyo3-macros-backend/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub fn impl_arg_params(
let mut required_positional_parameters = 0usize;
let mut keyword_only_parameters = Vec::new();

let mut all_positional_required = true;

for arg in spec.args.iter() {
if arg.py || is_args(&spec.attrs, arg.name) || is_kwargs(&spec.attrs, arg.name) {
continue;
Expand All @@ -100,7 +102,13 @@ pub fn impl_arg_params(
});
} else {
if required {
ensure_spanned!(
all_positional_required,
arg.name.span() => "Required positional parameters can't come after optional parameters"
);
required_positional_parameters += 1;
} else {
all_positional_required = false;
}
if posonly {
positional_only_parameters += 1;
Expand Down
3 changes: 3 additions & 0 deletions tests/ui/invalid_pyfunctions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ fn impl_trait_function(impl_trait: impl AsRef<PyAny>) {}
#[pyfunction]
async fn async_function() {}

#[pyfunction]
fn required_arg_after_optional(optional: Option<isize>, required: isize) {}

fn main() {}
6 changes: 6 additions & 0 deletions tests/ui/invalid_pyfunctions.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ Additional crates such as `pyo3-asyncio` can be used to integrate async Rust and
|
10 | async fn async_function() {}
| ^^^^^

error: Required positional parameters can't come after optional parameters
--> tests/ui/invalid_pyfunctions.rs:13:57
|
13 | fn required_arg_after_optional(optional: Option<isize>, required: isize) {}
| ^^^^^^^^
11 changes: 11 additions & 0 deletions tests/ui/invalid_pymethods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,15 @@ impl MyClass {
fn method_cannot_pass_module(&self, m: &PyModule) {}
}

#[pymethods]
impl MyClass {
fn required_arg_after_optional(&self, optional: Option<isize>, required: isize) {}
}

#[pymethods]
impl MyClass {
#[args(has_default = "1")]
fn default_arg_before_required(&self, has_default: isize, required: isize) {}
}

fn main() {}
12 changes: 12 additions & 0 deletions tests/ui/invalid_pymethods.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,15 @@ error: `pass_module` cannot be used on Python methods
|
112 | #[pyo3(pass_module)]
| ^^^^^^^^^^^

error: Required positional parameters can't come after optional parameters
--> tests/ui/invalid_pymethods.rs:118:68
|
118 | fn required_arg_after_optional(&self, optional: Option<isize>, required: isize) {}
| ^^^^^^^^

error: Required positional parameters can't come after optional parameters
--> tests/ui/invalid_pymethods.rs:124:63
|
124 | fn default_arg_before_required(&self, has_default: isize, required: isize) {}
| ^^^^^^^^

0 comments on commit 5659e4e

Please sign in to comment.