Skip to content

Commit

Permalink
Bugfix #324
Browse files Browse the repository at this point in the history
  • Loading branch information
CreepySkeleton committed Feb 4, 2020
1 parent cf11d46 commit d6729bc
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 62 deletions.
79 changes: 36 additions & 43 deletions clap_derive/src/derives/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase};
use proc_macro2::{self, Span, TokenStream};
use proc_macro_error::abort;
use quote::{quote, quote_spanned, ToTokens};
use syn::{self, ext::IdentExt, spanned::Spanned, Ident, LitStr, MetaNameValue, Type};
use syn::{self, ext::IdentExt, spanned::Spanned, Ident, MetaNameValue, Type};

/// Default casing style for generated arguments.
pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
Expand Down Expand Up @@ -78,8 +78,8 @@ pub enum CasingStyle {

#[derive(Clone)]
pub enum Name {
Derived(syn::Ident),
Assigned(syn::LitStr),
Derived(Ident),
Assigned(TokenStream),
}

#[derive(Clone)]
Expand Down Expand Up @@ -220,11 +220,11 @@ impl CasingStyle {
}

impl Name {
pub fn translate(self, style: CasingStyle) -> LitStr {
use self::CasingStyle::*;
pub fn translate(self, style: CasingStyle) -> TokenStream {
use CasingStyle::*;

match self {
Name::Assigned(lit) => lit,
Name::Assigned(tokens) => tokens,
Name::Derived(ident) => {
let s = ident.unraw().to_string();
let s = match style {
Expand All @@ -235,7 +235,7 @@ impl Name {
Snake => s.to_snake_case(),
Verbatim => s,
};
syn::LitStr::new(&s, ident.span())
quote_spanned!(ident.span()=> #s)
}
}
}
Expand Down Expand Up @@ -268,13 +268,13 @@ impl Attrs {
}
}

/// push `.method("str literal")`
fn push_str_method(&mut self, name: Sp<String>, arg: Sp<String>) {
if *name == "name" {
self.name = Name::Assigned(arg.as_lit());
fn push_method(&mut self, name: Ident, arg: impl ToTokens) {
if name == "name" {
self.name = Name::Assigned(quote!(#arg));
} else if name == "version" {
self.version = Some(Method::new(name, quote!(#arg)));
} else {
self.methods
.push(Method::new(name.as_ident(), quote!(#arg)))
self.methods.push(Method::new(name, quote!(#arg)))
}
}

Expand All @@ -284,17 +284,11 @@ impl Attrs {
for attr in parse_clap_attributes(attrs) {
match attr {
Short(ident) | Long(ident) => {
self.push_str_method(
ident.into(),
self.name.clone().translate(*self.casing).into(),
);
self.push_method(ident, self.name.clone().translate(*self.casing));
}

Env(ident) => {
self.push_str_method(
ident.into(),
self.name.clone().translate(*self.env_casing).into(),
);
self.push_method(ident, self.name.clone().translate(*self.env_casing));
}

Subcommand(ident) => {
Expand Down Expand Up @@ -357,16 +351,18 @@ impl Attrs {
}

Version(ident, version) => {
self.version = Some(Method::new(ident, quote!(#version)))
self.push_method(ident, version);
}

NameLitStr(name, lit) => {
self.push_str_method(name.into(), lit.into());
self.push_method(name, lit);
}

NameExpr(name, expr) => self.methods.push(Method::new(name, quote!(#expr))),
NameExpr(name, expr) => {
self.push_method(name, expr);
}

MethodCall(name, args) => self.methods.push(Method::new(name, quote!(#(#args),*))),
MethodCall(name, args) => self.push_method(name, quote!(#(#args),*)),

RenameAll(_, casing_lit) => {
self.casing = CasingStyle::from_lit(casing_lit);
Expand Down Expand Up @@ -584,28 +580,13 @@ impl Attrs {
}

/// generate methods from attributes on top of struct or enum
pub fn top_level_methods(&self) -> proc_macro2::TokenStream {
let version = match (&self.no_version, &self.version) {
(Some(no_version), Some(_)) => abort!(
no_version.span(),
"`no_version` and `version = \"version\"` can't be used together"
),

(None, Some(m)) => m.to_token_stream(),

(None, None) => std::env::var("CARGO_PKG_VERSION")
.map(|version| quote!( .version(#version) ))
.unwrap_or_default(),

(Some(_), None) => quote!(),
};

pub fn top_level_methods(&self) -> TokenStream {
let author = &self.author;
let about = &self.about;
let methods = &self.methods;
let doc_comment = &self.doc_comment;

quote!( #(#doc_comment)* #author #version #about #(#methods)* )
quote!( #(#doc_comment)* #author #about #(#methods)* )
}

/// generate methods on top of a field
Expand All @@ -615,7 +596,19 @@ impl Attrs {
quote!( #(#doc_comment)* #(#methods)* )
}

pub fn cased_name(&self) -> LitStr {
pub fn version(&self) -> TokenStream {
match (&self.no_version, &self.version) {
(None, Some(m)) => m.to_token_stream(),

(None, None) => std::env::var("CARGO_PKG_VERSION")
.map(|version| quote!( .version(#version) ))
.unwrap_or_default(),

_ => quote!(),
}
}

pub fn cased_name(&self) -> TokenStream {
self.name.clone().translate(*self.casing)
}

Expand Down
9 changes: 6 additions & 3 deletions clap_derive/src/derives/clap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,12 @@ fn gen_app_augmentation(
});

let app_methods = parent_attribute.top_level_methods();
let version = parent_attribute.version();
quote! {{
let #app_var = #app_var#app_methods;
#( #args )*
#subcmd
#app_var
#app_var#version
}}
}

Expand Down Expand Up @@ -237,22 +238,24 @@ fn gen_augment_app_for_enum(
let name = attrs.cased_name();
let from_attrs = attrs.top_level_methods();

let version = attrs.version();
quote! {
.subcommand({
let #app_var = ::clap::App::new(#name);
let #app_var = #arg_block;
#app_var#from_attrs
#app_var#from_attrs#version
})
}
});

let app_methods = parent_attribute.top_level_methods();

let version = parent_attribute.version();
quote! {
pub fn augment_app<'b>(
app: ::clap::App<'b>
) -> ::clap::App<'b> {
app #app_methods #( #subcommands )*
app #app_methods #( #subcommands )* #version
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion clap_derive/src/derives/from_argmatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub fn derive_from_argmatches(input: &syn::DeriveInput) -> proc_macro2::TokenStr
let attrs = Attrs::from_struct(
proc_macro2::Span::call_site(),
&input.attrs,
Name::Assigned(syn::LitStr::new(&name, proc_macro2::Span::call_site())),
Name::Assigned(quote!(#name)),
Sp::call_site(DEFAULT_CASING),
Sp::call_site(DEFAULT_ENV_CASING),
);
Expand Down
2 changes: 1 addition & 1 deletion clap_derive/src/derives/into_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub fn gen_app_builder(attrs: &[syn::Attribute]) -> GenOutput {
let attrs = Attrs::from_struct(
proc_macro2::Span::call_site(),
attrs,
Name::Assigned(syn::LitStr::new(&name, proc_macro2::Span::call_site())),
Name::Assigned(quote!(#name)),
Sp::call_site(DEFAULT_CASING),
Sp::call_site(DEFAULT_ENV_CASING),
);
Expand Down
10 changes: 0 additions & 10 deletions clap_derive/src/derives/spanned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,6 @@ impl<T> Sp<T> {
}
}

impl<T: ToString> Sp<T> {
pub fn as_ident(&self) -> Ident {
Ident::new(&self.to_string(), self.span)
}

pub fn as_lit(&self) -> LitStr {
LitStr::new(&self.to_string(), self.span)
}
}

impl<T> Deref for Sp<T> {
type Target = T;

Expand Down
32 changes: 28 additions & 4 deletions clap_derive/tests/issues.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// https://github.com/TeXitoi/structopt/issues/151
// https://github.com/TeXitoi/structopt/issues/289
// https://github.com/TeXitoi/structopt/issues/{NUMBER}

mod utils;
use utils::*;

use clap::{ArgGroup, Clap};

#[test]
fn issue_151() {
use clap::{ArgGroup, Clap};

#[derive(Clap, Debug)]
#[clap(group = ArgGroup::with_name("verb").required(true).multiple(true))]
struct Opt {
Expand Down Expand Up @@ -49,3 +51,25 @@ fn issue_289() {
assert!(Args::try_parse_from(&["test", "some-command", "test"]).is_ok());
assert!(Args::try_parse_from(&["test", "some", "test"]).is_ok());
}

#[test]
fn issue_324() {
fn my_version() -> &'static str {
"MY_VERSION"
}

#[derive(Clap)]
#[clap(version = my_version())]
struct Opt {
#[clap(subcommand)]
_cmd: Option<SubCommand>,
}

#[derive(Clap)]
enum SubCommand {
Start,
}

let help = get_long_help::<Opt>();
assert!(help.contains("MY_VERSION"));
}

0 comments on commit d6729bc

Please sign in to comment.