diff --git a/.travis.yml b/.travis.yml index 45793de3..11a38d41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,11 @@ rust: - beta - nightly matrix: + include: + - rust: nightly + env: FEATURES="--features testing_only_proc_macro2_nightly" allow_failures: - rust: nightly + +script: + - cargo test $FEATURES diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb1059a..6db7ca53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# Unreleased + +* Work around breakage when `proc-macro2`'s nightly feature is enabled. ([#77](https://github.com/Texitoi/structopt/pull/77) and [proc-macro2#67](https://github.com/alexcrichton/proc-macro2/issues/67)) + # v0.2.4 (2018-02-25) * Fix compilation with `#![deny(missig_docs]` ([#74](https://github.com/TeXitoi/structopt/issues/74)) by [@TeXitoi](https://github.com/TeXitoi) diff --git a/Cargo.toml b/Cargo.toml index c4503701..81bdf2a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ readme = "README.md" [features] default = ["clap/default"] +testing_only_proc_macro2_nightly = ["structopt-derive/testing_only_proc_macro2_nightly"] [badges] travis-ci = { repository = "TeXitoi/structopt" } diff --git a/structopt-derive/Cargo.toml b/structopt-derive/Cargo.toml index da21f0c3..5be85e3d 100644 --- a/structopt-derive/Cargo.toml +++ b/structopt-derive/Cargo.toml @@ -17,5 +17,8 @@ syn = "0.12" quote = "0.4" proc-macro2 = "0.2" +[features] +testing_only_proc_macro2_nightly = ["proc-macro2/nightly"] + [lib] proc-macro = true diff --git a/structopt-derive/src/attrs.rs b/structopt-derive/src/attrs.rs index d53a9ea9..724c37e1 100644 --- a/structopt-derive/src/attrs.rs +++ b/structopt-derive/src/attrs.rs @@ -50,7 +50,7 @@ impl Attrs { Attrs { name: name, methods: vec![], - parser: (Parser::TryFromStr, quote!(::std::str::FromStr::from_str)), + parser: (Parser::TryFromStr, my_quote!(::std::str::FromStr::from_str)), has_custom_parser: false, is_subcommand: false, } @@ -67,7 +67,7 @@ impl Attrs { ("name", new_name) => self.name = new_name.into(), (name, arg) => self.methods.push(Method { name: name.to_string(), - args: quote!(#arg), + args: my_quote!(#arg), }), } } @@ -79,21 +79,21 @@ impl Attrs { let iter = attrs.iter() .filter_map(|attr| { let path = &attr.path; - match quote!(#path) == quote!(structopt) { + match my_quote!(#path) == my_quote!(structopt) { true => Some( attr.interpret_meta() - .expect(&format!("invalid structopt syntax: {}", quote!(attr))) + .expect(&format!("invalid structopt syntax: {}", my_quote!(attr))) ), false => None, } }). flat_map(|m| match m { List(l) => l.nested, - tokens => panic!("unsupported syntax: {}", quote!(#tokens).to_string()), + tokens => panic!("unsupported syntax: {}", my_quote!(#tokens).to_string()), }) .map(|m| match m { Meta(m) => m, - ref tokens => panic!("unsupported syntax: {}", quote!(#tokens).to_string()), + ref tokens => panic!("unsupported syntax: {}", my_quote!(#tokens).to_string()), }); for attr in iter { match attr { @@ -102,7 +102,7 @@ impl Attrs { NameValue(MetaNameValue { ident, lit, .. }) => { self.methods.push(Method { name: ident.to_string(), - args: quote!(#lit), + args: my_quote!(#lit), }) } List(MetaList { ident, ref nested, .. }) if ident == "parse" => { @@ -114,21 +114,21 @@ impl Attrs { Meta(NameValue(MetaNameValue { ident, lit: Str(ref v), .. })) => { let function: syn::Path = v.parse().expect("parser function path"); let parser = ident.as_ref().parse().unwrap(); - (parser, quote!(#function)) + (parser, my_quote!(#function)) } Meta(Word(ref i)) => { use Parser::*; let parser = i.as_ref().parse().unwrap(); let function = match parser { - FromStr => quote!(::std::convert::From::from), - TryFromStr => quote!(::std::str::FromStr::from_str), - FromOsStr => quote!(::std::convert::From::from), + FromStr => my_quote!(::std::convert::From::from), + TryFromStr => my_quote!(::std::str::FromStr::from_str), + FromOsStr => my_quote!(::std::convert::From::from), TryFromOsStr => panic!("cannot omit parser function name with `try_from_os_str`"), - FromOccurrences => quote!({|v| v as _}), + FromOccurrences => my_quote!({|v| v as _}), }; (parser, function) } - ref l @ _ => panic!("unknown value parser specification: {}", quote!(#l)), + ref l @ _ => panic!("unknown value parser specification: {}", my_quote!(#l)), }; } List(MetaList { ident, ref nested, .. }) if ident == "raw" => { @@ -136,29 +136,29 @@ impl Attrs { match *method { Meta(NameValue(MetaNameValue { ident, lit: Str(ref v), .. })) => self.push_raw_method(ident.as_ref(), v), - ref mi @ _ => panic!("unsupported raw entry: {}", quote!(#mi)), + ref mi @ _ => panic!("unsupported raw entry: {}", my_quote!(#mi)), } } } Word(ref w) if w == "subcommand" => self.is_subcommand = true, ref i @ List(..) | ref i @ Word(..) => - panic!("unsupported option: {}", quote!(#i)), + panic!("unsupported option: {}", my_quote!(#i)), } } } fn push_raw_method(&mut self, name: &str, args: &LitStr) { let ts: ::proc_macro2::TokenStream = args.value().parse() - .expect(&format!("bad parameter {} = {}: the parameter must be valid rust code", name, quote!(#args))); + .expect(&format!("bad parameter {} = {}: the parameter must be valid rust code", name, my_quote!(#args))); self.methods.push(Method { name: name.to_string(), - args: quote!(#(#ts)*), + args: my_quote!(#(#ts)*), }) } fn push_doc_comment(&mut self, attrs: &[Attribute], name: &str) { let doc_comments: Vec<_> = attrs.iter() .filter_map(|attr| { let path = &attr.path; - match quote!(#path) == quote!(doc) { + match my_quote!(#path) == my_quote!(doc) { true => attr.interpret_meta(), false => None, } @@ -195,7 +195,7 @@ impl Attrs { .join("\n"); self.methods.push(Method { name: name.to_string(), - args: quote!(#arg), + args: my_quote!(#arg), }); } pub fn from_struct(attrs: &[Attribute], name: String) -> Attrs { @@ -243,9 +243,9 @@ impl Attrs { pub fn methods(&self) -> Tokens { let methods = self.methods.iter().map(|&Method { ref name, ref args }| { let name: ::syn::Ident = name.as_str().into(); - quote!( .#name(#args) ) + my_quote!( .#name(#args) ) }); - quote!( #(#methods)* ) + my_quote!( #(#methods)* ) } pub fn name(&self) -> &str { &self.name diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 5deedc39..fb7bce25 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -16,6 +16,9 @@ extern crate syn; extern crate quote; extern crate proc_macro2; +#[macro_use] +mod macros; + mod attrs; use proc_macro::TokenStream; @@ -94,12 +97,12 @@ fn gen_augmentation(fields: &Punctuated, app_var: &Ident) -> quote _ => &field.ty }; let required = if cur_type == Ty::Option { - quote!() + my_quote!() } else { - quote!( let #app_var = #app_var.setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp); ) + my_quote!( let #app_var = #app_var.setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp); ) }; - quote!{ + my_quote!{ let #app_var = #subcmd_type ::augment_clap( #app_var ); #required } @@ -124,35 +127,35 @@ fn gen_augmentation(fields: &Punctuated, app_var: &Ident) -> quote } let validator = match *attrs.parser() { - (Parser::TryFromStr, ref f) => quote! { + (Parser::TryFromStr, ref f) => my_quote! { .validator(|s| { #f(&s) .map(|_: #convert_type| ()) .map_err(|e| e.to_string()) }) }, - (Parser::TryFromOsStr, ref f) => quote! { + (Parser::TryFromOsStr, ref f) => my_quote! { .validator_os(|s| #f(&s).map(|_: #convert_type| ())) }, - _ => quote!(), + _ => my_quote!(), }; let modifier = match cur_type { - Ty::Bool => quote!( .takes_value(false).multiple(false) ), - Ty::Option => quote!( .takes_value(true).multiple(false) #validator ), - Ty::Vec => quote!( .takes_value(true).multiple(true) #validator ), - Ty::Other if occurences => quote!( .takes_value(false).multiple(true) ), + Ty::Bool => my_quote!( .takes_value(false).multiple(false) ), + Ty::Option => my_quote!( .takes_value(true).multiple(false) #validator ), + Ty::Vec => my_quote!( .takes_value(true).multiple(true) #validator ), + Ty::Other if occurences => my_quote!( .takes_value(false).multiple(true) ), Ty::Other => { let required = !attrs.has_method("default_value"); - quote!( .takes_value(true).multiple(false).required(#required) #validator ) + my_quote!( .takes_value(true).multiple(false).required(#required) #validator ) }, }; let methods = attrs.methods(); let name = attrs.name(); - Some(quote!(.arg(::structopt::clap::Arg::with_name(#name)#modifier#methods))) + Some(my_quote!(.arg(::structopt::clap::Arg::with_name(#name)#modifier#methods))) }); - quote! {{ + my_quote! {{ let #app_var = #app_var #( #args )* ; #( #subcmds )* #app_var @@ -170,10 +173,10 @@ fn gen_constructor(fields: &Punctuated) -> quote::Tokens { _ => &field.ty }; let unwrapper = match cur_type { - Ty::Option => quote!(), - _ => quote!( .unwrap() ) + Ty::Option => my_quote!(), + _ => my_quote!( .unwrap() ) }; - quote!(#field_name: #subcmd_type::from_subcommand(matches.subcommand())#unwrapper) + my_quote!(#field_name: #subcmd_type::from_subcommand(matches.subcommand())#unwrapper) } else { let real_ty = &field.ty; let mut cur_type = ty(real_ty); @@ -183,45 +186,45 @@ fn gen_constructor(fields: &Punctuated) -> quote::Tokens { use Parser::*; let (value_of, values_of, parse) = match *attrs.parser() { - (FromStr, ref f) => (quote!(value_of), quote!(values_of), f.clone()), + (FromStr, ref f) => (my_quote!(value_of), my_quote!(values_of), f.clone()), (TryFromStr, ref f) => - (quote!(value_of), quote!(values_of), quote!(|s| #f(s).unwrap())), + (my_quote!(value_of), my_quote!(values_of), my_quote!(|s| #f(s).unwrap())), (FromOsStr, ref f) => - (quote!(value_of_os), quote!(values_of_os), f.clone()), + (my_quote!(value_of_os), my_quote!(values_of_os), f.clone()), (TryFromOsStr, ref f) => - (quote!(value_of_os), quote!(values_of_os), quote!(|s| #f(s).unwrap())), - (FromOccurrences, ref f) => (quote!(occurrences_of), quote!(), f.clone()), + (my_quote!(value_of_os), my_quote!(values_of_os), my_quote!(|s| #f(s).unwrap())), + (FromOccurrences, ref f) => (my_quote!(occurrences_of), my_quote!(), f.clone()), }; let occurences = attrs.parser().0 == Parser::FromOccurrences; let name = attrs.name(); let field_value = match cur_type { - Ty::Bool => quote!(matches.is_present(#name)), - Ty::Option => quote! { + Ty::Bool => my_quote!(matches.is_present(#name)), + Ty::Option => my_quote! { matches.#value_of(#name) .as_ref() .map(#parse) }, - Ty::Vec => quote! { + Ty::Vec => my_quote! { matches.#values_of(#name) .map(|v| v.map(#parse).collect()) .unwrap_or_else(Vec::new) }, - Ty::Other if occurences => quote! { + Ty::Other if occurences => my_quote! { #parse(matches.#value_of(#name)) }, - Ty::Other => quote! { + Ty::Other => my_quote! { matches.#value_of(#name) .map(#parse) .unwrap() }, }; - quote!( #field_name: #field_value ) + my_quote!( #field_name: #field_value ) } }); - quote! {{ + my_quote! {{ #( #fields ),* }} } @@ -229,7 +232,7 @@ fn gen_constructor(fields: &Punctuated) -> quote::Tokens { fn gen_from_clap(struct_name: &Ident, fields: &Punctuated) -> quote::Tokens { let field_block = gen_constructor(fields); - quote! { + my_quote! { fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self { #struct_name #field_block } @@ -241,12 +244,12 @@ fn gen_clap(attrs: &[Attribute]) -> quote::Tokens { let attrs = Attrs::from_struct(attrs, name); let name = attrs.name(); let methods = attrs.methods(); - quote!(::structopt::clap::App::new(#name)#methods) + my_quote!(::structopt::clap::App::new(#name)#methods) } fn gen_clap_struct(struct_attrs: &[Attribute]) -> quote::Tokens { let gen = gen_clap(struct_attrs); - quote! { + my_quote! { fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> { let app = #gen; Self::augment_clap(app) @@ -257,7 +260,7 @@ fn gen_clap_struct(struct_attrs: &[Attribute]) -> quote::Tokens { fn gen_augment_clap(fields: &Punctuated) -> quote::Tokens { let app_var: Ident = "app".into(); let augmentation = gen_augmentation(fields, &app_var); - quote! { + my_quote! { pub fn augment_clap<'a, 'b>(#app_var: ::structopt::clap::App<'a, 'b>) -> ::structopt::clap::App<'a, 'b> { #augmentation } @@ -266,7 +269,7 @@ fn gen_augment_clap(fields: &Punctuated) -> quote::Tokens { fn gen_clap_enum(enum_attrs: &[Attribute]) -> quote::Tokens { let gen = gen_clap(enum_attrs); - quote! { + my_quote! { fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> { let app = #gen .setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp); @@ -284,10 +287,10 @@ fn gen_augment_clap_enum(variants: &Punctuated) -> quote::Tokens let app_var: Ident = "subcommand".into(); let arg_block = match variant.fields { Named(ref fields) => gen_augmentation(&fields.named, &app_var), - Unit => quote!( #app_var ), + Unit => my_quote!( #app_var ), Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { let ty = &unnamed[0]; - quote! { + my_quote! { { let #app_var = #ty::augment_clap(#app_var); if #ty::is_subcommand() { @@ -303,7 +306,7 @@ fn gen_augment_clap_enum(variants: &Punctuated) -> quote::Tokens let name = attrs.name(); let from_attrs = attrs.methods(); - quote! { + my_quote! { .subcommand({ let #app_var = ::structopt::clap::SubCommand::with_name(#name); let #app_var = #arg_block; @@ -312,7 +315,7 @@ fn gen_augment_clap_enum(variants: &Punctuated) -> quote::Tokens } }); - quote! { + my_quote! { pub fn augment_clap<'a, 'b>(app: ::structopt::clap::App<'a, 'b>) -> ::structopt::clap::App<'a, 'b> { app #( #subcommands )* } @@ -320,7 +323,7 @@ fn gen_augment_clap_enum(variants: &Punctuated) -> quote::Tokens } fn gen_from_clap_enum(name: &Ident) -> quote::Tokens { - quote! { + my_quote! { fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self { #name ::from_subcommand(matches.subcommand()) .unwrap() @@ -337,22 +340,22 @@ fn gen_from_subcommand(name: &Ident, variants: &Punctuated) -> q let variant_name = &variant.ident; let constructor_block = match variant.fields { Named(ref fields) => gen_constructor(&fields.named), - Unit => quote!(), + Unit => my_quote!(), Unnamed(ref fields) if fields.unnamed.len() == 1 => { let ty = &fields.unnamed[0]; - quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) + my_quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) } Unnamed(..) => panic!("{}: tuple enum are not supported", variant.ident), }; - quote! { + my_quote! { (#sub_name, Some(matches)) => Some(#name :: #variant_name #constructor_block) } }); - quote! { + my_quote! { pub fn from_subcommand<'a, 'b>(sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>)) -> Option { match sub { #( #match_arms ),*, @@ -371,7 +374,7 @@ fn impl_structopt_for_struct( let augment_clap = gen_augment_clap(fields); let from_clap = gen_from_clap(name, fields); - quote! { + my_quote! { #[allow(unused_variables)] impl ::structopt::StructOpt for #name { #clap @@ -397,7 +400,7 @@ fn impl_structopt_for_enum( let from_clap = gen_from_clap_enum(name); let from_subcommand = gen_from_subcommand(name, variants); - quote! { + my_quote! { impl ::structopt::StructOpt for #name { #clap #from_clap @@ -425,5 +428,5 @@ fn impl_structopt(input: &DeriveInput) -> quote::Tokens { _ => panic!("structopt only supports non-tuple structs and enums") }; - quote!(#inner_impl) + my_quote!(#inner_impl) } diff --git a/structopt-derive/src/macros.rs b/structopt-derive/src/macros.rs new file mode 100644 index 00000000..112f30f3 --- /dev/null +++ b/structopt-derive/src/macros.rs @@ -0,0 +1,11 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! my_quote { + ($($t:tt)*) => (quote_spanned!(::proc_macro2::Span::call_site() => $($t)*)) +}