Repository: dtolnay/cxx (language: rust)

Issue:
cxxbridge-cmd: Pin to clap 3.1, clap_lex 0.2.0
clap 3.2+ has some incompatible changes; see https://github.com/dtolnay/cxx/pull/1057
These might arguably be bugs - still to be determined.
And `clap_lex` now requires a newer Rust than our MSRV at the moment.



Discussion / hints:

Fix patch:
diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml
index 83dadcf2a..c3e42f977 100644
--- a/gen/cmd/Cargo.toml
+++ b/gen/cmd/Cargo.toml
@@ -21,7 +21,7 @@ path = "src/main.rs"
 experimental-async-fn = []
 
 [dependencies]
-clap = { version = "3.1", default-features = false, features = ["std", "suggestions"] }
+clap = { version = "3.2", default-features = false, features = ["std", "suggestions"] }
 codespan-reporting = "0.11"
 proc-macro2 = { version = "1.0.39", default-features = false, features = ["span-locations"] }
 quote = { version = "1.0", default-features = false }
diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs
index 97288c105..a1adb387d 100644
--- a/gen/cmd/src/app.rs
+++ b/gen/cmd/src/app.rs
@@ -6,10 +6,11 @@ use super::{Opt, Output};
 use crate::cfg::{self, CfgValue};
 use crate::gen::include::Include;
 use crate::syntax::IncludeKind;
+use clap::builder::{ArgAction, ValueParser};
 use clap::{Arg, Command};
 use std::collections::{BTreeMap as Map, BTreeSet as Set};
-use std::ffi::OsStr;
 use std::path::PathBuf;
+use std::sync::{Arc, Mutex, PoisonError};
 use syn::parse::Parser;
 
 const USAGE: &str = "\
@@ -63,11 +64,13 @@ const CFG: &str = "cfg";
 pub(super) fn from_args() -> Opt {
     let matches = app().get_matches();
 
-    let input = matches.value_of_os(INPUT).map(PathBuf::from);
-    let cxx_impl_annotations = matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned);
-    let header = matches.is_present(HEADER);
+    let input = matches.get_one::<PathBuf>(INPUT).cloned();
+    let cxx_impl_annotations = matches
+        .get_one::<String>(CXX_IMPL_ANNOTATIONS)
+        .map(String::clone);
+    let header = matches.contains_id(HEADER);
     let include = matches
