From 1a8f8f7958ff3724049231a9865d1c85a08a96e9 Mon Sep 17 00:00:00 2001 From: CreepySkeleton Date: Mon, 12 Oct 2020 17:49:34 +0300 Subject: [PATCH] Fix #439 If memory serves, a patch like this was submitted to clap_derive, but not to structopt. Oops. --- structopt-derive/src/ty.rs | 21 +++++++++++++++++++++ tests/regressions.rs | 25 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/regressions.rs diff --git a/structopt-derive/src/ty.rs b/structopt-derive/src/ty.rs index 89d8b00a..ad3acd9d 100644 --- a/structopt-derive/src/ty.rs +++ b/structopt-derive/src/ty.rs @@ -63,6 +63,8 @@ fn subty_if(ty: &syn::Type, f: F) -> Option<&syn::Type> where F: FnOnce(&PathSegment) -> bool, { + let ty = strip_group(ty); + only_last_segment(ty) .filter(|segment| f(segment)) .and_then(|segment| { @@ -85,6 +87,8 @@ pub fn subty_if_name<'a>(ty: &'a syn::Type, name: &str) -> Option<&'a syn::Type> } pub fn is_simple_ty(ty: &syn::Type, name: &str) -> bool { + let ty = strip_group(ty); + only_last_segment(ty) .map(|segment| { if let PathArguments::None = segment.arguments { @@ -96,6 +100,23 @@ pub fn is_simple_ty(ty: &syn::Type, name: &str) -> bool { .unwrap_or(false) } +// If the struct is placed inside of a macro_rules! declaration, +// in some circumstances, the tokens inside will be enclosed +// in `proc_macro::Group` delimited by invisible `proc_macro::Delimiter::None`. +// +// In syn speak, this is encoded via `*::Group` variants. We don't really care about +// that, so let's just strip it. +// +// Details: https://doc.rust-lang.org/proc_macro/enum.Delimiter.html#variant.None +// See also: https://github.com/TeXitoi/structopt/issues/439 +fn strip_group(mut ty: &syn::Type) -> &syn::Type { + while let Type::Group(group) = ty { + ty = &*group.elem; + } + + ty +} + fn is_generic_ty(ty: &syn::Type, name: &str) -> bool { subty_if_name(ty, name).is_some() } diff --git a/tests/regressions.rs b/tests/regressions.rs new file mode 100644 index 00000000..9b1a93a4 --- /dev/null +++ b/tests/regressions.rs @@ -0,0 +1,25 @@ +use structopt::StructOpt; + +mod utils; +use utils::*; + +#[test] +fn invisible_group_issue_439() { + macro_rules! m { + ($bool:ty) => { + #[derive(Debug, StructOpt)] + struct Opts { + #[structopt(long = "x")] + x: $bool + } + }; + } + + m!(bool); + + let help = get_long_help::(); + + assert!(help.contains("--x")); + assert!(!help.contains("--x ")); + Opts::from_iter_safe(&["test", "--x"]).unwrap(); +}