Mark ab/6881855 as merged
Bug: 172690556
Change-Id: I1bcff03831f2f1e3691a33c570490228592c4345
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 2df1587..ae54c03 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "203c6bc19e561da1734873794bbd420e0bfc79b9"
+ "sha1": "6fa8d68298eab6b4eb94b4b40ce64992b20c7263"
}
}
diff --git a/Android.bp b/Android.bp
index 0c11915..627688f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,7 +36,7 @@
// proc-macro-error-attr-1.0.4
// proc-macro2-1.0.24 "default,proc-macro"
// quote-1.0.7 "default,proc-macro"
-// syn-1.0.42 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote"
+// syn-1.0.48 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote"
// unicode-segmentation-1.6.0
// unicode-xid-0.2.1 "default"
// version_check-0.9.2
diff --git a/Cargo.toml b/Cargo.toml
index 301f046..4f0f326 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
[package]
edition = "2018"
name = "structopt-derive"
-version = "0.4.7"
+version = "0.4.13"
authors = ["Guillaume Pinot <texitoi@texitoi.eu>"]
description = "Parse command line argument by defining a struct, derive crate."
documentation = "https://docs.rs/structopt-derive"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
old mode 100644
new mode 100755
index 4e97528..176b34a
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "structopt-derive"
-version = "0.4.7"
+version = "0.4.13"
edition = "2018"
authors = ["Guillaume Pinot <texitoi@texitoi.eu>"]
description = "Parse command line argument by defining a struct, derive crate."
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
old mode 100644
new mode 100755
diff --git a/LICENSE-MIT b/LICENSE-MIT
old mode 100644
new mode 100755
diff --git a/METADATA b/METADATA
index bbf085b..18397ed 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/structopt-derive/structopt-derive-0.4.7.crate"
+ value: "https://static.crates.io/crates/structopt-derive/structopt-derive-0.4.13.crate"
}
- version: "0.4.7"
+ version: "0.4.13"
license_type: NOTICE
last_upgrade_date {
year: 2020
- month: 5
- day: 6
+ month: 10
+ day: 28
}
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 1c512e3..1e756de 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -2,8 +2,8 @@
{
"presubmit": [
{
- "name": "structopt-derive_host_test_src_lib",
- "host": true
+ "host": true,
+ "name": "structopt-derive_host_test_src_lib"
}
]
}
diff --git a/src/attrs.rs b/src/attrs.rs
old mode 100644
new mode 100755
index d75b50c..11655b8
--- a/src/attrs.rs
+++ b/src/attrs.rs
@@ -65,6 +65,10 @@
Snake,
/// Use the original attribute name defined in the code.
Verbatim,
+ /// Keep all letters lowercase and remove word boundaries.
+ Lower,
+ /// Keep all letters uppercase and remove word boundaries.
+ Upper,
}
#[derive(Clone)]
@@ -188,6 +192,8 @@
"screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
"snake" | "snakecase" => cs(Snake),
"verbatim" | "verbatimcase" => cs(Verbatim),
+ "lower" | "lowercase" => cs(Lower),
+ "upper" | "uppercase" => cs(Upper),
s => abort!(name, "unsupported casing: `{}`", s),
}
}
@@ -208,6 +214,8 @@
ScreamingSnake => s.to_shouty_snake_case(),
Snake => s.to_snake_case(),
Verbatim => s,
+ Lower => s.to_snake_case().replace("_", ""),
+ Upper => s.to_shouty_snake_case().replace("_", ""),
};
quote_spanned!(ident.span()=> #s)
}
@@ -437,12 +445,16 @@
"parse attribute is not allowed for flattened entry"
);
}
- if res.has_explicit_methods() || res.has_doc_methods() {
+ if res.has_explicit_methods() {
abort!(
res.kind.span(),
- "methods and doc comments are not allowed for flattened entry"
+ "methods are not allowed for flattened entry"
);
}
+
+ if res.has_doc_methods() {
+ res.doc_comment = vec![];
+ }
}
Kind::ExternalSubcommand => {}
diff --git a/src/doc_comments.rs b/src/doc_comments.rs
old mode 100644
new mode 100755
diff --git a/src/lib.rs b/src/lib.rs
old mode 100644
new mode 100755
index 5e49468..b99bd35
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,9 @@
//! for the usage of `#[derive(StructOpt)]`.
#![allow(clippy::large_enum_variant)]
+// FIXME: remove when and if our MSRV hits 1.42
+#![allow(clippy::match_like_matches_macro)]
+#![forbid(unsafe_code)]
extern crate proc_macro;
@@ -28,7 +31,7 @@
use proc_macro2::{Span, TokenStream};
use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy};
-use quote::{quote, quote_spanned};
+use quote::{format_ident, quote, quote_spanned};
use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, *};
/// Default casing style for generated arguments.
@@ -239,6 +242,16 @@
}
fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
+ // This ident is used in several match branches below,
+ // and the `quote[_spanned]` invocations have different spans.
+ //
+ // Given that this ident is used in several places and
+ // that the branches are located inside of a loop, it is possible that
+ // this ident will be given _different_ spans in different places, and
+ // thus will not be the _same_ ident anymore. To make sure the `matches`
+ // is always the same, we factor it out.
+ let matches = format_ident!("matches");
+
let fields = fields.iter().map(|field| {
let attrs = Attrs::from_field(
field,
@@ -265,13 +278,13 @@
};
quote_spanned! { kind.span()=>
#field_name: <#subcmd_type as ::structopt::StructOptInternal>::from_subcommand(
- matches.subcommand())
+ #matches.subcommand())
#unwrapper
}
}
Kind::Flatten => quote_spanned! { kind.span()=>
- #field_name: ::structopt::StructOpt::from_clap(matches)
+ #field_name: ::structopt::StructOpt::from_clap(#matches)
},
Kind::Skip(val) => match val {
@@ -318,24 +331,24 @@
let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
let name = attrs.cased_name();
let field_value = match **ty {
- Ty::Bool => quote_spanned!(ty.span()=> matches.is_present(#name)),
+ Ty::Bool => quote_spanned!(ty.span()=> #matches.is_present(#name)),
Ty::Option => quote_spanned! { ty.span()=>
- matches.#value_of(#name)
+ #matches.#value_of(#name)
.map(#parse)
},
Ty::OptionOption => quote_spanned! { ty.span()=>
- if matches.is_present(#name) {
- Some(matches.#value_of(#name).map(#parse))
+ if #matches.is_present(#name) {
+ Some(#matches.#value_of(#name).map(#parse))
} else {
None
}
},
Ty::OptionVec => quote_spanned! { ty.span()=>
- if matches.is_present(#name) {
- Some(matches.#values_of(#name)
+ if #matches.is_present(#name) {
+ Some(#matches.#values_of(#name)
.map_or_else(Vec::new, |v| v.map(#parse).collect()))
} else {
None
@@ -343,20 +356,20 @@
},
Ty::Vec => quote_spanned! { ty.span()=>
- matches.#values_of(#name)
+ #matches.#values_of(#name)
.map_or_else(Vec::new, |v| v.map(#parse).collect())
},
Ty::Other if occurrences => quote_spanned! { ty.span()=>
- #parse(matches.#value_of(#name))
+ #parse(#matches.#value_of(#name))
},
Ty::Other if flag => quote_spanned! { ty.span()=>
- #parse(matches.is_present(#name))
+ #parse(#matches.is_present(#name))
},
Ty::Other => quote_spanned! { ty.span()=>
- matches.#value_of(#name)
+ #matches.#value_of(#name)
.map(#parse)
.unwrap()
},
@@ -471,8 +484,9 @@
let kind = attrs.kind();
match &*kind {
Kind::ExternalSubcommand => {
+ let app_var = Ident::new("app", Span::call_site());
quote_spanned! { attrs.kind().span()=>
- let app = app.setting(
+ let #app_var = #app_var.setting(
::structopt::clap::AppSettings::AllowExternalSubcommands
);
}
@@ -549,7 +563,8 @@
quote! {
fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
<#name as ::structopt::StructOptInternal>::from_subcommand(matches.subcommand())
- .unwrap()
+ .expect("structopt misuse: You likely tried to #[flatten] a struct \
+ that contains #[subcommand]. This is forbidden.")
}
}
}
@@ -648,10 +663,10 @@
}
(external, None) => {
- ::std::option::Option::Some(#name::#var_name({
+ ::std::option::Option::Some(#name::#var_name(
::std::iter::once(#str_ty::from(external))
.collect::<::std::vec::Vec<_>>()
- }))
+ ))
}
}
},
@@ -720,7 +735,7 @@
#[cfg(feature = "paw")]
fn gen_paw_impl(name: &Ident) -> TokenStream {
quote! {
- impl paw::ParseArgs for #name {
+ impl ::structopt::paw::ParseArgs for #name {
type Error = std::io::Error;
fn parse_args() -> std::result::Result<Self, Self::Error> {
diff --git a/src/parse.rs b/src/parse.rs
old mode 100644
new mode 100755
diff --git a/src/spanned.rs b/src/spanned.rs
old mode 100644
new mode 100755
index 19dbe47..1c02a82
--- a/src/spanned.rs
+++ b/src/spanned.rs
@@ -88,7 +88,7 @@
// this is the simplest way out of correct ones to change span on
// arbitrary token tree I can come up with
let tt = self.val.to_token_stream().into_iter().map(|mut tt| {
- tt.set_span(self.span.clone());
+ tt.set_span(self.span);
tt
});
diff --git a/src/ty.rs b/src/ty.rs
old mode 100644
new mode 100755
index 89d8b00..ad3acd9
--- a/src/ty.rs
+++ b/src/ty.rs
@@ -63,6 +63,8 @@
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 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 @@
.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()
}