Update to syn-2 am: f58c27309a am: 63b88a06da am: aa6a8cefab am: f00e21b0de am: 0768caef01

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/argh_derive/+/2520875

Change-Id: I9bc4d79452eb901617a108b301c65fe79043622c
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Cargo.toml b/Cargo.toml
index b7ddfba..fcd6837 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,4 +36,4 @@
 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 @@
     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 @@
         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 @@
         (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 @@
                 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 @@
     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 @@
                 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 @@
     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 @@
         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 @@
 
 // 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 @@
     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 @@
                 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 @@
     }
 
     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 @@
                 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 @@
 ) {
     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 @@
         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_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 {