From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/clap/src/builder/debug_asserts.rs | 851 +++++++++++++++++++++++++++++++ 1 file changed, 851 insertions(+) create mode 100644 vendor/clap/src/builder/debug_asserts.rs (limited to 'vendor/clap/src/builder/debug_asserts.rs') diff --git a/vendor/clap/src/builder/debug_asserts.rs b/vendor/clap/src/builder/debug_asserts.rs new file mode 100644 index 000000000..864b8b479 --- /dev/null +++ b/vendor/clap/src/builder/debug_asserts.rs @@ -0,0 +1,851 @@ +use std::cmp::Ordering; + +use clap_lex::RawOsStr; + +use crate::builder::arg::ArgProvider; +use crate::mkeymap::KeyType; +use crate::ArgAction; +use crate::{Arg, Command, ValueHint}; + +pub(crate) fn assert_app(cmd: &Command) { + debug!("Command::_debug_asserts"); + + let mut short_flags = vec![]; + let mut long_flags = vec![]; + + // Invalid version flag settings + if cmd.get_version().is_none() && cmd.get_long_version().is_none() { + // PropagateVersion is meaningless if there is no version + assert!( + !cmd.is_propagate_version_set(), + "Command {}: No version information via Command::version or Command::long_version to propagate", + cmd.get_name(), + ); + + // Used `Command::mut_arg("version", ..) but did not provide any version information to display + let version_needed = cmd + .get_arguments() + .filter(|x| { + let action_set = matches!(x.get_action(), ArgAction::Version); + #[cfg(not(feature = "unstable-v4"))] + let provider_set = matches!(x.provider, ArgProvider::GeneratedMutated); + #[cfg(feature = "unstable-v4")] + let provider_set = matches!( + x.provider, + ArgProvider::User | ArgProvider::GeneratedMutated + ); + action_set && provider_set + }) + .map(|x| x.get_id()) + .collect::>(); + + assert_eq!(version_needed, Vec::<&str>::new(), "Command {}: `ArgAction::Version` used without providing Command::version or Command::long_version" + ,cmd.get_name() + ); + } + + for sc in cmd.get_subcommands() { + if let Some(s) = sc.get_short_flag().as_ref() { + short_flags.push(Flag::Command(format!("-{}", s), sc.get_name())); + } + + for short_alias in sc.get_all_short_flag_aliases() { + short_flags.push(Flag::Command(format!("-{}", short_alias), sc.get_name())); + } + + if let Some(l) = sc.get_long_flag().as_ref() { + #[cfg(feature = "unstable-v4")] + { + assert!(!l.starts_with('-'), "Command {}: long_flag {:?} must not start with a `-`, that will be handled by the parser", sc.get_name(), l); + } + long_flags.push(Flag::Command(format!("--{}", l), sc.get_name())); + } + + for long_alias in sc.get_all_long_flag_aliases() { + long_flags.push(Flag::Command(format!("--{}", long_alias), sc.get_name())); + } + } + + for arg in cmd.get_arguments() { + assert_arg(arg); + + assert!( + !cmd.is_multicall_set(), + "Command {}: Arguments like {} cannot be set on a multicall command", + cmd.get_name(), + arg.name + ); + + if let Some(s) = arg.short.as_ref() { + short_flags.push(Flag::Arg(format!("-{}", s), &*arg.name)); + } + + for (short_alias, _) in &arg.short_aliases { + short_flags.push(Flag::Arg(format!("-{}", short_alias), arg.name)); + } + + if let Some(l) = arg.long.as_ref() { + #[cfg(feature = "unstable-v4")] + { + assert!(!l.starts_with('-'), "Argument {}: long {:?} must not start with a `-`, that will be handled by the parser", arg.name, l); + } + long_flags.push(Flag::Arg(format!("--{}", l), &*arg.name)); + } + + for (long_alias, _) in &arg.aliases { + long_flags.push(Flag::Arg(format!("--{}", long_alias), arg.name)); + } + + // Name conflicts + assert!( + cmd.two_args_of(|x| x.id == arg.id).is_none(), + "Command {}: Argument names must be unique, but '{}' is in use by more than one argument or group", + cmd.get_name(), + arg.name, + ); + + // Long conflicts + if let Some(l) = arg.long { + if let Some((first, second)) = cmd.two_args_of(|x| x.long == Some(l)) { + panic!( + "Command {}: Long option names must be unique for each argument, \ + but '--{}' is in use by both '{}' and '{}'", + cmd.get_name(), + l, + first.name, + second.name + ) + } + } + + // Short conflicts + if let Some(s) = arg.short { + if let Some((first, second)) = cmd.two_args_of(|x| x.short == Some(s)) { + panic!( + "Command {}: Short option names must be unique for each argument, \ + but '-{}' is in use by both '{}' and '{}'", + cmd.get_name(), + s, + first.name, + second.name + ) + } + } + + // Index conflicts + if let Some(idx) = arg.index { + if let Some((first, second)) = + cmd.two_args_of(|x| x.is_positional() && x.index == Some(idx)) + { + panic!( + "Command {}: Argument '{}' has the same index as '{}' \ + and they are both positional arguments\n\n\t \ + Use Arg::multiple_values(true) to allow one \ + positional argument to take multiple values", + cmd.get_name(), + first.name, + second.name + ) + } + } + + // requires, r_if, r_unless + for req in &arg.requires { + assert!( + cmd.id_exists(&req.1), + "Command {}: Argument or group '{:?}' specified in 'requires*' for '{}' does not exist", + cmd.get_name(), + req.1, + arg.name, + ); + } + + for req in &arg.r_ifs { + #[cfg(feature = "unstable-v4")] + { + assert!( + !arg.is_required_set(), + "Argument {}: `required` conflicts with `required_if_eq*`", + arg.name + ); + } + assert!( + cmd.id_exists(&req.0), + "Command {}: Argument or group '{:?}' specified in 'required_if_eq*' for '{}' does not exist", + cmd.get_name(), + req.0, + arg.name + ); + } + + for req in &arg.r_ifs_all { + #[cfg(feature = "unstable-v4")] + { + assert!( + !arg.is_required_set(), + "Argument {}: `required` conflicts with `required_if_eq_all`", + arg.name + ); + } + assert!( + cmd.id_exists(&req.0), + "Command {}: Argument or group '{:?}' specified in 'required_if_eq_all' for '{}' does not exist", + cmd.get_name(), + req.0, + arg.name + ); + } + + for req in &arg.r_unless { + #[cfg(feature = "unstable-v4")] + { + assert!( + !arg.is_required_set(), + "Argument {}: `required` conflicts with `required_unless*`", + arg.name + ); + } + assert!( + cmd.id_exists(req), + "Command {}: Argument or group '{:?}' specified in 'required_unless*' for '{}' does not exist", + cmd.get_name(), + req, + arg.name, + ); + } + + for req in &arg.r_unless_all { + #[cfg(feature = "unstable-v4")] + { + assert!( + !arg.is_required_set(), + "Argument {}: `required` conflicts with `required_unless*`", + arg.name + ); + } + assert!( + cmd.id_exists(req), + "Command {}: Argument or group '{:?}' specified in 'required_unless*' for '{}' does not exist", + cmd.get_name(), + req, + arg.name, + ); + } + + // blacklist + for req in &arg.blacklist { + assert!( + cmd.id_exists(req), + "Command {}: Argument or group '{:?}' specified in 'conflicts_with*' for '{}' does not exist", + cmd.get_name(), + req, + arg.name, + ); + } + + if arg.is_last_set() { + assert!( + arg.long.is_none(), + "Command {}: Flags or Options cannot have last(true) set. '{}' has both a long and last(true) set.", + cmd.get_name(), + arg.name + ); + assert!( + arg.short.is_none(), + "Command {}: Flags or Options cannot have last(true) set. '{}' has both a short and last(true) set.", + cmd.get_name(), + arg.name + ); + } + + assert!( + !(arg.is_required_set() && arg.is_global_set()), + "Command {}: Global arguments cannot be required.\n\n\t'{}' is marked as both global and required", + cmd.get_name(), + arg.name + ); + + // validators + assert!( + arg.validator.is_none() || arg.validator_os.is_none(), + "Command {}: Argument '{}' has both `validator` and `validator_os` set which is not allowed", + cmd.get_name(), + arg.name + ); + + if arg.get_value_hint() == ValueHint::CommandWithArguments { + assert!( + arg.is_positional(), + "Command {}: Argument '{}' has hint CommandWithArguments and must be positional.", + cmd.get_name(), + arg.name + ); + + assert!( + cmd.is_trailing_var_arg_set(), + "Command {}: Positional argument '{}' has hint CommandWithArguments, so Command must have TrailingVarArg set.", + cmd.get_name(), + arg.name + ); + } + } + + for group in cmd.get_groups() { + // Name conflicts + assert!( + cmd.get_groups().filter(|x| x.id == group.id).count() < 2, + "Command {}: Argument group name must be unique\n\n\t'{}' is already in use", + cmd.get_name(), + group.name, + ); + + // Groups should not have naming conflicts with Args + assert!( + !cmd.get_arguments().any(|x| x.id == group.id), + "Command {}: Argument group name '{}' must not conflict with argument name", + cmd.get_name(), + group.name, + ); + + for arg in &group.args { + // Args listed inside groups should exist + assert!( + cmd.get_arguments().any(|x| x.id == *arg), + "Command {}: Argument group '{}' contains non-existent argument '{:?}'", + cmd.get_name(), + group.name, + arg + ); + } + } + + // Conflicts between flags and subcommands + + long_flags.sort_unstable(); + short_flags.sort_unstable(); + + detect_duplicate_flags(&long_flags, "long"); + detect_duplicate_flags(&short_flags, "short"); + + _verify_positionals(cmd); + + if let Some(help_template) = cmd.get_help_template() { + assert!( + !help_template.contains("{flags}"), + "Command {}: {}", + cmd.get_name(), + "`{flags}` template variable was removed in clap3, they are now included in `{options}`", + ); + assert!( + !help_template.contains("{unified}"), + "Command {}: {}", + cmd.get_name(), + "`{unified}` template variable was removed in clap3, use `{options}` instead" + ); + } + + cmd._panic_on_missing_help(cmd.is_help_expected_set()); + assert_app_flags(cmd); +} + +#[derive(Eq)] +enum Flag<'a> { + Command(String, &'a str), + Arg(String, &'a str), +} + +impl PartialEq for Flag<'_> { + fn eq(&self, other: &Flag) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl PartialOrd for Flag<'_> { + fn partial_cmp(&self, other: &Flag) -> Option { + use Flag::*; + + match (self, other) { + (Command(s1, _), Command(s2, _)) + | (Arg(s1, _), Arg(s2, _)) + | (Command(s1, _), Arg(s2, _)) + | (Arg(s1, _), Command(s2, _)) => { + if s1 == s2 { + Some(Ordering::Equal) + } else { + s1.partial_cmp(s2) + } + } + } + } +} + +impl Ord for Flag<'_> { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).unwrap() + } +} + +fn detect_duplicate_flags(flags: &[Flag], short_or_long: &str) { + use Flag::*; + + for (one, two) in find_duplicates(flags) { + match (one, two) { + (Command(flag, one), Command(_, another)) if one != another => panic!( + "the '{}' {} flag is specified for both '{}' and '{}' subcommands", + flag, short_or_long, one, another + ), + + (Arg(flag, one), Arg(_, another)) if one != another => panic!( + "{} option names must be unique, but '{}' is in use by both '{}' and '{}'", + short_or_long, flag, one, another + ), + + (Arg(flag, arg), Command(_, sub)) | (Command(flag, sub), Arg(_, arg)) => panic!( + "the '{}' {} flag for the '{}' argument conflicts with the short flag \ + for '{}' subcommand", + flag, short_or_long, arg, sub + ), + + _ => {} + } + } +} + +/// Find duplicates in a sorted array. +/// +/// The algorithm is simple: the array is sorted, duplicates +/// must be placed next to each other, we can check only adjacent elements. +fn find_duplicates(slice: &[T]) -> impl Iterator { + slice.windows(2).filter_map(|w| { + if w[0] == w[1] { + Some((&w[0], &w[1])) + } else { + None + } + }) +} + +fn assert_app_flags(cmd: &Command) { + macro_rules! checker { + ($a:ident requires $($b:ident)|+) => { + if cmd.$a() { + let mut s = String::new(); + + $( + if !cmd.$b() { + s.push_str(&format!(" AppSettings::{} is required when AppSettings::{} is set.\n", std::stringify!($b), std::stringify!($a))); + } + )+ + + if !s.is_empty() { + panic!("{}", s) + } + } + }; + ($a:ident conflicts $($b:ident)|+) => { + if cmd.$a() { + let mut s = String::new(); + + $( + if cmd.$b() { + s.push_str(&format!(" AppSettings::{} conflicts with AppSettings::{}.\n", std::stringify!($b), std::stringify!($a))); + } + )+ + + if !s.is_empty() { + panic!("{}\n{}", cmd.get_name(), s) + } + } + }; + } + + checker!(is_allow_invalid_utf8_for_external_subcommands_set requires is_allow_external_subcommands_set); + checker!(is_multicall_set conflicts is_no_binary_name_set); +} + +#[cfg(debug_assertions)] +fn _verify_positionals(cmd: &Command) -> bool { + debug!("Command::_verify_positionals"); + // Because you must wait until all arguments have been supplied, this is the first chance + // to make assertions on positional argument indexes + // + // First we verify that the index highest supplied index, is equal to the number of + // positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3 + // but no 2) + + let highest_idx = cmd + .get_keymap() + .keys() + .filter_map(|x| { + if let KeyType::Position(n) = x { + Some(*n) + } else { + None + } + }) + .max() + .unwrap_or(0); + + let num_p = cmd.get_keymap().keys().filter(|x| x.is_position()).count(); + + assert!( + highest_idx == num_p, + "Found positional argument whose index is {} but there \ + are only {} positional arguments defined", + highest_idx, + num_p + ); + + // Next we verify that only the highest index has takes multiple arguments (if any) + let only_highest = |a: &Arg| a.is_multiple() && (a.index.unwrap_or(0) != highest_idx); + if cmd.get_positionals().any(only_highest) { + // First we make sure if there is a positional that allows multiple values + // the one before it (second to last) has one of these: + // * a value terminator + // * ArgSettings::Last + // * The last arg is Required + + // We can't pass the closure (it.next()) to the macro directly because each call to + // find() (iterator, not macro) gets called repeatedly. + let last = &cmd.get_keymap()[&KeyType::Position(highest_idx)]; + let second_to_last = &cmd.get_keymap()[&KeyType::Position(highest_idx - 1)]; + + // Either the final positional is required + // Or the second to last has a terminator or .last(true) set + let ok = last.is_required_set() + || (second_to_last.terminator.is_some() || second_to_last.is_last_set()) + || last.is_last_set(); + assert!( + ok, + "When using a positional argument with .multiple_values(true) that is *not the \ + last* positional argument, the last positional argument (i.e. the one \ + with the highest index) *must* have .required(true) or .last(true) set." + ); + + // We make sure if the second to last is Multiple the last is ArgSettings::Last + let ok = second_to_last.is_multiple() || last.is_last_set(); + assert!( + ok, + "Only the last positional argument, or second to last positional \ + argument may be set to .multiple_values(true)" + ); + + // Next we check how many have both Multiple and not a specific number of values set + let count = cmd + .get_positionals() + .filter(|p| { + #[allow(deprecated)] + { + p.is_multiple_occurrences_set() + || (p.is_multiple_values_set() && p.num_vals.is_none()) + } + }) + .count(); + let ok = count <= 1 + || (last.is_last_set() + && last.is_multiple() + && second_to_last.is_multiple() + && count == 2); + assert!( + ok, + "Only one positional argument with .multiple_values(true) set is allowed per \ + command, unless the second one also has .last(true) set" + ); + } + + let mut found = false; + + if cmd.is_allow_missing_positional_set() { + // Check that if a required positional argument is found, all positions with a lower + // index are also required. + let mut foundx2 = false; + + for p in cmd.get_positionals() { + if foundx2 && !p.is_required_set() { + assert!( + p.is_required_set(), + "Found non-required positional argument with a lower \ + index than a required positional argument by two or more: {:?} \ + index {:?}", + p.name, + p.index + ); + } else if p.is_required_set() && !p.is_last_set() { + // Args that .last(true) don't count since they can be required and have + // positionals with a lower index that aren't required + // Imagine: prog [opt1] -- + // Both of these are valid invocations: + // $ prog r1 -- r2 + // $ prog r1 o1 -- r2 + if found { + foundx2 = true; + continue; + } + found = true; + continue; + } else { + found = false; + } + } + } else { + // Check that if a required positional argument is found, all positions with a lower + // index are also required + for p in (1..=num_p).rev().filter_map(|n| cmd.get_keymap().get(&n)) { + if found { + assert!( + p.is_required_set(), + "Found non-required positional argument with a lower \ + index than a required positional argument: {:?} index {:?}", + p.name, + p.index + ); + } else if p.is_required_set() && !p.is_last_set() { + // Args that .last(true) don't count since they can be required and have + // positionals with a lower index that aren't required + // Imagine: prog [opt1] -- + // Both of these are valid invocations: + // $ prog r1 -- r2 + // $ prog r1 o1 -- r2 + found = true; + continue; + } + } + } + assert!( + cmd.get_positionals().filter(|p| p.is_last_set()).count() < 2, + "Only one positional argument may have last(true) set. Found two." + ); + if cmd + .get_positionals() + .any(|p| p.is_last_set() && p.is_required_set()) + && cmd.has_subcommands() + && !cmd.is_subcommand_negates_reqs_set() + { + panic!( + "Having a required positional argument with .last(true) set *and* child \ + subcommands without setting SubcommandsNegateReqs isn't compatible." + ); + } + + true +} + +fn assert_arg(arg: &Arg) { + debug!("Arg::_debug_asserts:{}", arg.name); + + // Self conflict + // TODO: this check should be recursive + assert!( + !arg.blacklist.iter().any(|x| *x == arg.id), + "Argument '{}' cannot conflict with itself", + arg.name, + ); + + assert_eq!( + arg.get_action().takes_values(), + arg.is_takes_value_set(), + "Argument `{}`'s selected action {:?} contradicts `takes_value`", + arg.name, + arg.get_action() + ); + if let Some(action_type_id) = arg.get_action().value_type_id() { + assert_eq!( + action_type_id, + arg.get_value_parser().type_id(), + "Argument `{}`'s selected action {:?} contradicts `value_parser` ({:?})", + arg.name, + arg.get_action(), + arg.get_value_parser() + ); + } + + if arg.get_value_hint() != ValueHint::Unknown { + assert!( + arg.is_takes_value_set(), + "Argument '{}' has value hint but takes no value", + arg.name + ); + + if arg.get_value_hint() == ValueHint::CommandWithArguments { + assert!( + arg.is_multiple_values_set(), + "Argument '{}' uses hint CommandWithArguments and must accept multiple values", + arg.name + ) + } + } + + if arg.index.is_some() { + assert!( + arg.is_positional(), + "Argument '{}' is a positional argument and can't have short or long name versions", + arg.name + ); + assert!( + arg.is_takes_value_set(), + "Argument '{}` is positional, it must take a value", + arg.name + ); + } + + #[cfg(feature = "unstable-v4")] + { + let num_vals = arg.get_num_vals().unwrap_or(usize::MAX); + let num_val_names = arg.get_value_names().unwrap_or(&[]).len(); + if num_vals < num_val_names { + panic!( + "Argument {}: Too many value names ({}) compared to number_of_values ({})", + arg.name, num_val_names, num_vals + ); + } + } + + assert_arg_flags(arg); + + assert_defaults(arg, "default_value", arg.default_vals.iter().copied()); + assert_defaults( + arg, + "default_missing_value", + arg.default_missing_vals.iter().copied(), + ); + assert_defaults( + arg, + "default_value_if", + arg.default_vals_ifs + .iter() + .filter_map(|(_, _, default)| *default), + ); +} + +fn assert_arg_flags(arg: &Arg) { + macro_rules! checker { + ($a:ident requires $($b:ident)|+) => { + if arg.$a() { + let mut s = String::new(); + + $( + if !arg.$b() { + s.push_str(&format!(" Arg::{} is required when Arg::{} is set.\n", std::stringify!($b), std::stringify!($a))); + } + )+ + + if !s.is_empty() { + panic!("Argument {:?}\n{}", arg.get_id(), s) + } + } + } + } + + checker!(is_require_value_delimiter_set requires is_takes_value_set); + checker!(is_require_value_delimiter_set requires is_use_value_delimiter_set); + checker!(is_hide_possible_values_set requires is_takes_value_set); + checker!(is_allow_hyphen_values_set requires is_takes_value_set); + checker!(is_require_equals_set requires is_takes_value_set); + checker!(is_last_set requires is_takes_value_set); + checker!(is_hide_default_value_set requires is_takes_value_set); + checker!(is_multiple_values_set requires is_takes_value_set); + checker!(is_ignore_case_set requires is_takes_value_set); + { + #![allow(deprecated)] + checker!(is_forbid_empty_values_set requires is_takes_value_set); + checker!(is_allow_invalid_utf8_set requires is_takes_value_set); + } +} + +fn assert_defaults<'d>( + arg: &Arg, + field: &'static str, + defaults: impl IntoIterator, +) { + for default_os in defaults { + if let Some(default_s) = default_os.to_str() { + if !arg.possible_vals.is_empty() { + if let Some(delim) = arg.get_value_delimiter() { + for part in default_s.split(delim) { + assert!( + arg.possible_vals.iter().any(|possible_val| { + possible_val.matches(part, arg.is_ignore_case_set()) + }), + "Argument `{}`'s {}={} doesn't match possible values", + arg.name, + field, + part + ) + } + } else { + assert!( + arg.possible_vals.iter().any(|possible_val| { + possible_val.matches(default_s, arg.is_ignore_case_set()) + }), + "Argument `{}`'s {}={} doesn't match possible values", + arg.name, + field, + default_s + ); + } + } + + if let Some(validator) = arg.validator.as_ref() { + let mut validator = validator.lock().unwrap(); + if let Some(delim) = arg.get_value_delimiter() { + for part in default_s.split(delim) { + if let Err(err) = validator(part) { + panic!( + "Argument `{}`'s {}={} failed validation: {}", + arg.name, field, part, err + ); + } + } + } else if let Err(err) = validator(default_s) { + panic!( + "Argument `{}`'s {}={} failed validation: {}", + arg.name, field, default_s, err + ); + } + } + } + + if let Some(validator) = arg.validator_os.as_ref() { + let mut validator = validator.lock().unwrap(); + if let Some(delim) = arg.get_value_delimiter() { + let default_os = RawOsStr::new(default_os); + for part in default_os.split(delim) { + if let Err(err) = validator(&part.to_os_str()) { + panic!( + "Argument `{}`'s {}={:?} failed validation: {}", + arg.name, field, part, err + ); + } + } + } else if let Err(err) = validator(default_os) { + panic!( + "Argument `{}`'s {}={:?} failed validation: {}", + arg.name, field, default_os, err + ); + } + } + + let value_parser = arg.get_value_parser(); + let assert_cmd = Command::new("assert"); + if let Some(delim) = arg.get_value_delimiter() { + let default_os = RawOsStr::new(default_os); + for part in default_os.split(delim) { + if let Err(err) = value_parser.parse_ref(&assert_cmd, Some(arg), &part.to_os_str()) + { + panic!( + "Argument `{}`'s {}={:?} failed validation: {}", + arg.name, + field, + part.to_str_lossy(), + err + ); + } + } + } else if let Err(err) = value_parser.parse_ref(&assert_cmd, Some(arg), default_os) { + panic!( + "Argument `{}`'s {}={:?} failed validation: {}", + arg.name, field, default_os, err + ); + } + } +} -- cgit v1.2.3