Repository: mozilla/cbindgen (language: rust)

Issue:
Upgrade to clap 4 (getting rid of `atty` as a transitive dependency)
`cbindgen` is still depending on `clap 3` which in turn depends no `atty`. An unmaintained crate with a [known unsoundness issue](https://rustsec.org/advisories/RUSTSEC-2021-0145.html). We are really trying to get rid of `atty` from our dependency tree, but we also need `cbindgen`. So this issue is about considering an upgrade to `clap 4`.


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 8f71f5e2d..c1c20cf99 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,14 +3,52 @@
 version = 3
 
 [[package]]
-name = "atty"
-version = "0.2.14"
+name = "anstream"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
 dependencies = [
- "hermit-abi",
- "libc",
- "winapi",
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+dependencies = [
+ "anstyle",
+ "windows-sys",
 ]
 
 [[package]]
@@ -43,6 +81,12 @@ dependencies = [
  "toml",
 ]
 
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
@@ -51,26 +95,57 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "clap"
-version = "3.2.25"
+version = "4.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80672091db20273a15cf9fdd4e47ed43b5091ec9841bf4c6145c9dfbbcae09ed"
+dependencies = [
+ "clap_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
+checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636"
 dependencies = [
- "atty",
+ "anstream",
+ "anstyle",
  "bitflags",
  "clap_lex",
- "indexmap",
  "strsim",
- "termcolor",
- "textwrap",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.2.4"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
 dependencies = [
- "os_str_bytes",
+ "cc",
+ "libc",
 ]
 
 [[package]]
@@ -96,12 +171,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
 
 [[package]]
 name = "indexmap"
@@ -122,6 +194,29 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
+dependencies = [
+ "hermit-abi",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.6"
@@ -140,6 +235,12 @@ version = "0.2.144"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
 
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
 [[package]]
 name = "lock_api"
 version = "0.4.9"
@@ -159,12 +260,6 @@ dependencies = [
  "cfg-if",
 ]
 
-[[package]]
-name = "os_str_bytes"
-version = "6.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
-
 [[package]]
 name = "parking_lot"
 version = "0.11.2"
@@ -226,6 +321,20 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "rustix"
+version = "0.37.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
 [[package]]
 name = "ryu"
 version = "1.0.13"
@@ -339,21 +448,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "termcolor"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
-
 [[package]]
 name = "toml"
 version = "0.5.11"
@@ -369,6 +463,12 @@ version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
 
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -386,16 +486,73 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
-name = "winapi-util"
-version = "0.1.5"
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "winapi",
+ "windows-targets",
 ]
 
 [[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "windows-targets"
+version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/Cargo.toml b/Cargo.toml
index 7b44bc399..cf3181802 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,13 +13,13 @@ keywords = ["bindings", "ffi", "code-generation"]
 categories = ["external-ffi-bindings", "development-tools::ffi"]
 repository = "https://github.com/eqrion/cbindgen"
 edition = "2018"
-rust-version = "1.57"
+rust-version = "1.64"
 exclude = [
   "tests/profile.rs", # Test relies in a sub-crate, see https://github.com/rust-lang/cargo/issues/9017
 ]
 
 [dependencies]
-clap = { version = "3.1", optional = true }
+clap = { version = "4", optional = true }
 indexmap = "1"
 log = "0.4"
 serde = { version = "1.0.103", default-features = false, features = ["derive"] }
diff --git a/src/main.rs b/src/main.rs
index eb78a080b..8a607fb84 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,7 +5,6 @@
 use std::env;
 use std::io;
 use std::path::{Path, PathBuf};
-use std::str::FromStr;
 
 extern crate clap;
 #[macro_use]
@@ -20,60 +19,60 @@ extern crate quote;
 extern crate syn;
 extern crate toml;
 
+use clap::builder::PossibleValuesParser;
+use clap::ArgAction;
 use clap::{Arg, ArgMatches, Command};
 
 mod bindgen;
 mod logging;
 
-use crate::bindgen::{Bindings, Builder, Cargo, Config, Error, Profile, Style};
+use crate::bindgen::{Bindings, Builder, Cargo, Config, Error};
 
 fn apply_config_overrides(config: &mut Config, matches: &ArgMatches) {
     // We allow specifying a language to override the config default. This is
     // used by compile-tests.
-    if let Some(lang) = matches.value_of("lang") {
-        config.language = match lang.parse() {
-            Ok(lang) => lang,
-            Err(reason) => {
-                error!("{}", reason);
-                return;
-            }
+    match matches.try_get_one("lang") {
+        Ok(Some(lang)) => {
+            config.language = *lang;
+        }
+        Err(reason) => {
+            error!("{}", reason);
+            return;
         }
+        _ => (),
     }
 
-    if matches.is_present("cpp-compat") {
+    if matches.contains_id("cpp-compat") {
         config.cpp_compat = true;
     }
 
-    if matches.is_present("only-target-dependencies") {
+    if matches.contains_id("only-target-dependencies") {
         config.only_target_dependencies = true;
     }
 
-    if let Some(style) = matches.value_of("style") {
-        config.style = match style {
-            "Both" => Style::Both,
-            "both" => Style::Both,
-            "Tag" => Style::Tag,
-            "tag" => Style::Tag,
-            "Type" => Style::Type,
-            "type" => Style::Type,
-            _ => {
-                error!("Unknown style specified.");
-                return;
-            }
+    match matches.try_get_one("style") {
+        Ok(Some(style)) => {
+            config.style = *style;
+        }
+        Err(_) => {
+            error!("Unknown style specified.");
+            return;
         }
+        _ => (),
     }
 
-    if let Some(profile) = matches.value_of("profile") {
-        config.parse.expand.profile = match Profile::from_str(profile) {
-            Ok(p) => p,
-            Err(e) => {
-                error!("{}", e);
-                return;
-            }
+    match matches.try_get_one("profile") {
+        Ok(Some(profile)) => {
+            config.parse.expand.profile = *profile;
+        }
+        Err(e) => {
+            error!("{}", e);
+            return;
         }
+        _ => (),
     }
 
-    if matches.is_present("d") {
+    if matches.contains_id("d") {
         config.parse.parse_deps = true;
     }
 }
@@ -82,7 +81,7 @@ fn load_bindings(input: &Path, matches: &ArgMatches) -> Result<Bindings, Error>
     // If a file is specified then we load it as a single source
     if !input.is_dir() {
         // Load any config specified or search in the input directory
-        let mut config = match matches.value_of("config") {
+        let mut config = match matches.get_one::<PathBuf>("config") {
             Some(c) => Config::from_file(c).unwrap(),
             None => Config::from_root_or_default(
                 input
@@ -102,16 +101,16 @@ fn load_bindings(input: &Path, matches: &ArgMatches) -> Result<Bindings, Error>
     // We have to load a whole crate, so we use cargo to gather metadata
     let lib = Cargo::load(
         input,
-        matches.value_of("lockfile"),
-        matches.value_of("crate"),
+        matches.get_one::<String>("lockfile").map(|s| s.as_str()),
+        matches.get_one::<String>("crate").map(|s| s.as_str()),
         true,
-        matches.is_present("clean"),
-        matches.is_present("only-target-dependencies"),
-        matches.value_of("metadata").map(Path::new),
+        matches.contains_id("clean"),
+        matches.contains_id("only-target-dependencies"),
+        matches.get_one::<PathBuf>("metadata").map(|p| p.as_path()),
     )?;
 
     // Load any config specified or search in the binding crate directory
-    let mut config = match matches.value_of("config") {
+    let mut config = match matches.get_one::<PathBuf>("config") {
         Some(c) => Config::from_file(c).unwrap(),
         None => {
             let binding_crate_dir = lib.find_crate_dir(&lib.binding_crate_ref());
@@ -140,7 +139,7 @@ fn main() {
         .arg(
             Arg::new("v")
                 .short('v')
-                .multiple_occurrences(true)
+                .action(ArgAction::Count)
                 .help("Enable verbose logging"),
         )
         .arg(
@@ -161,7 +160,7 @@ fn main() {
                 .long("lang")
                 .value_name("LANGUAGE")
                 .help("Specify the language to output bindings in")
-                .possible_values(["c++", "C++", "c", "C", "cython", "Cython"]),
+                .value_parser(PossibleValuesParser::new(["c++", "C++", "c", "C", "cython", "Cython"])),
         )
         .arg(
             Arg::new("cpp-compat")
@@ -180,7 +179,7 @@ fn main() {
                 .long("style")
                 .value_name("STYLE")
                 .help("Specify the declaration style to use for bindings")
-                .possible_values(["Both", "both", "Tag", "tag", "Type", "type"]),
+                .value_parser(PossibleValuesParser::new(["Both", "both", "Tag", "tag", "Type", "type"])),
         )
         .arg(
             Arg::new("d")
@@ -257,7 +256,7 @@ fn main() {
                     "Specify the profile to use when expanding macros. \
                      Has no effect otherwise."
                 )
-                .possible_values(["Debug", "debug", "Release", "release"]),
+                .value_parser(PossibleValuesParser::new(["Debug", "debug", "Release", "release"])),
         )
         .arg(
             Arg::new("quiet")
@@ -270,9 +269,7 @@ fn main() {
             Arg::new("depfile")
                 .value_name("PATH")
                 .long("depfile")
-                .takes_value(true)
-                .min_values(1)
-                .max_values(1)
+                .num_args(1)
                 .required(false)
                 .help("Generate a depfile at the given Path listing the source files \
                     cbindgen traversed when generating the bindings. Useful when \
@@ -282,7 +279,7 @@ fn main() {
         )
         .get_matches();
 
-    if !matches.is_present("out") && matches.is_present("verify") {
+    if !matches.contains_id("out") && matches.contains_id("verify") {
         error!(
             "Cannot verify bindings against `stdout`, please specify a file to compare against."
         );
@@ -290,10 +287,10 @@ fn main() {
     }
 
     // Initialize logging
-    if matches.is_present("quiet") {
+    if matches.contains_id("quiet") {
         logging::ErrorLogger::init().unwrap();
     } else {
-        match matches.occurrences_of("v") {
+        match matches.get_count("v") {
             0 => logging::WarnLogger::init().unwrap(),
             1 => logging::InfoLogger::init().unwrap(),
             _ => logging::TraceLogger::init().unwrap(),
@@ -301,10 +298,10 @@ fn main() {
     }
 
     // Find the input directory
-    let input = match matches.value_of("INPUT") {
-        Some(input) => PathBuf::from(input),
-        None => env::current_dir().unwrap(),
-    };
+    let input: PathBuf = matches
+        .get_one("INPUT")
+        .cloned()
+        .unwrap_or_else(|| env::current_dir().unwrap());
 
     let bindings = match load_bindings(&input, &matches) {
         Ok(bindings) => bindings,
@@ -316,15 +313,15 @@ fn main() {
     };
 
     // Write the bindings file
-    match matches.value_of("out") {
+    match matches.get_one::<String>("out") {
         Some(file) => {
             let changed = bindings.write_to_file(file);
 
-            if matches.is_present("verify") && changed {
+            if matches.contains_id("verify") && changed {
                 error!("Bindings changed: {}", file);
                 std::process::exit(2);
             }
-            if let Some(depfile) = matches.value_of("depfile") {
+            if let Some(depfile) = matches.get_one("depfile") {
                 bindings.generate_depfile(file, depfile)
             }
         }


Test patch: