clap: AppSettings::SubcommandsNegateReqs may cause panic in Clap::parse(), Clap::try_parse()

Clap version

3.0.0-beta.2

Where

https://docs.rs/clap/3.0.0-beta.2/clap/enum.AppSettings.html#variant.SubcommandsNegateReqs

What’s wrong

There’s no mention that this AppSetting can cause the Clap derive macro to panic on parse() and try_parse()

How to fix

use clap::{AppSettings, Clap, IntoApp, FromArgMatches};

#[derive(Debug, Clap)]
#[clap(setting(AppSettings::SubcommandsNegateReqs))]
struct ExOpts {
    
    req_str: String,

    #[clap(subcommand)]
    pub cmd: Option<SubCommands>,
}

#[derive(Debug, Clap)]
enum SubCommands{
    ExSub {
        #[clap(short, long, parse(from_occurrences))]
        verbose: u8
    },
}

fn main(){

    // We cant use Clap::parse or try_parse because we have SubcommandsNegateReqs enabled
    // this would cause a panic when Clap attempts to parse the req_str arg that isn't there
    // let opts = ExOpts::parse(); // panics
    // let opts = ExOpts::try_parse(); // panics 

    // Instead we need to check to see if a subcommand exists before we run from_arg_matches on ExOpts
    let matches = ExOpts::into_app().get_matches();
    if matches.subcommand_name().is_none() {
        // If no subcommand we can derive ExOpts
        let opts = ExOpts::from_arg_matches(&matches);
        println!("{:?}", opts);
    } else {
        // If subcommand we need derive the subcommands instead
        let cmd_opts = SubCommands::from_arg_matches(&matches);
        println!("{:?}", cmd_opts);
    }

}

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 16 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Oh wait. Haha, sorry. No it still panics. Sorry.

$ cargo run – ex-sub Finished dev [unoptimized + debuginfo] target(s) in 0.02s Running target/debug/clap-ex ex-sub thread ‘main’ panicked at ‘called Option::unwrap() on a None value’, src/main.rs:9:14 note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

#[allow(unused_imports)]
use clap::{AppSettings, Clap, IntoApp, FromArgMatches};

#[derive(Debug, Clap)]
#[clap(setting(AppSettings::SubcommandsNegateReqs))]
struct ExOpts {

    #[clap(required = true)]
    req_str: String,

    #[clap(subcommand)]
    pub cmd: Option<SubCommands>,
}

#[derive(Debug, Clap)]
enum SubCommands{
    ExSub {
        #[clap(short, long, parse(from_occurrences))]
        verbose: u8
    },
}

fn main(){

    // We cant use Clap::parse or try_parse because we have SubcommandsNegateReqs enabled
    // this would cause a panic when Clap attempts to parse the req_str arg that isn't there
    let opts = ExOpts::parse(); // panics
    println!("{:?}", opts);

}