1 use std
::iter
::FromIterator
;
3 use proc_macro_error
::{abort, ResultExt}
;
7 parse
::{Parse, ParseBuffer, ParseStream}
,
8 punctuated
::Punctuated
,
9 Attribute
, Expr
, ExprLit
, Ident
, Lit
, LitBool
, LitStr
, Token
,
12 pub fn parse_clap_attributes(all_attrs
: &[Attribute
]) -> Vec
<ClapAttr
> {
15 .filter(|attr
| attr
.path
.is_ident("clap") || attr
.path
.is_ident("structopt"))
17 attr
.parse_args_with(Punctuated
::<ClapAttr
, Token
![,]>::parse_terminated
)
23 #[allow(clippy::large_enum_variant)]
26 // single-identifier attributes
36 VerbatimDocComment(Ident
),
37 ExternalSubcommand(Ident
),
42 // ident = "string literal"
43 RenameAllEnv(Ident
, LitStr
),
44 RenameAll(Ident
, LitStr
),
45 NameLitStr(Ident
, LitStr
),
47 // parse(parser_kind [= parser_func])
48 Parse(Ident
, ParserSpec
),
50 // ident [= arbitrary_expr]
51 Skip(Ident
, Option
<Expr
>),
53 // ident = arbitrary_expr
54 NameExpr(Ident
, Expr
),
55 DefaultValueT(Ident
, Option
<Expr
>),
56 DefaultValuesT(Ident
, Expr
),
57 DefaultValueOsT(Ident
, Option
<Expr
>),
58 DefaultValuesOsT(Ident
, Expr
),
59 NextDisplayOrder(Ident
, Expr
),
60 NextHelpHeading(Ident
, Expr
),
61 HelpHeading(Ident
, Expr
),
63 // ident(arbitrary_expr,*)
64 MethodCall(Ident
, Vec
<Expr
>),
67 impl Parse
for ClapAttr
{
68 fn parse(input
: ParseStream
) -> syn
::Result
<Self> {
69 use self::ClapAttr
::*;
71 let name
: Ident
= input
.parse()?
;
72 let name_str
= name
.to_string();
74 if input
.peek(Token
![=]) {
75 // `name = value` attributes.
76 let assign_token
= input
.parse
::<Token
![=]>()?
; // skip '='
78 if input
.peek(LitStr
) {
79 let lit
: LitStr
= input
.parse()?
;
82 "rename_all" => Ok(RenameAll(name
, lit
)),
83 "rename_all_env" => Ok(RenameAllEnv(name
, lit
)),
90 let expr
= Expr
::Lit(expr
);
91 Ok(Skip(name
, Some(expr
)))
94 "next_display_order" => {
99 let expr
= Expr
::Lit(expr
);
100 Ok(NextDisplayOrder(name
, expr
))
103 "next_help_heading" => {
108 let expr
= Expr
::Lit(expr
);
109 Ok(NextHelpHeading(name
, expr
))
116 let expr
= Expr
::Lit(expr
);
117 Ok(HelpHeading(name
, expr
))
120 _
=> Ok(NameLitStr(name
, lit
)),
123 match input
.parse
::<Expr
>() {
124 Ok(expr
) => match &*name_str
{
125 "skip" => Ok(Skip(name
, Some(expr
))),
126 "default_value_t" => Ok(DefaultValueT(name
, Some(expr
))),
127 "default_values_t" => Ok(DefaultValuesT(name
, expr
)),
128 "default_value_os_t" => Ok(DefaultValueOsT(name
, Some(expr
))),
129 "default_values_os_t" => Ok(DefaultValuesOsT(name
, expr
)),
130 "next_display_order" => Ok(NextDisplayOrder(name
, expr
)),
131 "next_help_heading" => Ok(NextHelpHeading(name
, expr
)),
132 "help_heading" => Ok(HelpHeading(name
, expr
)),
133 _
=> Ok(NameExpr(name
, expr
)),
138 "expected `string literal` or `expression` after `=`"
142 } else if input
.peek(syn
::token
::Paren
) {
143 // `name(...)` attributes.
145 parenthesized
!(nested
in input
);
147 match name_str
.as_ref() {
149 let parser_specs
: Punctuated
<ParserSpec
, Token
![,]> =
150 nested
.parse_terminated(ParserSpec
::parse
)?
;
152 if parser_specs
.len() == 1 {
153 Ok(Parse(name
, parser_specs
[0].clone()))
155 abort
!(name
, "parse must have exactly one argument")
159 "raw" => match nested
.parse
::<LitBool
>() {
163 lit
: Lit
::Bool(bool_token
),
165 let expr
= Expr
::Lit(expr
);
166 Ok(MethodCall(name
, vec
![expr
]))
171 "`#[clap(raw(...))` attributes are removed, \
172 they are replaced with raw methods";
173 help
= "if you meant to call `clap::Arg::raw()` method \
174 you should use bool literal, like `raw(true)` or `raw(false)`";
175 note
= raw_method_suggestion(nested
);
181 let method_args
: Punctuated
<_
, Token
![,]> =
182 nested
.parse_terminated(Expr
::parse
)?
;
183 Ok(MethodCall(name
, Vec
::from_iter(method_args
)))
187 // Attributes represented with a sole identifier.
188 match name_str
.as_ref() {
189 "long" => Ok(Long(name
)),
190 "short" => Ok(Short(name
)),
191 "value_parser" => Ok(ValueParser(name
)),
192 "action" => Ok(Action(name
)),
193 "env" => Ok(Env(name
)),
194 "flatten" => Ok(Flatten(name
)),
195 "arg_enum" => Ok(ValueEnum(name
)),
196 "value_enum" => Ok(ValueEnum(name
)),
197 "from_global" => Ok(FromGlobal(name
)),
198 "subcommand" => Ok(Subcommand(name
)),
199 "external_subcommand" => Ok(ExternalSubcommand(name
)),
200 "verbatim_doc_comment" => Ok(VerbatimDocComment(name
)),
204 "`#[clap(default_value)` attribute (without a value) has been replaced by `#[clap(default_value_t)]`.";
205 help
= "Change the attribute to `#[clap(default_value_t)]`";
208 "default_value_t" => Ok(DefaultValueT(name
, None
)),
209 "default_value_os_t" => Ok(DefaultValueOsT(name
, None
)),
210 "about" => (Ok(About(name
))),
211 "author" => (Ok(Author(name
))),
212 "version" => Ok(Version(name
)),
214 "skip" => Ok(Skip(name
, None
)),
216 _
=> abort
!(name
, "unexpected attribute: {}", name_str
),
223 pub struct ParserSpec
{
225 pub eq_token
: Option
<Token
![=]>,
226 pub parse_func
: Option
<Expr
>,
229 impl Parse
for ParserSpec
{
230 fn parse(input
: ParseStream
<'_
>) -> syn
::Result
<Self> {
233 .map_err(|_
| input
.error("parser specification must start with identifier"))?
;
234 let eq_token
= input
.parse()?
;
235 let parse_func
= match eq_token
{
237 Some(_
) => Some(input
.parse()?
),
247 fn raw_method_suggestion(ts
: ParseBuffer
) -> String
{
248 let do_parse
= move || -> Result
<(Ident
, Punctuated
<Expr
, Token
![,]>), syn
::Error
> {
249 let name
= ts
.parse()?
;
250 let _eq
: Token
![=] = ts
.parse()?
;
251 let val
: LitStr
= ts
.parse()?
;
252 let exprs
= val
.parse_with(Punctuated
::<Expr
, Token
![,]>::parse_terminated
)?
;
256 fn to_string
<T
: ToTokens
>(val
: &T
) -> String
{
257 val
.to_token_stream()
263 if let Ok((name
, exprs
)) = do_parse() {
264 let suggestion
= if exprs
.len() == 1 {
265 let val
= to_string(&exprs
[0]);
266 format
!(" = {}", val
)
270 .map(|expr
| to_string(&expr
))
278 "if you need to call `clap::Arg/Command::{}` method you \
279 can do it like this: #[clap({}{})]",
280 name
, name
, suggestion
283 "if you need to call some method from `clap::Arg/Command` \
284 you should use raw method, see \
285 https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#raw-attributes"