Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Positional arg after repeated positional arg - prefer compiler error to runtime error #3281

Closed
2 tasks done
nipunn1313 opened this issue Jan 11, 2022 · 4 comments
Closed
2 tasks done
Labels
A-derive Area: #[derive]` macro API C-enhancement Category: Raise on the bar on expectations S-duplicate Status: Closed as Duplicate

Comments

@nipunn1313
Copy link

nipunn1313 commented Jan 11, 2022

Please complete the following tasks

  • I have searched the discussions
  • I have searched the existing issues

Clap Version

3.0.6

Describe your use case

use clap::Parser;

#[derive(clap::Parser, Debug)]
struct Args {
    arg2: Vec<String>,
    arg1: String,
}

fn main() {
    let args = Args::parse();
    dbg!(args);
}

This code is bad. Currently when running it, it fails at runtime

➜  clap_repro cargo run
   Compiling clap-repro v0.1.0 (/Users/nipunn/src/clap_repro)
    Finished dev [unoptimized + debuginfo] target(s) in 0.30s
     Running `target/debug/clap-repro`
thread 'main' panicked at 'Found non-required positional argument with a lower index than a required positional argument: "arg2" index Some(1)', /Users/nipunn/.cargo/registry/src/github.com-1ecc6299db9ec823/clap-3.0.6/src/parse/parser.rs:210:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Describe the solution you'd like

If possible, some kind of compile time error.
Probably would involve a proc-macro to iterate/logic over the struct and bail

Alternatives, if applicable

Status quo runtime error could work, but means you don't catch clear bugs until much later.
If we stick with status quo, a better error message might be nice.

"Found positional argument after a repeated positional argument"

Additional Context

No response

@nipunn1313 nipunn1313 added the C-enhancement Category: Raise on the bar on expectations label Jan 11, 2022
@epage
Copy link
Member

epage commented Jan 11, 2022

#2720 is about supporting all debug asserts in the derive macro. As noted in that issue, there are complications with that. This falls into some of those complications because of flatten making it so we can't fully reason about this case. At best, we can implement a compile error only for when a flatten is not used, leaving it to runtime otherwise.

As noted in #2720, our CHANGELOG and Derive Tutorial's tips, we encourage having a test like below so these will be caught while testing (and for all subcommands, when relevant) as compared to manual testing / using the application.

#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
    ...
}

#[test]
fn verify_app() {
    use clap::IntoApp;
    Cli::into_app().debug_assert()
}

So like #2720, I lean towards closing this issue out as "won't fix". If there are thoughts or concerns about this, let us know!

@epage epage closed this as completed Jan 11, 2022
@epage epage added A-derive Area: #[derive]` macro API S-duplicate Status: Closed as Duplicate labels Jan 11, 2022
@nipunn1313
Copy link
Author

Sounds good. May still be nice to have a better runtime error message!

thread 'main' panicked at 'Found non-required positional argument with a lower index than a required positional argument: "arg2" index Some(1)'

was somewhat misleading to us. To my user, it seemed like it was user-error for providing the args in the wrong order (or something like that) - when actually it was a bug in the specification of the CLI by me.

@nipunn1313
Copy link
Author

I also don't think debug_assert() catches this issue

Here's a cargo play compatible snippit - drop it into a main.rs. (cargo install cargo-play ; cargo play main.rs)

//# clap = {version="3", features=["derive"]}
use clap::{IntoApp, Parser};

#[derive(clap::Parser, Debug)]
struct Args {
    arg2: Vec<String>,
    arg1: String,
}

fn main() {
    Args::into_app().debug_assert();
    let args = Args::parse();
    dbg!(args);
}

If I run it, the debug_assert doesn't catch the issue, but Args::parse() fails at runtime.

@epage
Copy link
Member

epage commented Jan 12, 2022

Interesting! This lives in a completely different place but doesn't need to. My guess is it predates some of how we do assertions today.

epage added a commit to epage/clap that referenced this issue Jan 12, 2022
This will help prevent issues from being deployed to users like in clap-rs#3281

I do not consider this a breaking change because any normal operation
will assert anyways.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-derive Area: #[derive]` macro API C-enhancement Category: Raise on the bar on expectations S-duplicate Status: Closed as Duplicate
Projects
None yet
Development

No branches or pull requests

2 participants