diff options
author | Matthew Maurer <mmaurer@google.com> | 2023-05-27 02:52:12 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-05-27 02:52:12 +0000 |
commit | 0ef19ecdc9aed55a8e2a1ea67110399dc5e4692b (patch) | |
tree | ab7629d3a6476d778f218e5134befd902245d692 | |
parent | e1b618e52770a00ec37f013d078613384cfbff9b (diff) | |
parent | 101a22614f9753e1ed4830669e6b91057729c015 (diff) | |
download | argh_derive-0ef19ecdc9aed55a8e2a1ea67110399dc5e4692b.tar.gz |
Update to syn-2 am: f58c27309a am: 63b88a06da am: aa6a8cefab am: f00e21b0de am: 0768caef01 am: 101a22614f
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/argh_derive/+/2520875
Change-Id: I5a101a295731dce70da6fe7d08cf6c7126f3cd61
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | patches/syn-2.patch | 383 | ||||
-rw-r--r-- | src/errors.rs | 59 | ||||
-rw-r--r-- | src/parse_attrs.rs | 130 |
4 files changed, 461 insertions, 113 deletions
@@ -36,4 +36,4 @@ version = "1.0" version = "1.0" [dependencies.syn] -version = "1.0" +version = "2.0.1" diff --git a/patches/syn-2.patch b/patches/syn-2.patch new file mode 100644 index 0000000..ffe0488 --- /dev/null +++ b/patches/syn-2.patch @@ -0,0 +1,383 @@ +diff --git a/src/errors.rs b/src/errors.rs +index cfad383..fc5b584 100644 +--- a/src/errors.rs ++++ b/src/errors.rs +@@ -14,15 +14,15 @@ pub struct Errors { + errors: RefCell<Vec<syn::Error>>, + } + +-/// Produce functions to expect particular variants of `syn::Lit` ++/// Produce functions to expect particular literals in `syn::Expr` + macro_rules! expect_lit_fn { + ($(($fn_name:ident, $syn_type:ident, $variant:ident, $lit_name:literal),)*) => { + $( +- pub fn $fn_name<'a>(&self, lit: &'a syn::Lit) -> Option<&'a syn::$syn_type> { +- if let syn::Lit::$variant(inner) = lit { ++ pub fn $fn_name<'a>(&self, e: &'a syn::Expr) -> Option<&'a syn::$syn_type> { ++ if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::$variant(inner), .. }) = e { + Some(inner) + } else { +- self.unexpected_lit($lit_name, lit); ++ self.unexpected_lit($lit_name, e); + None + } + } +@@ -65,28 +65,6 @@ impl Errors { + self.err_span(first, &["First ", attr_kind, " attribute here"].concat()); + } + +- /// Error on literals, expecting attribute syntax. +- pub fn expect_nested_meta<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Meta> { +- match nm { +- syn::NestedMeta::Lit(l) => { +- self.err(l, "Unexpected literal"); +- None +- } +- syn::NestedMeta::Meta(m) => Some(m), +- } +- } +- +- /// Error on attribute syntax, expecting literals +- pub fn expect_nested_lit<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Lit> { +- match nm { +- syn::NestedMeta::Meta(m) => { +- self.err(m, "Expected literal"); +- None +- } +- syn::NestedMeta::Lit(l) => Some(l), +- } +- } +- + expect_lit_fn![ + (expect_lit_str, LitStr, Str, "string"), + (expect_lit_char, LitChar, Char, "character"), +@@ -99,7 +77,7 @@ impl Errors { + (expect_meta_name_value, MetaNameValue, NameValue, "name-value pair"), + ]; + +- fn unexpected_lit(&self, expected: &str, found: &syn::Lit) { ++ fn unexpected_lit(&self, expected: &str, found: &syn::Expr) { + fn lit_kind(lit: &syn::Lit) -> &'static str { + use syn::Lit::{Bool, Byte, ByteStr, Char, Float, Int, Str, Verbatim}; + match lit { +@@ -111,13 +89,21 @@ impl Errors { + Float(_) => "float", + Bool(_) => "boolean", + Verbatim(_) => "unknown (possibly extra-large integer)", ++ _ => "unknown literal kind", + } + } + +- self.err( +- found, +- &["Expected ", expected, " literal, found ", lit_kind(found), " literal"].concat(), +- ) ++ if let syn::Expr::Lit(syn::ExprLit { lit, .. }) = found { ++ self.err( ++ found, ++ &["Expected ", expected, " literal, found ", lit_kind(lit), " literal"].concat(), ++ ) ++ } else { ++ self.err( ++ found, ++ &["Expected ", expected, " literal, found non-literal expression."].concat(), ++ ) ++ } + } + + fn unexpected_meta(&self, expected: &str, found: &syn::Meta) { +@@ -155,6 +141,17 @@ impl Errors { + pub fn push(&self, err: syn::Error) { + self.errors.borrow_mut().push(err); + } ++ ++ /// Convert a `syn::Result` to an `Option`, logging the error if present. ++ pub fn ok<T>(&self, r: syn::Result<T>) -> Option<T> { ++ match r { ++ Ok(v) => Some(v), ++ Err(e) => { ++ self.push(e); ++ None ++ } ++ } ++ } + } + + impl ToTokens for Errors { +diff --git a/src/parse_attrs.rs b/src/parse_attrs.rs +index 04dcbdd..10ebcb5 100644 +--- a/src/parse_attrs.rs ++++ b/src/parse_attrs.rs +@@ -76,49 +76,47 @@ impl FieldAttrs { + continue; + }; + +- for meta in &ml.nested { +- let meta = if let Some(m) = errors.expect_nested_meta(meta) { m } else { continue }; +- ++ for meta in ml { + let name = meta.path(); + if name.is_ident("arg_name") { +- if let Some(m) = errors.expect_meta_name_value(meta) { ++ if let Some(m) = errors.expect_meta_name_value(&meta) { + this.parse_attr_arg_name(errors, m); + } + } else if name.is_ident("default") { +- if let Some(m) = errors.expect_meta_name_value(meta) { ++ if let Some(m) = errors.expect_meta_name_value(&meta) { + this.parse_attr_default(errors, m); + } + } else if name.is_ident("description") { +- if let Some(m) = errors.expect_meta_name_value(meta) { ++ if let Some(m) = errors.expect_meta_name_value(&meta) { + parse_attr_description(errors, m, &mut this.description); + } + } else if name.is_ident("from_str_fn") { +- if let Some(m) = errors.expect_meta_list(meta) { ++ if let Some(m) = errors.expect_meta_list(&meta) { + this.parse_attr_from_str_fn(errors, m); + } + } else if name.is_ident("long") { +- if let Some(m) = errors.expect_meta_name_value(meta) { ++ if let Some(m) = errors.expect_meta_name_value(&meta) { + this.parse_attr_long(errors, m); + } + } else if name.is_ident("option") { +- parse_attr_field_type(errors, meta, FieldKind::Option, &mut this.field_type); ++ parse_attr_field_type(errors, &meta, FieldKind::Option, &mut this.field_type); + } else if name.is_ident("short") { +- if let Some(m) = errors.expect_meta_name_value(meta) { ++ if let Some(m) = errors.expect_meta_name_value(&meta) { + this.parse_attr_short(errors, m); + } + } else if name.is_ident("subcommand") { + parse_attr_field_type( + errors, +- meta, ++ &meta, + FieldKind::SubCommand, + &mut this.field_type, + ); + } else if name.is_ident("switch") { +- parse_attr_field_type(errors, meta, FieldKind::Switch, &mut this.field_type); ++ parse_attr_field_type(errors, &meta, FieldKind::Switch, &mut this.field_type); + } else if name.is_ident("positional") { + parse_attr_field_type( + errors, +- meta, ++ &meta, + FieldKind::Positional, + &mut this.field_type, + ); +@@ -189,7 +187,7 @@ impl FieldAttrs { + fn parse_attr_short(&mut self, errors: &Errors, m: &syn::MetaNameValue) { + if let Some(first) = &self.short { + errors.duplicate_attrs("short", first, m); +- } else if let Some(lit_char) = errors.expect_lit_char(&m.lit) { ++ } else if let Some(lit_char) = errors.expect_lit_char(&m.value) { + self.short = Some(lit_char.clone()); + if !lit_char.value().is_ascii() { + errors.err(lit_char, "Short names must be ASCII"); +@@ -217,16 +215,7 @@ fn parse_attr_fn_name( + errors.duplicate_attrs(attr_name, first, m); + } + +- if m.nested.len() != 1 { +- errors.err(&m.nested, "Expected a single argument containing the function name"); +- return; +- } +- +- // `unwrap` will not fail because of the call above +- let nested = m.nested.first().unwrap(); +- if let Some(path) = errors.expect_nested_meta(nested).and_then(|m| errors.expect_meta_word(m)) { +- *slot = path.get_ident().cloned(); +- } ++ *slot = errors.ok(m.parse_args()); + } + + fn parse_attr_field_type( +@@ -246,7 +235,7 @@ fn parse_attr_field_type( + + // Whether the attribute is one like `#[<name> ...]` + fn is_matching_attr(name: &str, attr: &syn::Attribute) -> bool { +- attr.path.segments.len() == 1 && attr.path.segments[0].ident == name ++ attr.path().segments.len() == 1 && attr.path().segments[0].ident == name + } + + /// Checks for `#[doc ...]`, which is generated by doc comments. +@@ -259,34 +248,18 @@ fn is_argh_attr(attr: &syn::Attribute) -> bool { + is_matching_attr("argh", attr) + } + +-fn attr_to_meta_subtype<R: Clone>( ++/// Filters out non-`#[argh(...)]` attributes and converts to a sequence of `syn::Meta`. ++fn argh_attr_to_meta_list( + errors: &Errors, + attr: &syn::Attribute, +- f: impl FnOnce(&syn::Meta) -> Option<&R>, +-) -> Option<R> { +- match attr.parse_meta() { +- Ok(meta) => f(&meta).cloned(), +- Err(e) => { +- errors.push(e); +- None +- } +- } +-} +- +-fn attr_to_meta_list(errors: &Errors, attr: &syn::Attribute) -> Option<syn::MetaList> { +- attr_to_meta_subtype(errors, attr, |m| errors.expect_meta_list(m)) +-} +- +-fn attr_to_meta_name_value(errors: &Errors, attr: &syn::Attribute) -> Option<syn::MetaNameValue> { +- attr_to_meta_subtype(errors, attr, |m| errors.expect_meta_name_value(m)) +-} +- +-/// Filters out non-`#[argh(...)]` attributes and converts to `syn::MetaList`. +-fn argh_attr_to_meta_list(errors: &Errors, attr: &syn::Attribute) -> Option<syn::MetaList> { ++) -> Option<impl IntoIterator<Item = syn::Meta>> { + if !is_argh_attr(attr) { + return None; + } +- attr_to_meta_list(errors, attr) ++ let ml = errors.expect_meta_list(&attr.meta)?; ++ errors.ok(ml.parse_args_with( ++ syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated, ++ )) + } + + /// Represents a `#[derive(FromArgs)]` type's top-level attributes. +@@ -317,32 +290,31 @@ impl TypeAttrs { + continue; + }; + +- for meta in &ml.nested { +- let meta = if let Some(m) = errors.expect_nested_meta(meta) { m } else { continue }; +- ++ for meta in ml { + let name = meta.path(); + if name.is_ident("description") { +- if let Some(m) = errors.expect_meta_name_value(meta) { ++ if let Some(m) = errors.expect_meta_name_value(&meta) { + parse_attr_description(errors, m, &mut this.description); + } + } else if name.is_ident("error_code") { +- if let Some(m) = errors.expect_meta_list(meta) { ++ if let Some(m) = errors.expect_meta_list(&meta) { + this.parse_attr_error_code(errors, m); + } + } else if name.is_ident("example") { +- if let Some(m) = errors.expect_meta_name_value(meta) { ++ if let Some(m) = errors.expect_meta_name_value(&meta) { + this.parse_attr_example(errors, m); + } + } else if name.is_ident("name") { +- if let Some(m) = errors.expect_meta_name_value(meta) { ++ if let Some(m) = errors.expect_meta_name_value(&meta) { + this.parse_attr_name(errors, m); + } + } else if name.is_ident("note") { +- if let Some(m) = errors.expect_meta_name_value(meta) { ++ if let Some(m) = errors.expect_meta_name_value(&meta) { + this.parse_attr_note(errors, m); + } + } else if name.is_ident("subcommand") { +- if let Some(ident) = errors.expect_meta_word(meta).and_then(|p| p.get_ident()) { ++ if let Some(ident) = errors.expect_meta_word(&meta).and_then(|p| p.get_ident()) ++ { + this.parse_attr_subcommand(errors, ident); + } + } else { +@@ -396,20 +368,17 @@ impl TypeAttrs { + } + + fn parse_attr_error_code(&mut self, errors: &Errors, ml: &syn::MetaList) { +- if ml.nested.len() != 2 { +- errors.err(&ml, "Expected two arguments, an error number and a string description"); +- return; +- } +- +- let err_code = &ml.nested[0]; +- let err_msg = &ml.nested[1]; +- +- let err_code = errors.expect_nested_lit(err_code).and_then(|l| errors.expect_lit_int(l)); +- let err_msg = errors.expect_nested_lit(err_msg).and_then(|l| errors.expect_lit_str(l)); +- +- if let (Some(err_code), Some(err_msg)) = (err_code, err_msg) { +- self.error_codes.push((err_code.clone(), err_msg.clone())); +- } ++ errors.ok(ml.parse_args_with(|input: syn::parse::ParseStream| { ++ let err_code = input.parse()?; ++ input.parse::<syn::Token![,]>()?; ++ let err_msg = input.parse()?; ++ if let (Some(err_code), Some(err_msg)) = ++ (errors.expect_lit_int(&err_code), errors.expect_lit_str(&err_msg)) ++ { ++ self.error_codes.push((err_code.clone(), err_msg.clone())); ++ } ++ Ok(()) ++ })); + } + + fn parse_attr_example(&mut self, errors: &Errors, m: &syn::MetaNameValue) { +@@ -470,15 +439,13 @@ impl VariantAttrs { + continue; + }; + +- for meta in &ml.nested { +- let meta = if let Some(m) = errors.expect_nested_meta(meta) { m } else { continue }; +- ++ for meta in ml { + let name = meta.path(); + if name.is_ident("dynamic") { + if let Some(prev) = this.is_dynamic.as_ref() { +- errors.duplicate_attrs("dynamic", prev, meta); ++ errors.duplicate_attrs("dynamic", prev, &meta); + } else { +- this.is_dynamic = errors.expect_meta_word(meta).cloned(); ++ this.is_dynamic = errors.expect_meta_word(&meta).cloned(); + } + } else { + errors.err( +@@ -515,19 +482,19 @@ fn parse_attr_single_string( + ) { + if let Some(first) = slot { + errors.duplicate_attrs(name, first, m); +- } else if let Some(lit_str) = errors.expect_lit_str(&m.lit) { ++ } else if let Some(lit_str) = errors.expect_lit_str(&m.value) { + *slot = Some(lit_str.clone()); + } + } + + fn parse_attr_multi_string(errors: &Errors, m: &syn::MetaNameValue, list: &mut Vec<syn::LitStr>) { +- if let Some(lit_str) = errors.expect_lit_str(&m.lit) { ++ if let Some(lit_str) = errors.expect_lit_str(&m.value) { + list.push(lit_str.clone()); + } + } + + fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option<Description>) { +- let nv = if let Some(nv) = attr_to_meta_name_value(errors, attr) { ++ let nv = if let Some(nv) = errors.expect_meta_name_value(&attr.meta) { + nv + } else { + return; +@@ -538,7 +505,7 @@ fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option<Desc + return; + } + +- if let Some(lit_str) = errors.expect_lit_str(&nv.lit) { ++ if let Some(lit_str) = errors.expect_lit_str(&nv.value) { + let lit_str = if let Some(previous) = slot { + let previous = &previous.content; + let previous_span = previous.span(); +@@ -551,7 +518,8 @@ fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option<Desc + } + + fn parse_attr_description(errors: &Errors, m: &syn::MetaNameValue, slot: &mut Option<Description>) { +- let lit_str = if let Some(lit_str) = errors.expect_lit_str(&m.lit) { lit_str } else { return }; ++ let lit_str = ++ if let Some(lit_str) = errors.expect_lit_str(&m.value) { lit_str } else { return }; + + // Don't allow multiple explicit (non doc-comment) descriptions + if let Some(description) = slot { diff --git a/src/errors.rs b/src/errors.rs index cfad383..fc5b584 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -14,15 +14,15 @@ pub struct Errors { errors: RefCell<Vec<syn::Error>>, } -/// Produce functions to expect particular variants of `syn::Lit` +/// Produce functions to expect particular literals in `syn::Expr` macro_rules! expect_lit_fn { ($(($fn_name:ident, $syn_type:ident, $variant:ident, $lit_name:literal),)*) => { $( - pub fn $fn_name<'a>(&self, lit: &'a syn::Lit) -> Option<&'a syn::$syn_type> { - if let syn::Lit::$variant(inner) = lit { + pub fn $fn_name<'a>(&self, e: &'a syn::Expr) -> Option<&'a syn::$syn_type> { + if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::$variant(inner), .. }) = e { Some(inner) } else { - self.unexpected_lit($lit_name, lit); + self.unexpected_lit($lit_name, e); None } } @@ -65,28 +65,6 @@ impl Errors { self.err_span(first, &["First ", attr_kind, " attribute here"].concat()); } - /// Error on literals, expecting attribute syntax. - pub fn expect_nested_meta<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Meta> { - match nm { - syn::NestedMeta::Lit(l) => { - self.err(l, "Unexpected literal"); - None - } - syn::NestedMeta::Meta(m) => Some(m), - } - } - - /// Error on attribute syntax, expecting literals - pub fn expect_nested_lit<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Lit> { - match nm { - syn::NestedMeta::Meta(m) => { - self.err(m, "Expected literal"); - None - } - syn::NestedMeta::Lit(l) => Some(l), - } - } - expect_lit_fn![ (expect_lit_str, LitStr, Str, "string"), (expect_lit_char, LitChar, Char, "character"), @@ -99,7 +77,7 @@ impl Errors { (expect_meta_name_value, MetaNameValue, NameValue, "name-value pair"), ]; - fn unexpected_lit(&self, expected: &str, found: &syn::Lit) { + fn unexpected_lit(&self, expected: &str, found: &syn::Expr) { fn lit_kind(lit: &syn::Lit) -> &'static str { use syn::Lit::{Bool, Byte, ByteStr, Char, Float, Int, Str, Verbatim}; match lit { @@ -111,13 +89,21 @@ impl Errors { Float(_) => "float", Bool(_) => "boolean", Verbatim(_) => "unknown (possibly extra-large integer)", + _ => "unknown literal kind", } } - self.err( - found, - &["Expected ", expected, " literal, found ", lit_kind(found), " literal"].concat(), - ) + if let syn::Expr::Lit(syn::ExprLit { lit, .. }) = found { + self.err( + found, + &["Expected ", expected, " literal, found ", lit_kind(lit), " literal"].concat(), + ) + } else { + self.err( + found, + &["Expected ", expected, " literal, found non-literal expression."].concat(), + ) + } } fn unexpected_meta(&self, expected: &str, found: &syn::Meta) { @@ -155,6 +141,17 @@ impl Errors { pub fn push(&self, err: syn::Error) { self.errors.borrow_mut().push(err); } + + /// Convert a `syn::Result` to an `Option`, logging the error if present. + pub fn ok<T>(&self, r: syn::Result<T>) -> Option<T> { + match r { + Ok(v) => Some(v), + Err(e) => { + self.push(e); + None + } + } + } } impl ToTokens for Errors { diff --git a/src/parse_attrs.rs b/src/parse_attrs.rs index 04dcbdd..10ebcb5 100644 --- a/src/parse_attrs.rs +++ b/src/parse_attrs.rs @@ -76,49 +76,47 @@ impl FieldAttrs { continue; }; - for meta in &ml.nested { - let meta = if let Some(m) = errors.expect_nested_meta(meta) { m } else { continue }; - + for meta in ml { let name = meta.path(); if name.is_ident("arg_name") { - if let Some(m) = errors.expect_meta_name_value(meta) { + if let Some(m) = errors.expect_meta_name_value(&meta) { this.parse_attr_arg_name(errors, m); } } else if name.is_ident("default") { - if let Some(m) = errors.expect_meta_name_value(meta) { + if let Some(m) = errors.expect_meta_name_value(&meta) { this.parse_attr_default(errors, m); } } else if name.is_ident("description") { - if let Some(m) = errors.expect_meta_name_value(meta) { + if let Some(m) = errors.expect_meta_name_value(&meta) { parse_attr_description(errors, m, &mut this.description); } } else if name.is_ident("from_str_fn") { - if let Some(m) = errors.expect_meta_list(meta) { + if let Some(m) = errors.expect_meta_list(&meta) { this.parse_attr_from_str_fn(errors, m); } } else if name.is_ident("long") { - if let Some(m) = errors.expect_meta_name_value(meta) { + if let Some(m) = errors.expect_meta_name_value(&meta) { this.parse_attr_long(errors, m); } } else if name.is_ident("option") { - parse_attr_field_type(errors, meta, FieldKind::Option, &mut this.field_type); + parse_attr_field_type(errors, &meta, FieldKind::Option, &mut this.field_type); } else if name.is_ident("short") { - if let Some(m) = errors.expect_meta_name_value(meta) { + if let Some(m) = errors.expect_meta_name_value(&meta) { this.parse_attr_short(errors, m); } } else if name.is_ident("subcommand") { parse_attr_field_type( errors, - meta, + &meta, FieldKind::SubCommand, &mut this.field_type, ); } else if name.is_ident("switch") { - parse_attr_field_type(errors, meta, FieldKind::Switch, &mut this.field_type); + parse_attr_field_type(errors, &meta, FieldKind::Switch, &mut this.field_type); } else if name.is_ident("positional") { parse_attr_field_type( errors, - meta, + &meta, FieldKind::Positional, &mut this.field_type, ); @@ -189,7 +187,7 @@ impl FieldAttrs { fn parse_attr_short(&mut self, errors: &Errors, m: &syn::MetaNameValue) { if let Some(first) = &self.short { errors.duplicate_attrs("short", first, m); - } else if let Some(lit_char) = errors.expect_lit_char(&m.lit) { + } else if let Some(lit_char) = errors.expect_lit_char(&m.value) { self.short = Some(lit_char.clone()); if !lit_char.value().is_ascii() { errors.err(lit_char, "Short names must be ASCII"); @@ -217,16 +215,7 @@ fn parse_attr_fn_name( errors.duplicate_attrs(attr_name, first, m); } - if m.nested.len() != 1 { - errors.err(&m.nested, "Expected a single argument containing the function name"); - return; - } - - // `unwrap` will not fail because of the call above - let nested = m.nested.first().unwrap(); - if let Some(path) = errors.expect_nested_meta(nested).and_then(|m| errors.expect_meta_word(m)) { - *slot = path.get_ident().cloned(); - } + *slot = errors.ok(m.parse_args()); } fn parse_attr_field_type( @@ -246,7 +235,7 @@ fn parse_attr_field_type( // Whether the attribute is one like `#[<name> ...]` fn is_matching_attr(name: &str, attr: &syn::Attribute) -> bool { - attr.path.segments.len() == 1 && attr.path.segments[0].ident == name + attr.path().segments.len() == 1 && attr.path().segments[0].ident == name } /// Checks for `#[doc ...]`, which is generated by doc comments. @@ -259,34 +248,18 @@ fn is_argh_attr(attr: &syn::Attribute) -> bool { is_matching_attr("argh", attr) } -fn attr_to_meta_subtype<R: Clone>( +/// Filters out non-`#[argh(...)]` attributes and converts to a sequence of `syn::Meta`. +fn argh_attr_to_meta_list( errors: &Errors, attr: &syn::Attribute, - f: impl FnOnce(&syn::Meta) -> Option<&R>, -) -> Option<R> { - match attr.parse_meta() { - Ok(meta) => f(&meta).cloned(), - Err(e) => { - errors.push(e); - None - } - } -} - -fn attr_to_meta_list(errors: &Errors, attr: &syn::Attribute) -> Option<syn::MetaList> { - attr_to_meta_subtype(errors, attr, |m| errors.expect_meta_list(m)) -} - -fn attr_to_meta_name_value(errors: &Errors, attr: &syn::Attribute) -> Option<syn::MetaNameValue> { - attr_to_meta_subtype(errors, attr, |m| errors.expect_meta_name_value(m)) -} - -/// Filters out non-`#[argh(...)]` attributes and converts to `syn::MetaList`. -fn argh_attr_to_meta_list(errors: &Errors, attr: &syn::Attribute) -> Option<syn::MetaList> { +) -> Option<impl IntoIterator<Item = syn::Meta>> { if !is_argh_attr(attr) { return None; } - attr_to_meta_list(errors, attr) + let ml = errors.expect_meta_list(&attr.meta)?; + errors.ok(ml.parse_args_with( + syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated, + )) } /// Represents a `#[derive(FromArgs)]` type's top-level attributes. @@ -317,32 +290,31 @@ impl TypeAttrs { continue; }; - for meta in &ml.nested { - let meta = if let Some(m) = errors.expect_nested_meta(meta) { m } else { continue }; - + for meta in ml { let name = meta.path(); if name.is_ident("description") { - if let Some(m) = errors.expect_meta_name_value(meta) { + if let Some(m) = errors.expect_meta_name_value(&meta) { parse_attr_description(errors, m, &mut this.description); } } else if name.is_ident("error_code") { - if let Some(m) = errors.expect_meta_list(meta) { + if let Some(m) = errors.expect_meta_list(&meta) { this.parse_attr_error_code(errors, m); } } else if name.is_ident("example") { - if let Some(m) = errors.expect_meta_name_value(meta) { + if let Some(m) = errors.expect_meta_name_value(&meta) { this.parse_attr_example(errors, m); } } else if name.is_ident("name") { - if let Some(m) = errors.expect_meta_name_value(meta) { + if let Some(m) = errors.expect_meta_name_value(&meta) { this.parse_attr_name(errors, m); } } else if name.is_ident("note") { - if let Some(m) = errors.expect_meta_name_value(meta) { + if let Some(m) = errors.expect_meta_name_value(&meta) { this.parse_attr_note(errors, m); } } else if name.is_ident("subcommand") { - if let Some(ident) = errors.expect_meta_word(meta).and_then(|p| p.get_ident()) { + if let Some(ident) = errors.expect_meta_word(&meta).and_then(|p| p.get_ident()) + { this.parse_attr_subcommand(errors, ident); } } else { @@ -396,20 +368,17 @@ impl TypeAttrs { } fn parse_attr_error_code(&mut self, errors: &Errors, ml: &syn::MetaList) { - if ml.nested.len() != 2 { - errors.err(&ml, "Expected two arguments, an error number and a string description"); - return; - } - - let err_code = &ml.nested[0]; - let err_msg = &ml.nested[1]; - - let err_code = errors.expect_nested_lit(err_code).and_then(|l| errors.expect_lit_int(l)); - let err_msg = errors.expect_nested_lit(err_msg).and_then(|l| errors.expect_lit_str(l)); - - if let (Some(err_code), Some(err_msg)) = (err_code, err_msg) { - self.error_codes.push((err_code.clone(), err_msg.clone())); - } + errors.ok(ml.parse_args_with(|input: syn::parse::ParseStream| { + let err_code = input.parse()?; + input.parse::<syn::Token![,]>()?; + let err_msg = input.parse()?; + if let (Some(err_code), Some(err_msg)) = + (errors.expect_lit_int(&err_code), errors.expect_lit_str(&err_msg)) + { + self.error_codes.push((err_code.clone(), err_msg.clone())); + } + Ok(()) + })); } fn parse_attr_example(&mut self, errors: &Errors, m: &syn::MetaNameValue) { @@ -470,15 +439,13 @@ impl VariantAttrs { continue; }; - for meta in &ml.nested { - let meta = if let Some(m) = errors.expect_nested_meta(meta) { m } else { continue }; - + for meta in ml { let name = meta.path(); if name.is_ident("dynamic") { if let Some(prev) = this.is_dynamic.as_ref() { - errors.duplicate_attrs("dynamic", prev, meta); + errors.duplicate_attrs("dynamic", prev, &meta); } else { - this.is_dynamic = errors.expect_meta_word(meta).cloned(); + this.is_dynamic = errors.expect_meta_word(&meta).cloned(); } } else { errors.err( @@ -515,19 +482,19 @@ fn parse_attr_single_string( ) { if let Some(first) = slot { errors.duplicate_attrs(name, first, m); - } else if let Some(lit_str) = errors.expect_lit_str(&m.lit) { + } else if let Some(lit_str) = errors.expect_lit_str(&m.value) { *slot = Some(lit_str.clone()); } } fn parse_attr_multi_string(errors: &Errors, m: &syn::MetaNameValue, list: &mut Vec<syn::LitStr>) { - if let Some(lit_str) = errors.expect_lit_str(&m.lit) { + if let Some(lit_str) = errors.expect_lit_str(&m.value) { list.push(lit_str.clone()); } } fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option<Description>) { - let nv = if let Some(nv) = attr_to_meta_name_value(errors, attr) { + let nv = if let Some(nv) = errors.expect_meta_name_value(&attr.meta) { nv } else { return; @@ -538,7 +505,7 @@ fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option<Desc return; } - if let Some(lit_str) = errors.expect_lit_str(&nv.lit) { + if let Some(lit_str) = errors.expect_lit_str(&nv.value) { let lit_str = if let Some(previous) = slot { let previous = &previous.content; let previous_span = previous.span(); @@ -551,7 +518,8 @@ fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option<Desc } fn parse_attr_description(errors: &Errors, m: &syn::MetaNameValue, slot: &mut Option<Description>) { - let lit_str = if let Some(lit_str) = errors.expect_lit_str(&m.lit) { lit_str } else { return }; + let lit_str = + if let Some(lit_str) = errors.expect_lit_str(&m.value) { lit_str } else { return }; // Don't allow multiple explicit (non doc-comment) descriptions if let Some(description) = slot { |