-        .values_of(INCLUDE)
+        .get_many::<String>(INCLUDE)
         .unwrap_or_default()
         .map(|include| {
             if include.starts_with('<') && include.ends_with('>') {
@@ -85,11 +88,11 @@ pub(super) fn from_args() -> Opt {
         .collect();
 
     let mut outputs = Vec::new();
-    for path in matches.values_of_os(OUTPUT).unwrap_or_default() {
-        outputs.push(if path == "-" {
+    for path in matches.get_many::<PathBuf>(OUTPUT).unwrap_or_default() {
+        outputs.push(if path.as_os_str() == "-" {
             Output::Stdout
         } else {
-            Output::File(PathBuf::from(path))
+            Output::File(path.clone())
         });
     }
     if outputs.is_empty() {
@@ -97,7 +100,7 @@ pub(super) fn from_args() -> Opt {
     }
 
     let mut cfg = Map::new();
-    for arg in matches.values_of(CFG).unwrap_or_default() {
+    for arg in matches.get_many::<String>(CFG).unwrap_or_default() {
         let (name, value) = cfg::parse.parse_str(arg).unwrap();
         cfg.entry(name).or_insert_with(Set::new).insert(value);
     }
@@ -112,19 +115,11 @@ pub(super) fn from_args() -> Opt {
     }
 }
 
-fn validate_utf8(arg: &OsStr) -> Result<(), &'static str> {
-    if arg.to_str().is_some() {
-        Ok(())
-    } else {
-        Err("invalid utf-8 sequence")
-    }
-}
-
 fn arg_input() -> Arg<'static> {
     Arg::new(INPUT)
         .help("Input Rust source file containing #[cxx::bridge].")
         .required_unless_present(HEADER)
-        .allow_invalid_utf8(true)
+        .value_parser(ValueParser::path_buf())
 }
 
 fn arg_cxx_impl_annotations() -> Arg<'static> {
@@ -138,8 +133,7 @@ these C++ functions in another.";
         .long(CXX_IMPL_ANNOTATIONS)
         .takes_value(true)
         .value_name("annotation")
-        .allow_invalid_utf8(true)
-        .validator_os(validate_utf8)
+        .value_parser(ValueParser::string())
         .help(HELP)
 }
 
@@ -159,9 +153,8 @@ into the generated C++ code as #include lines.";
         .long(INCLUDE)
         .short('i')
         .takes_value(true)
-        .multiple_occurrences(true)
-        .allow_invalid_utf8(true)
-        .validator_os(validate_utf8)
+        .action(ArgAction::Append)
+        .value_parser(ValueParser::string())
         .help(HELP)
 }
 
@@ -173,9 +166,8 @@ not specified.";
         .long(OUTPUT)
         .short('o')
         .takes_value(true)
-        .multiple_occurrences(true)
-        .allow_invalid_utf8(true)
-        .validator_os(validate_utf8)
+        .action(ArgAction::Append)
+        .value_parser(ValueParser::path_buf())
         .help(HELP)
 }
 
@@ -183,22 +175,23 @@ fn arg_cfg() -> Arg<'static> {
     const HELP: &str = "\
 Compilation configuration matching what will be used to build
 the Rust side of the bridge.";
-    let mut bool_cfgs = Map::<String, bool>::new();
+    let bool_cfgs = Arc::new(Mutex::new(Map::<String, bool>::new()));
     Arg::new(CFG)
         .long(CFG)
         .takes_value(true)
         .value_name("name=\"value\" | name[=true] | name=false")
-        .multiple_occurrences(true)
-        .validator(move |arg| match cfg::parse.parse_str(arg) {
-            Ok((_, CfgValue::Str(_))) => Ok(()),
+        .action(ArgAction::Append)
+        .value_parser(move |arg: &str| match cfg::parse.parse_str(arg) {
+            Ok((_, CfgValue::Str(_))) => Ok(arg.to_owned()),
             Ok((name, CfgValue::Bool(value))) => {
+                let mut bool_cfgs = bool_cfgs.lock().unwrap_or_else(PoisonError::into_inner);
                 if let Some(&prev) = bool_cfgs.get(&name) {
                     if prev != value {
                         return Err(format!("cannot have both {0}=false and {0}=true", name));
                     }
                 }
                 bool_cfgs.insert(name, value);
-                Ok(())
+                Ok(arg.to_owned())
             }
             Err(_) => Err("expected name=\"value\", name=true, or name=false".to_owned()),
         })
diff --git a/third-party/BUCK b/third-party/BUCK
index 9c35418f6..d18835251 100644
--- a/third-party/BUCK
+++ b/third-party/BUCK
@@ -17,8 +17,8 @@ rust_library(
 
 rust_library(
     name = "clap",
-    srcs = glob(["vendor/clap-3.1.18/src/**"]),
-    edition = "2018",
+    srcs = glob(["vendor/clap-3.2.1/src/**"]),
+    edition = "2021",
     features = ["std"],
     visibility = ["PUBLIC"],
     deps = [
@@ -31,8 +31,8 @@ rust_library(
 
 rust_library(
     name = "clap_lex",
-    srcs = glob(["vendor/clap_lex-0.2.0/src/**"]),
-    edition = "2018",
+    srcs = glob(["vendor/clap_lex-0.2.2/src/**"]),
+    edition = "2021",
     deps = [
         ":os_str_bytes",
     ],
diff --git a/third-party/BUILD b/third-party/BUILD
index 0bb295db7..570e0d876 100644
--- a/third-party/BUILD
+++ b/third-party/BUILD
@@ -20,9 +20,9 @@ rust_library(
 
 rust_library(
     name = "clap",
-    srcs = glob(["vendor/clap-3.1.18/src/**"]),
+    srcs = glob(["vendor/clap-3.2.1/src/**"]),
     crate_features = ["std"],
-    edition = "2018",
+    edition = "2021",
     visibility = ["//visibility:public"],
     deps = [
         ":bitflags",
@@ -34,8 +34,8 @@ rust_library(
 
 rust_library(
     name = "clap_lex",
-    srcs = glob(["vendor/clap_lex-0.2.0/src/**"]),
-    edition = "2018",
+    srcs = glob(["vendor/clap_lex-0.2.2/src/**"]),
+    edition = "2021",
     deps = [
         ":os_str_bytes",
     ],
diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock
index 3a66246bd..1cf498041 100644
--- a/third-party/Cargo.lock
+++ b/third-party/Cargo.lock
@@ -46,9 +46,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "3.1.18"
+version = "3.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
+checksum = "a836566fa5f52f7ddf909a8a2f9029b9f78ca584cd95cf7e87f8073110f4c5c9"
 dependencies = [
  "bitflags",
  "clap_lex",
@@ -59,9 +59,9 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.2.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
+checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
 dependencies = [
  "os_str_bytes",
 ]


Test patch: