diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:12:43 +0000 |
commit | cf94bdc0742c13e2a0cac864c478b8626b266e1b (patch) | |
tree | 044670aa50cc5e2b4229aa0b6b3df6676730c0a6 /vendor/xflags-macros/src | |
parent | Adding debian version 1.65.0+dfsg1-2. (diff) | |
download | rustc-cf94bdc0742c13e2a0cac864c478b8626b266e1b.tar.xz rustc-cf94bdc0742c13e2a0cac864c478b8626b266e1b.zip |
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | vendor/xflags-macros/src/ast.rs | 13 | ||||
-rw-r--r-- | vendor/xflags-macros/src/emit.rs | 350 | ||||
-rw-r--r-- | vendor/xflags-macros/src/lib.rs | 26 | ||||
-rw-r--r-- | vendor/xflags-macros/src/parse.rs | 166 | ||||
-rw-r--r-- | vendor/xflags-macros/src/update.rs | 3 |
5 files changed, 358 insertions, 200 deletions
diff --git a/vendor/xflags-macros/src/ast.rs b/vendor/xflags-macros/src/ast.rs index 5ccebd951..7fb9df1e1 100644 --- a/vendor/xflags-macros/src/ast.rs +++ b/vendor/xflags-macros/src/ast.rs @@ -4,6 +4,12 @@ pub(crate) struct XFlags { pub(crate) cmd: Cmd, } +impl XFlags { + pub fn is_anon(&self) -> bool { + self.cmd.name.is_empty() + } +} + #[derive(Debug)] pub(crate) struct Cmd { pub(crate) name: String, @@ -12,6 +18,7 @@ pub(crate) struct Cmd { pub(crate) flags: Vec<Flag>, pub(crate) subcommands: Vec<Cmd>, pub(crate) default: bool, + pub(crate) idx: u8, } #[derive(Debug)] @@ -30,6 +37,12 @@ pub(crate) struct Flag { pub(crate) val: Option<Val>, } +impl Flag { + pub(crate) fn is_help(&self) -> bool { + self.name == "help" + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum Arity { Optional, diff --git a/vendor/xflags-macros/src/emit.rs b/vendor/xflags-macros/src/emit.rs index 05d5f01dd..1f3d88ead 100644 --- a/vendor/xflags-macros/src/emit.rs +++ b/vendor/xflags-macros/src/emit.rs @@ -1,15 +1,25 @@ use crate::{ast, update}; -use std::{fmt::Write, path::Path}; +use std::{env, fmt::Write, path::Path}; + +macro_rules! w { + ($($tt:tt)*) => { + drop(write!($($tt)*)) + }; +} pub(crate) fn emit(xflags: &ast::XFlags) -> String { let mut buf = String::new(); + if xflags.is_anon() { + w!(buf, "{{\n"); + } + emit_cmd(&mut buf, &xflags.cmd); blank_line(&mut buf); emit_api(&mut buf, xflags); - if std::env::var("UPDATE_XFLAGS").is_ok() { + if !xflags.is_anon() && env::var("UPDATE_XFLAGS").is_ok() { if let Some(src) = &xflags.src { update::in_place(&buf, Path::new(src.as_str())) } else { @@ -22,22 +32,22 @@ pub(crate) fn emit(xflags: &ast::XFlags) -> String { } blank_line(&mut buf); - emit_impls(&mut buf, &xflags); + emit_impls(&mut buf, xflags); emit_help(&mut buf, xflags); - buf -} + if xflags.is_anon() { + w!(buf, "Flags::from_env_or_exit()"); + w!(buf, "}}\n"); + } -macro_rules! w { - ($($tt:tt)*) => { - drop(write!($($tt)*)) - }; + buf } fn emit_cmd(buf: &mut String, cmd: &ast::Cmd) { w!(buf, "#[derive(Debug)]\n"); w!(buf, "pub struct {}", cmd.ident()); - if cmd.args.is_empty() && cmd.flags.is_empty() && cmd.subcommands.is_empty() { + let flags = cmd.flags.iter().filter(|it| !it.is_help()).collect::<Vec<_>>(); + if cmd.args.is_empty() && flags.is_empty() && cmd.subcommands.is_empty() { w!(buf, ";\n"); return; } @@ -45,16 +55,16 @@ fn emit_cmd(buf: &mut String, cmd: &ast::Cmd) { for arg in &cmd.args { let ty = gen_arg_ty(arg.arity, &arg.val.ty); - w!(buf, " pub {}: {},\n", arg.val.ident(), ty); + w!(buf, " pub {}: {ty},\n", arg.val.ident()); } - if !cmd.args.is_empty() && !cmd.flags.is_empty() { + if !cmd.args.is_empty() && !flags.is_empty() { blank_line(buf); } - for flag in &cmd.flags { + for flag in &flags { let ty = gen_flag_ty(flag.arity, flag.val.as_ref().map(|it| &it.ty)); - w!(buf, " pub {}: {},\n", flag.ident(), ty); + w!(buf, " pub {}: {ty},\n", flag.ident()); } if cmd.has_subcommands() { @@ -67,8 +77,8 @@ fn emit_cmd(buf: &mut String, cmd: &ast::Cmd) { w!(buf, "#[derive(Debug)]\n"); w!(buf, "pub enum {} {{\n", cmd.cmd_enum_ident()); for sub in &cmd.subcommands { - let name = camel(&sub.name); - w!(buf, " {}({}),\n", name, name); + let name = sub.ident(); + w!(buf, " {name}({name}),\n"); } w!(buf, "}}\n"); @@ -104,9 +114,12 @@ fn gen_arg_ty(arity: ast::Arity, ty: &ast::Ty) -> String { } fn emit_api(buf: &mut String, xflags: &ast::XFlags) { - w!(buf, "impl {} {{\n", camel(&xflags.cmd.name)); + w!(buf, "impl {} {{\n", xflags.cmd.ident()); - w!(buf, " pub const HELP: &'static str = Self::HELP_;\n"); + w!(buf, " #[allow(dead_code)]\n"); + w!(buf, " pub fn from_env_or_exit() -> Self {{\n"); + w!(buf, " Self::from_env_or_exit_()\n"); + w!(buf, " }}\n"); blank_line(buf); w!(buf, " #[allow(dead_code)]\n"); @@ -123,7 +136,10 @@ fn emit_api(buf: &mut String, xflags: &ast::XFlags) { } fn emit_impls(buf: &mut String, xflags: &ast::XFlags) -> () { - w!(buf, "impl {} {{\n", camel(&xflags.cmd.name)); + w!(buf, "impl {} {{\n", xflags.cmd.ident()); + w!(buf, " fn from_env_or_exit_() -> Self {{\n"); + w!(buf, " Self::from_env_().unwrap_or_else(|err| err.exit())\n"); + w!(buf, " }}\n"); w!(buf, " fn from_env_() -> xflags::Result<Self> {{\n"); w!(buf, " let mut p = xflags::rt::Parser::new_from_env();\n"); w!(buf, " Self::parse_(&mut p)\n"); @@ -134,101 +150,115 @@ fn emit_impls(buf: &mut String, xflags: &ast::XFlags) -> () { w!(buf, " }}\n"); w!(buf, "}}\n"); blank_line(buf); - emit_impls_rec(buf, &xflags.cmd) -} - -fn emit_impls_rec(buf: &mut String, cmd: &ast::Cmd) -> () { - emit_impl(buf, cmd); - for sub in &cmd.subcommands { - blank_line(buf); - emit_impls_rec(buf, sub); - } + emit_parse(buf, &xflags.cmd) } -fn emit_impl(buf: &mut String, cmd: &ast::Cmd) -> () { - w!(buf, "impl {} {{\n", camel(&cmd.name)); +fn emit_parse(buf: &mut String, cmd: &ast::Cmd) { + w!(buf, "impl {} {{\n", cmd.ident()); w!(buf, "fn parse_(p_: &mut xflags::rt::Parser) -> xflags::Result<Self> {{\n"); + w!(buf, "#![allow(non_snake_case)]\n"); - for flag in &cmd.flags { - w!(buf, "let mut {} = Vec::new();\n", flag.ident()); - } + let mut prefix = String::new(); + emit_locals_rec(buf, &mut prefix, cmd); blank_line(buf); + w!(buf, "let mut state_ = 0u8;\n"); + w!(buf, "while let Some(arg_) = p_.pop_flag() {{\n"); + w!(buf, "match arg_ {{\n"); + { + w!(buf, "Ok(flag_) => match (state_, flag_.as_str()) {{\n"); + emit_match_flag_rec(buf, &mut prefix, cmd); + w!(buf, "_ => return Err(p_.unexpected_flag(&flag_)),\n"); + w!(buf, "}}\n"); - if !cmd.args.is_empty() { - for arg in &cmd.args { - w!(buf, "let mut {} = (false, Vec::new());\n", arg.val.ident()); - } - blank_line(buf); + w!(buf, "Err(arg_) => match (state_, arg_.to_str().unwrap_or(\"\")) {{\n"); + emit_match_arg_rec(buf, &mut prefix, cmd); + w!(buf, "_ => return Err(p_.unexpected_arg(arg_)),\n"); + w!(buf, "}}\n"); } + w!(buf, "}}\n"); + w!(buf, "}}\n"); + emit_default_transitions(buf, cmd); - if cmd.has_subcommands() { - w!(buf, "let mut sub_ = None;"); - blank_line(buf); + w!(buf, "Ok("); + emit_record_rec(buf, &mut prefix, cmd); + w!(buf, ")"); + + w!(buf, "}}\n"); + w!(buf, "}}\n"); +} + +fn emit_locals_rec(buf: &mut String, prefix: &mut String, cmd: &ast::Cmd) { + for flag in &cmd.flags { + if !flag.is_help() { + w!(buf, "let mut {prefix}{} = Vec::new();\n", flag.ident()); + } + } + for arg in &cmd.args { + w!(buf, "let mut {prefix}{} = (false, Vec::new());\n", arg.val.ident()); + } + for sub in &cmd.subcommands { + let l = sub.push_prefix(prefix); + emit_locals_rec(buf, prefix, sub); + prefix.truncate(l); } +} - w!(buf, "while let Some(arg_) = p_.pop_flag() {{\n"); - w!(buf, "match arg_ {{\n"); - { - w!(buf, "Ok(flag_) => match flag_.as_str() {{\n"); - for flag in &cmd.flags { - w!(buf, "\"--{}\"", flag.name); - if let Some(short) = &flag.short { - w!(buf, "| \"-{}\"", short); - } - w!(buf, " => {}.push(", flag.ident()); +fn emit_match_flag_rec(buf: &mut String, prefix: &mut String, cmd: &ast::Cmd) { + for flag in &cmd.flags { + w!(buf, "("); + emit_all_ids_rec(buf, cmd); + w!(buf, ", \"--{}\"", flag.name); + if let Some(short) = &flag.short { + w!(buf, "| \"-{short}\""); + } + w!(buf, ") => "); + if flag.is_help() { + w!(buf, "return Err(p_.help(Self::HELP_)),"); + } else { + w!(buf, "{prefix}{}.push(", flag.ident()); match &flag.val { Some(val) => match &val.ty { ast::Ty::OsString | ast::Ty::PathBuf => { w!(buf, "p_.next_value(&flag_)?.into()") } ast::Ty::FromStr(ty) => { - w!(buf, "p_.next_value_from_str::<{}>(&flag_)?", ty) + w!(buf, "p_.next_value_from_str::<{ty}>(&flag_)?") } }, None => w!(buf, "()"), } w!(buf, "),"); } - if cmd.default_subcommand().is_some() { - w!(buf, "_ => {{ p_.push_back(Ok(flag_)); break; }}"); - } else { - w!(buf, "_ => return Err(p_.unexpected_flag(&flag_)),"); - } - w!(buf, "}}\n"); } - { - w!(buf, "Err(arg_) => {{\n"); - if cmd.has_subcommands() { - w!(buf, "match arg_.to_str().unwrap_or(\"\") {{\n"); - for sub in cmd.named_subcommands() { - w!(buf, "\"{}\" => {{\n", sub.name); - w!( - buf, - "sub_ = Some({}::{}({}::parse_(p_)?));", - cmd.cmd_enum_ident(), - sub.ident(), - sub.ident() - ); - w!(buf, "break;"); - w!(buf, "}}\n"); - } - w!(buf, "_ => (),\n"); - w!(buf, "}}\n"); - } + if let Some(sub) = cmd.default_subcommand() { + w!(buf, "({}, _) => {{ p_.push_back(Ok(flag_)); state_ = {}; }}", cmd.idx, sub.idx); + } + for sub in &cmd.subcommands { + let l = sub.push_prefix(prefix); + emit_match_flag_rec(buf, prefix, sub); + prefix.truncate(l); + } +} +fn emit_match_arg_rec(buf: &mut String, prefix: &mut String, cmd: &ast::Cmd) { + for sub in cmd.named_subcommands() { + w!(buf, "({}, \"{}\") => state_ = {},\n", cmd.idx, sub.name, sub.idx); + } + if !cmd.args.is_empty() || cmd.has_subcommands() { + w!(buf, "({}, _) => {{\n", cmd.idx); for arg in &cmd.args { let done = match arg.arity { ast::Arity::Optional | ast::Arity::Required => "done_ @ ", ast::Arity::Repeated => "", }; - w!(buf, "if let ({}false, buf_) = &mut {} {{\n", done, arg.val.ident()); + w!(buf, "if let ({done}false, buf_) = &mut {prefix}{} {{\n", arg.val.ident()); w!(buf, "buf_.push("); match &arg.val.ty { ast::Ty::OsString | ast::Ty::PathBuf => { w!(buf, "arg_.into()") } ast::Ty::FromStr(ty) => { - w!(buf, "p_.value_from_str::<{}>(\"{}\", arg_)?", ty, arg.val.name); + w!(buf, "p_.value_from_str::<{ty}>(\"{}\", arg_)?", arg.val.name); } } w!(buf, ");\n"); @@ -241,79 +271,110 @@ fn emit_impl(buf: &mut String, cmd: &ast::Cmd) -> () { w!(buf, "continue;\n"); w!(buf, "}}\n"); } - if cmd.default_subcommand().is_some() { - w!(buf, "p_.push_back(Err(arg_)); break;"); + + if let Some(sub) = cmd.default_subcommand() { + w!(buf, "p_.push_back(Err(arg_)); state_ = {};", sub.idx); } else { w!(buf, "return Err(p_.unexpected_arg(arg_));"); } w!(buf, "}}\n"); } - w!(buf, "}}\n"); - w!(buf, "}}\n"); - if let Some(sub) = cmd.default_subcommand() { - w!(buf, "if sub_.is_none() {{\n"); - w!( - buf, - "sub_ = Some({}::{}({}::parse_(p_)?));", - cmd.cmd_enum_ident(), - sub.ident(), - sub.ident() - ); - w!(buf, "}}\n"); + for sub in &cmd.subcommands { + let l = sub.push_prefix(prefix); + emit_match_arg_rec(buf, prefix, sub); + prefix.truncate(l); } +} - w!(buf, "Ok(Self {{\n"); - if !cmd.args.is_empty() { - for arg in &cmd.args { - let val = &arg.val; - w!(buf, "{}: ", val.ident()); - match arg.arity { - ast::Arity::Optional => { - w!(buf, "p_.optional(\"{}\", {}.1)?", val.name, val.ident()) - } - ast::Arity::Required => { - w!(buf, "p_.required(\"{}\", {}.1)?", val.name, val.ident()) - } - ast::Arity::Repeated => w!(buf, "{}.1", val.ident()), - } - w!(buf, ",\n"); - } - blank_line(buf); - } +fn emit_record_rec(buf: &mut String, prefix: &mut String, cmd: &ast::Cmd) { + w!(buf, "{} {{\n", cmd.ident()); for flag in &cmd.flags { + if flag.is_help() { + continue; + } w!(buf, "{}: ", flag.ident()); match &flag.val { Some(_val) => match flag.arity { ast::Arity::Optional => { - w!(buf, "p_.optional(\"--{}\", {})?", flag.name, flag.ident()) + w!(buf, "p_.optional(\"--{}\", {prefix}{})?", flag.name, flag.ident()) } ast::Arity::Required => { - w!(buf, "p_.required(\"--{}\", {})?", flag.name, flag.ident()) + w!(buf, "p_.required(\"--{}\", {prefix}{})?", flag.name, flag.ident()) } - ast::Arity::Repeated => w!(buf, "{}", flag.ident()), + ast::Arity::Repeated => w!(buf, "{prefix}{}", flag.ident()), }, None => match flag.arity { ast::Arity::Optional => { - w!(buf, "p_.optional(\"--{}\", {})?.is_some()", flag.name, flag.ident()) + w!(buf, "p_.optional(\"--{}\", {prefix}{})?.is_some()", flag.name, flag.ident()) } ast::Arity::Required => { - w!(buf, "p_.required(\"--{}\", {})?", flag.name, flag.ident()) + w!(buf, "p_.required(\"--{}\", {prefix}{})?", flag.name, flag.ident()) } - ast::Arity::Repeated => w!(buf, "{}.len() as u32", flag.ident()), + ast::Arity::Repeated => w!(buf, "{prefix}{}.len() as u32", flag.ident()), }, } w!(buf, ",\n"); } + for arg in &cmd.args { + let val = &arg.val; + w!(buf, "{}: ", val.ident()); + match arg.arity { + ast::Arity::Optional => { + w!(buf, "p_.optional(\"{}\", {prefix}{}.1)?", val.name, val.ident()) + } + ast::Arity::Required => { + w!(buf, "p_.required(\"{}\", {prefix}{}.1)?", val.name, val.ident()) + } + ast::Arity::Repeated => w!(buf, "{prefix}{}.1", val.ident()), + } + w!(buf, ",\n"); + } if cmd.has_subcommands() { - w!(buf, "subcommand: p_.subcommand(sub_)?,\n"); + w!(buf, "subcommand: match state_ {{\n"); + for sub in &cmd.subcommands { + emit_leaf_ids_rec(buf, sub); + w!(buf, " => {}::{}(", cmd.cmd_enum_ident(), sub.ident()); + let l = prefix.len(); + prefix.push_str(&snake(&sub.name)); + prefix.push_str("__"); + emit_record_rec(buf, prefix, sub); + prefix.truncate(l); + w!(buf, "),\n"); + } + w!(buf, "_ => return Err(p_.subcommand_required()),"); + w!(buf, "}}\n"); } - w!(buf, "}})\n"); - w!(buf, "}}\n"); - w!(buf, "}}\n"); + w!(buf, "}}"); +} + +fn emit_leaf_ids_rec(buf: &mut String, cmd: &ast::Cmd) { + if cmd.has_subcommands() { + for sub in &cmd.subcommands { + emit_leaf_ids_rec(buf, sub) + } + } else { + w!(buf, "| {}", cmd.idx) + } +} + +fn emit_all_ids_rec(buf: &mut String, cmd: &ast::Cmd) { + w!(buf, "| {}", cmd.idx); + for sub in &cmd.subcommands { + emit_all_ids_rec(buf, sub) + } +} + +fn emit_default_transitions(buf: &mut String, cmd: &ast::Cmd) { + if let Some(sub) = cmd.default_subcommand() { + w!(buf, "state_ = if state_ == {} {{ {} }} else {{ state_ }};", cmd.idx, sub.idx); + } + for sub in &cmd.subcommands { + emit_default_transitions(buf, sub); + } } fn emit_help(buf: &mut String, xflags: &ast::XFlags) { @@ -327,7 +388,7 @@ fn emit_help(buf: &mut String, xflags: &ast::XFlags) { let help = format!("{:?}", help); let help = help.replace("\\n", "\n").replacen("\"", "\"\\\n", 1); - w!(buf, "const HELP_: &'static str = {};", help); + w!(buf, "const HELP_: &'static str = {help};"); w!(buf, "}}\n"); } @@ -336,26 +397,34 @@ fn write_lines_indented(buf: &mut String, multiline_str: &str, indent: usize) { if line.is_empty() { w!(buf, "\n") } else { - w!(buf, "{blank:indent$}{}\n", line, indent = indent, blank = ""); + w!(buf, "{blank:indent$}{line}\n", blank = ""); } } } fn help_rec(buf: &mut String, prefix: &str, cmd: &ast::Cmd) { - w!(buf, "{}{}\n", prefix, cmd.name); + let mut empty_help = true; + if !cmd.name.is_empty() { + empty_help = false; + w!(buf, "{}{}\n", prefix, cmd.name); + } if let Some(doc) = &cmd.doc { + empty_help = false; write_lines_indented(buf, doc, 2); } let indent = if prefix.is_empty() { "" } else { " " }; let args = cmd.args_with_default(); if !args.is_empty() { - blank_line(buf); + if !empty_help { + blank_line(buf); + } + empty_help = false; w!(buf, "{}ARGS:\n", indent); let mut blank = ""; for arg in &args { - w!(buf, "{}", blank); + w!(buf, "{blank}"); blank = "\n"; let (l, r) = match arg.arity { @@ -363,7 +432,7 @@ fn help_rec(buf: &mut String, prefix: &str, cmd: &ast::Cmd) { ast::Arity::Required => ("<", ">"), ast::Arity::Repeated => ("<", ">..."), }; - w!(buf, " {}{}{}\n", l, arg.val.name, r); + w!(buf, " {l}{}{r}\n", arg.val.name); if let Some(doc) = &arg.doc { write_lines_indented(buf, doc, 6) } @@ -372,17 +441,19 @@ fn help_rec(buf: &mut String, prefix: &str, cmd: &ast::Cmd) { let flags = cmd.flags_with_default(); if !flags.is_empty() { - blank_line(buf); - w!(buf, "{}OPTIONS:\n", indent); + if !empty_help { + blank_line(buf); + } + w!(buf, "{indent}OPTIONS:\n"); let mut blank = ""; for flag in &flags { - w!(buf, "{}", blank); + w!(buf, "{blank}",); blank = "\n"; - let short = flag.short.as_ref().map(|it| format!("-{}, ", it)).unwrap_or_default(); + let short = flag.short.as_ref().map(|it| format!("-{it}, ")).unwrap_or_default(); let value = flag.val.as_ref().map(|it| format!(" <{}>", it.name)).unwrap_or_default(); - w!(buf, " {}--{}{}\n", short, flag.name, value); + w!(buf, " {short}--{}{value}\n", flag.name); if let Some(doc) = &flag.doc { write_lines_indented(buf, doc, 6); } @@ -407,11 +478,20 @@ fn help_rec(buf: &mut String, prefix: &str, cmd: &ast::Cmd) { impl ast::Cmd { fn ident(&self) -> String { + if self.name.is_empty() { + return "Flags".to_string(); + } camel(&self.name) } fn cmd_enum_ident(&self) -> String { format!("{}Cmd", self.ident()) } + fn push_prefix(&self, buf: &mut String) -> usize { + let l = buf.len(); + buf.push_str(&snake(&self.name)); + buf.push_str("__"); + l + } fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() } @@ -510,10 +590,10 @@ mod tests { #[test] fn gen_it() { - let test_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/it"); + let test_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests"); let mut did_update = false; - for entry in fs::read_dir(test_dir.join("src")).unwrap() { + for entry in fs::read_dir(test_dir.join("data")).unwrap() { let entry = entry.unwrap(); let text = fs::read_to_string(entry.path()).unwrap(); @@ -531,7 +611,7 @@ mod tests { ); let name = entry.file_name(); - did_update |= update_on_disk_if_different(&test_dir.join(name), code); + did_update |= update_on_disk_if_different(&test_dir.join("it").join(name), code); if fmt.is_none() { panic!("syntax error"); diff --git a/vendor/xflags-macros/src/lib.rs b/vendor/xflags-macros/src/lib.rs index 8fac00587..f1c0e0599 100644 --- a/vendor/xflags-macros/src/lib.rs +++ b/vendor/xflags-macros/src/lib.rs @@ -8,19 +8,39 @@ pub fn xflags(_ts: proc_macro::TokenStream) -> proc_macro::TokenStream { // Stub out the code, but let rust-analyzer resolve the invocation #[cfg(not(test))] { - let cmd = parse::parse(_ts).unwrap(); - let text = emit::emit(&cmd); + let text = match parse::xflags(_ts) { + Ok(cmd) => emit::emit(&cmd), + Err(err) => format!("compile_error!(\"invalid flags syntax, {err}\");"), + }; text.parse().unwrap() } #[cfg(test)] unimplemented!() } +#[proc_macro] +pub fn parse_or_exit(_ts: proc_macro::TokenStream) -> proc_macro::TokenStream { + // Stub out the code, but let rust-analyzer resolve the invocation + #[cfg(not(test))] + { + let text = match parse::parse_or_exit(_ts) { + Ok(cmd) => emit::emit(&cmd), + Err(err) => format!("compile_error!(\"invalid flags syntax, {err}\")"), + }; + text.parse().unwrap() + } + #[cfg(test)] + { + let _ = parse::parse_or_exit; + unimplemented!(); + } +} + #[cfg(test)] pub fn compile(src: &str) -> String { use proc_macro2::TokenStream; let ts = src.parse::<TokenStream>().unwrap(); - let cmd = parse::parse(ts).unwrap(); + let cmd = parse::xflags(ts).unwrap(); emit::emit(&cmd) } diff --git a/vendor/xflags-macros/src/parse.rs b/vendor/xflags-macros/src/parse.rs index e9749b141..48f9588e9 100644 --- a/vendor/xflags-macros/src/parse.rs +++ b/vendor/xflags-macros/src/parse.rs @@ -1,4 +1,4 @@ -use std::mem; +use std::{fmt, mem}; #[cfg(not(test))] use proc_macro::{Delimiter, TokenStream, TokenTree}; @@ -16,15 +16,41 @@ pub(crate) struct Error { impl std::error::Error for Error {} -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.msg) +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.msg, f) } } -pub(crate) fn parse(ts: TokenStream) -> Result<ast::XFlags> { - let mut p = Parser::new(ts); - xflags(&mut p) +pub(crate) fn xflags(ts: TokenStream) -> Result<ast::XFlags> { + let p = &mut Parser::new(ts); + let src = if p.eat_keyword("src") { Some(p.expect_string()?) } else { None }; + let doc = opt_doc(p)?; + let mut cmd = cmd(p)?; + cmd.doc = doc; + add_help(&mut cmd); + let res = ast::XFlags { src, cmd }; + Ok(res) +} + +pub(crate) fn parse_or_exit(ts: TokenStream) -> Result<ast::XFlags> { + let p = &mut Parser::new(ts); + let mut cmd = anon_cmd(p)?; + assert!(cmd.subcommands.is_empty()); + add_help(&mut cmd); + let res = ast::XFlags { src: None, cmd }; + Ok(res) +} + +fn add_help(cmd: &mut ast::Cmd) { + let help = ast::Flag { + arity: ast::Arity::Optional, + name: "help".to_string(), + short: Some("h".to_string()), + doc: Some("Prints help information.".to_string()), + val: None, + }; + cmd.flags.push(help); } macro_rules! format_err { @@ -40,19 +66,25 @@ macro_rules! bail { }; } -fn xflags(p: &mut Parser) -> Result<ast::XFlags> { - let src = if p.eat_keyword("src") { Some(p.expect_string()?) } else { None }; - let doc = opt_doc(p)?; - let mut cmd = cmd(p)?; - cmd.doc = doc; - let res = ast::XFlags { src, cmd }; - Ok(res) +fn anon_cmd(p: &mut Parser) -> Result<ast::Cmd> { + cmd_impl(p, true) } fn cmd(p: &mut Parser) -> Result<ast::Cmd> { - p.expect_keyword("cmd")?; + cmd_impl(p, false) +} + +fn cmd_impl(p: &mut Parser, anon: bool) -> Result<ast::Cmd> { + let name = if anon { + String::new() + } else { + p.expect_keyword("cmd")?; + cmd_name(p)? + }; + + let idx = p.idx; + p.idx += 1; - let name = cmd_name(p)?; let mut res = ast::Cmd { name, doc: None, @@ -60,25 +92,16 @@ fn cmd(p: &mut Parser) -> Result<ast::Cmd> { flags: Vec::new(), subcommands: Vec::new(), default: false, + idx, }; - while !p.at_delim(Delimiter::Brace) { - let doc = opt_doc(p)?; - let arity = arity(p)?; - match opt_val(p)? { - Some(val) => { - let arg = ast::Arg { arity, doc, val }; - res.args.push(arg); - } - None => bail!("expected ident"), - } + if !anon { + p.enter_delim(Delimiter::Brace)?; } - - p.enter_delim(Delimiter::Brace)?; while !p.end() { let doc = opt_doc(p)?; - let default = p.eat_keyword("default"); - if default || p.at_keyword("cmd") { + let default = !anon && p.eat_keyword("default"); + if !anon && (default || p.at_keyword("cmd")) { let mut cmd = cmd(p)?; cmd.doc = doc; res.subcommands.push(cmd); @@ -90,35 +113,56 @@ fn cmd(p: &mut Parser) -> Result<ast::Cmd> { res.subcommands.rotate_right(1); } } else { - let mut flag = flag(p)?; - flag.doc = doc; - res.flags.push(flag); + let arity = arity(p)?; + let is_val = p.lookahead_punct(':', 1); + let name = p.expect_name()?; + if name.starts_with('-') { + let mut flag = flag(p, name)?; + flag.doc = doc; + flag.arity = arity; + res.flags.push(flag) + } else if is_val { + p.expect_punct(':')?; + let ty = ty(p)?; + let val = ast::Val { name, ty }; + let arg = ast::Arg { arity, doc, val }; + res.args.push(arg); + } else { + bail!("expected `--flag` or `arg: Type`") + } } } - p.exit_delim()?; + if !anon { + p.exit_delim()?; + } Ok(res) } -fn flag(p: &mut Parser) -> Result<ast::Flag> { - let arity = arity(p)?; - - let mut short = None; - let mut name = flag_name(p)?; - if !name.starts_with("--") { +fn flag(p: &mut Parser, name: String) -> Result<ast::Flag> { + let short; + let long; + if name.starts_with("--") { + short = None; + long = name; + } else { short = Some(name); if !p.eat_punct(',') { bail!("long option is required for `{}`", short.unwrap()); } - name = flag_name(p)?; - if !name.starts_with("--") { - bail!("long name must begin with `--`: `{}`", name); + long = flag_name(p)?; + if !long.starts_with("--") { + bail!("long name must begin with `--`: `{long}`"); } } + if long == "--help" { + bail!("`--help` flag is generated automatically") + } + let val = opt_val(p)?; Ok(ast::Flag { - arity, - name: name[2..].to_string(), + arity: ast::Arity::Required, + name: long[2..].to_string(), short: short.map(|it| it[1..].to_string()), doc: None, val, @@ -148,7 +192,7 @@ fn arity(p: &mut Parser) -> Result<ast::Arity> { return Ok(ast::Arity::Repeated); } if let Some(name) = p.eat_name() { - bail!("expected one of `optional`, `required`, `repeated`, got `{}`", name) + bail!("expected one of `optional`, `required`, `repeated`, got `{name}`") } bail!("expected one of `optional`, `required`, `repeated`, got {:?}", p.ts.pop()) } @@ -193,7 +237,7 @@ fn opt_doc(p: &mut Parser) -> Result<Option<String>> { fn cmd_name(p: &mut Parser) -> Result<String> { let name = p.expect_name()?; if name.starts_with('-') { - bail!("command name can't begin with `-`: `{}`", name); + bail!("command name can't begin with `-`: `{name}`"); } Ok(name) } @@ -201,7 +245,7 @@ fn cmd_name(p: &mut Parser) -> Result<String> { fn flag_name(p: &mut Parser) -> Result<String> { let name = p.expect_name()?; if !name.starts_with('-') { - bail!("flag name should begin with `-`: `{}`", name); + bail!("flag name should begin with `-`: `{name}`"); } Ok(name) } @@ -209,21 +253,16 @@ fn flag_name(p: &mut Parser) -> Result<String> { struct Parser { stack: Vec<Vec<TokenTree>>, ts: Vec<TokenTree>, + idx: u8, } impl Parser { fn new(ts: TokenStream) -> Self { let mut ts = ts.into_iter().collect::<Vec<_>>(); ts.reverse(); - Self { stack: Vec::new(), ts } + Self { stack: Vec::new(), ts, idx: 0 } } - fn at_delim(&mut self, delimiter: Delimiter) -> bool { - match self.ts.last() { - Some(TokenTree::Group(g)) => g.delimiter() == delimiter, - _ => false, - } - } fn enter_delim(&mut self, delimiter: Delimiter) -> Result<()> { match self.ts.pop() { Some(TokenTree::Group(g)) if g.delimiter() == delimiter => { @@ -249,7 +288,7 @@ impl Parser { fn expect_keyword(&mut self, kw: &str) -> Result<()> { if !self.eat_keyword(kw) { - bail!("expected `{}`", kw) + bail!("expected `{kw}`") } Ok(()) } @@ -271,7 +310,7 @@ impl Parser { fn expect_name(&mut self) -> Result<String> { self.eat_name().ok_or_else(|| { let next = self.ts.pop().map(|it| it.to_string()).unwrap_or_default(); - format_err!("expected a name, got: `{}`", next) + format_err!("expected a name, got: `{next}`") }) } fn eat_name(&mut self) -> Option<String> { @@ -307,7 +346,7 @@ impl Parser { fn expect_punct(&mut self, punct: char) -> Result<()> { if !self.eat_punct(punct) { - bail!("expected `{}`", punct) + bail!("expected `{punct}`") } Ok(()) } @@ -330,11 +369,18 @@ impl Parser { fn expect_string(&mut self) -> Result<String> { match self.ts.pop() { Some(TokenTree::Literal(lit)) if lit.to_string().starts_with('"') => { - let text = lit.to_string(); - let res = text.trim_matches('"').to_string(); + let res = str_lit_value(lit.to_string()); Ok(res) } _ => bail!("expected a string"), } } } + +/// "Parser" a string literal into the corresponding value. +/// +/// Really needs support in the proc_macro library: +/// <https://internals.rust-lang.org/t/getting-value-out-of-proc-macro-literal/14140> +fn str_lit_value(lit: String) -> String { + lit.trim_matches('"').replace("\\'", "'") +} diff --git a/vendor/xflags-macros/src/update.rs b/vendor/xflags-macros/src/update.rs index e476e37aa..83a404c93 100644 --- a/vendor/xflags-macros/src/update.rs +++ b/vendor/xflags-macros/src/update.rs @@ -6,8 +6,7 @@ pub(crate) fn in_place(api: &str, path: &Path) { Path::new(&dir).join(path) }; - let mut text = - fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read {:?}", path)); + let mut text = fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read {path:?}")); let (insert_to, indent) = locate(&text); |