133d722a9Sopenharmony_ciuse crate::syntax::cfg::CfgExpr;
233d722a9Sopenharmony_ciuse crate::syntax::namespace::Namespace;
333d722a9Sopenharmony_ciuse crate::syntax::report::Errors;
433d722a9Sopenharmony_ciuse crate::syntax::Atom::{self, *};
533d722a9Sopenharmony_ciuse crate::syntax::{cfg, Derive, Doc, ForeignName};
633d722a9Sopenharmony_ciuse proc_macro2::{Ident, TokenStream};
733d722a9Sopenharmony_ciuse quote::ToTokens;
833d722a9Sopenharmony_ciuse syn::parse::ParseStream;
933d722a9Sopenharmony_ciuse syn::{Attribute, Error, Expr, Lit, LitStr, Meta, Path, Result, Token};
1033d722a9Sopenharmony_ci
1133d722a9Sopenharmony_ci// Intended usage:
1233d722a9Sopenharmony_ci//
1333d722a9Sopenharmony_ci//     let mut doc = Doc::new();
1433d722a9Sopenharmony_ci//     let mut cxx_name = None;
1533d722a9Sopenharmony_ci//     let mut rust_name = None;
1633d722a9Sopenharmony_ci//     /* ... */
1733d722a9Sopenharmony_ci//     let attrs = attrs::parse(
1833d722a9Sopenharmony_ci//         cx,
1933d722a9Sopenharmony_ci//         item.attrs,
2033d722a9Sopenharmony_ci//         attrs::Parser {
2133d722a9Sopenharmony_ci//             doc: Some(&mut doc),
2233d722a9Sopenharmony_ci//             cxx_name: Some(&mut cxx_name),
2333d722a9Sopenharmony_ci//             rust_name: Some(&mut rust_name),
2433d722a9Sopenharmony_ci//             /* ... */
2533d722a9Sopenharmony_ci//             ..Default::default()
2633d722a9Sopenharmony_ci//         },
2733d722a9Sopenharmony_ci//     );
2833d722a9Sopenharmony_ci//
2933d722a9Sopenharmony_ci#[derive(Default)]
3033d722a9Sopenharmony_cipub struct Parser<'a> {
3133d722a9Sopenharmony_ci    pub cfg: Option<&'a mut CfgExpr>,
3233d722a9Sopenharmony_ci    pub doc: Option<&'a mut Doc>,
3333d722a9Sopenharmony_ci    pub derives: Option<&'a mut Vec<Derive>>,
3433d722a9Sopenharmony_ci    pub repr: Option<&'a mut Option<Atom>>,
3533d722a9Sopenharmony_ci    pub namespace: Option<&'a mut Namespace>,
3633d722a9Sopenharmony_ci    pub cxx_name: Option<&'a mut Option<ForeignName>>,
3733d722a9Sopenharmony_ci    pub rust_name: Option<&'a mut Option<Ident>>,
3833d722a9Sopenharmony_ci    pub variants_from_header: Option<&'a mut Option<Attribute>>,
3933d722a9Sopenharmony_ci    pub ignore_unrecognized: bool,
4033d722a9Sopenharmony_ci
4133d722a9Sopenharmony_ci    // Suppress clippy needless_update lint ("struct update has no effect, all
4233d722a9Sopenharmony_ci    // the fields in the struct have already been specified") when preemptively
4333d722a9Sopenharmony_ci    // writing `..Default::default()`.
4433d722a9Sopenharmony_ci    pub(crate) _more: (),
4533d722a9Sopenharmony_ci}
4633d722a9Sopenharmony_ci
4733d722a9Sopenharmony_cipub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
4833d722a9Sopenharmony_ci    let mut passthrough_attrs = Vec::new();
4933d722a9Sopenharmony_ci    for attr in attrs {
5033d722a9Sopenharmony_ci        let attr_path = attr.path();
5133d722a9Sopenharmony_ci        if attr_path.is_ident("doc") {
5233d722a9Sopenharmony_ci            match parse_doc_attribute(&attr.meta) {
5333d722a9Sopenharmony_ci                Ok(attr) => {
5433d722a9Sopenharmony_ci                    if let Some(doc) = &mut parser.doc {
5533d722a9Sopenharmony_ci                        match attr {
5633d722a9Sopenharmony_ci                            DocAttribute::Doc(lit) => doc.push(lit),
5733d722a9Sopenharmony_ci                            DocAttribute::Hidden => doc.hidden = true,
5833d722a9Sopenharmony_ci                        }
5933d722a9Sopenharmony_ci                        continue;
6033d722a9Sopenharmony_ci                    }
6133d722a9Sopenharmony_ci                }
6233d722a9Sopenharmony_ci                Err(err) => {
6333d722a9Sopenharmony_ci                    cx.push(err);
6433d722a9Sopenharmony_ci                    break;
6533d722a9Sopenharmony_ci                }
6633d722a9Sopenharmony_ci            }
6733d722a9Sopenharmony_ci        } else if attr_path.is_ident("derive") {
6833d722a9Sopenharmony_ci            match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
6933d722a9Sopenharmony_ci                Ok(attr) => {
7033d722a9Sopenharmony_ci                    if let Some(derives) = &mut parser.derives {
7133d722a9Sopenharmony_ci                        derives.extend(attr);
7233d722a9Sopenharmony_ci                        continue;
7333d722a9Sopenharmony_ci                    }
7433d722a9Sopenharmony_ci                }
7533d722a9Sopenharmony_ci                Err(err) => {
7633d722a9Sopenharmony_ci                    cx.push(err);
7733d722a9Sopenharmony_ci                    break;
7833d722a9Sopenharmony_ci                }
7933d722a9Sopenharmony_ci            }
8033d722a9Sopenharmony_ci        } else if attr_path.is_ident("repr") {
8133d722a9Sopenharmony_ci            match attr.parse_args_with(parse_repr_attribute) {
8233d722a9Sopenharmony_ci                Ok(attr) => {
8333d722a9Sopenharmony_ci                    if let Some(repr) = &mut parser.repr {
8433d722a9Sopenharmony_ci                        **repr = Some(attr);
8533d722a9Sopenharmony_ci                        continue;
8633d722a9Sopenharmony_ci                    }
8733d722a9Sopenharmony_ci                }
8833d722a9Sopenharmony_ci                Err(err) => {
8933d722a9Sopenharmony_ci                    cx.push(err);
9033d722a9Sopenharmony_ci                    break;
9133d722a9Sopenharmony_ci                }
9233d722a9Sopenharmony_ci            }
9333d722a9Sopenharmony_ci        } else if attr_path.is_ident("namespace") {
9433d722a9Sopenharmony_ci            match Namespace::parse_meta(&attr.meta) {
9533d722a9Sopenharmony_ci                Ok(attr) => {
9633d722a9Sopenharmony_ci                    if let Some(namespace) = &mut parser.namespace {
9733d722a9Sopenharmony_ci                        **namespace = attr;
9833d722a9Sopenharmony_ci                        continue;
9933d722a9Sopenharmony_ci                    }
10033d722a9Sopenharmony_ci                }
10133d722a9Sopenharmony_ci                Err(err) => {
10233d722a9Sopenharmony_ci                    cx.push(err);
10333d722a9Sopenharmony_ci                    break;
10433d722a9Sopenharmony_ci                }
10533d722a9Sopenharmony_ci            }
10633d722a9Sopenharmony_ci        } else if attr_path.is_ident("cxx_name") {
10733d722a9Sopenharmony_ci            match parse_cxx_name_attribute(&attr.meta) {
10833d722a9Sopenharmony_ci                Ok(attr) => {
10933d722a9Sopenharmony_ci                    if let Some(cxx_name) = &mut parser.cxx_name {
11033d722a9Sopenharmony_ci                        **cxx_name = Some(attr);
11133d722a9Sopenharmony_ci                        continue;
11233d722a9Sopenharmony_ci                    }
11333d722a9Sopenharmony_ci                }
11433d722a9Sopenharmony_ci                Err(err) => {
11533d722a9Sopenharmony_ci                    cx.push(err);
11633d722a9Sopenharmony_ci                    break;
11733d722a9Sopenharmony_ci                }
11833d722a9Sopenharmony_ci            }
11933d722a9Sopenharmony_ci        } else if attr_path.is_ident("rust_name") {
12033d722a9Sopenharmony_ci            match parse_rust_name_attribute(&attr.meta) {
12133d722a9Sopenharmony_ci                Ok(attr) => {
12233d722a9Sopenharmony_ci                    if let Some(rust_name) = &mut parser.rust_name {
12333d722a9Sopenharmony_ci                        **rust_name = Some(attr);
12433d722a9Sopenharmony_ci                        continue;
12533d722a9Sopenharmony_ci                    }
12633d722a9Sopenharmony_ci                }
12733d722a9Sopenharmony_ci                Err(err) => {
12833d722a9Sopenharmony_ci                    cx.push(err);
12933d722a9Sopenharmony_ci                    break;
13033d722a9Sopenharmony_ci                }
13133d722a9Sopenharmony_ci            }
13233d722a9Sopenharmony_ci        } else if attr_path.is_ident("cfg") {
13333d722a9Sopenharmony_ci            match cfg::parse_attribute(&attr) {
13433d722a9Sopenharmony_ci                Ok(cfg_expr) => {
13533d722a9Sopenharmony_ci                    if let Some(cfg) = &mut parser.cfg {
13633d722a9Sopenharmony_ci                        cfg.merge(cfg_expr);
13733d722a9Sopenharmony_ci                        passthrough_attrs.push(attr);
13833d722a9Sopenharmony_ci                        continue;
13933d722a9Sopenharmony_ci                    }
14033d722a9Sopenharmony_ci                }
14133d722a9Sopenharmony_ci                Err(err) => {
14233d722a9Sopenharmony_ci                    cx.push(err);
14333d722a9Sopenharmony_ci                    break;
14433d722a9Sopenharmony_ci                }
14533d722a9Sopenharmony_ci            }
14633d722a9Sopenharmony_ci        } else if attr_path.is_ident("variants_from_header")
14733d722a9Sopenharmony_ci            && cfg!(feature = "experimental-enum-variants-from-header")
14833d722a9Sopenharmony_ci        {
14933d722a9Sopenharmony_ci            if let Err(err) = attr.meta.require_path_only() {
15033d722a9Sopenharmony_ci                cx.push(err);
15133d722a9Sopenharmony_ci            }
15233d722a9Sopenharmony_ci            if let Some(variants_from_header) = &mut parser.variants_from_header {
15333d722a9Sopenharmony_ci                **variants_from_header = Some(attr);
15433d722a9Sopenharmony_ci                continue;
15533d722a9Sopenharmony_ci            }
15633d722a9Sopenharmony_ci        } else if attr_path.is_ident("allow")
15733d722a9Sopenharmony_ci            || attr_path.is_ident("warn")
15833d722a9Sopenharmony_ci            || attr_path.is_ident("deny")
15933d722a9Sopenharmony_ci            || attr_path.is_ident("forbid")
16033d722a9Sopenharmony_ci            || attr_path.is_ident("deprecated")
16133d722a9Sopenharmony_ci            || attr_path.is_ident("must_use")
16233d722a9Sopenharmony_ci        {
16333d722a9Sopenharmony_ci            // https://doc.rust-lang.org/reference/attributes/diagnostics.html
16433d722a9Sopenharmony_ci            passthrough_attrs.push(attr);
16533d722a9Sopenharmony_ci            continue;
16633d722a9Sopenharmony_ci        } else if attr_path.is_ident("serde") {
16733d722a9Sopenharmony_ci            passthrough_attrs.push(attr);
16833d722a9Sopenharmony_ci            continue;
16933d722a9Sopenharmony_ci        } else if attr_path.segments.len() > 1 {
17033d722a9Sopenharmony_ci            let tool = &attr_path.segments.first().unwrap().ident;
17133d722a9Sopenharmony_ci            if tool == "rustfmt" {
17233d722a9Sopenharmony_ci                // Skip, rustfmt only needs to find it in the pre-expansion source file.
17333d722a9Sopenharmony_ci                continue;
17433d722a9Sopenharmony_ci            } else if tool == "clippy" {
17533d722a9Sopenharmony_ci                passthrough_attrs.push(attr);
17633d722a9Sopenharmony_ci                continue;
17733d722a9Sopenharmony_ci            }
17833d722a9Sopenharmony_ci        }
17933d722a9Sopenharmony_ci        if !parser.ignore_unrecognized {
18033d722a9Sopenharmony_ci            cx.error(attr, "unsupported attribute");
18133d722a9Sopenharmony_ci            break;
18233d722a9Sopenharmony_ci        }
18333d722a9Sopenharmony_ci    }
18433d722a9Sopenharmony_ci    OtherAttrs(passthrough_attrs)
18533d722a9Sopenharmony_ci}
18633d722a9Sopenharmony_ci
18733d722a9Sopenharmony_cienum DocAttribute {
18833d722a9Sopenharmony_ci    Doc(LitStr),
18933d722a9Sopenharmony_ci    Hidden,
19033d722a9Sopenharmony_ci}
19133d722a9Sopenharmony_ci
19233d722a9Sopenharmony_cimod kw {
19333d722a9Sopenharmony_ci    syn::custom_keyword!(hidden);
19433d722a9Sopenharmony_ci}
19533d722a9Sopenharmony_ci
19633d722a9Sopenharmony_cifn parse_doc_attribute(meta: &Meta) -> Result<DocAttribute> {
19733d722a9Sopenharmony_ci    match meta {
19833d722a9Sopenharmony_ci        Meta::NameValue(meta) => {
19933d722a9Sopenharmony_ci            if let Expr::Lit(expr) = &meta.value {
20033d722a9Sopenharmony_ci                if let Lit::Str(lit) = &expr.lit {
20133d722a9Sopenharmony_ci                    return Ok(DocAttribute::Doc(lit.clone()));
20233d722a9Sopenharmony_ci                }
20333d722a9Sopenharmony_ci            }
20433d722a9Sopenharmony_ci        }
20533d722a9Sopenharmony_ci        Meta::List(meta) => {
20633d722a9Sopenharmony_ci            meta.parse_args::<kw::hidden>()?;
20733d722a9Sopenharmony_ci            return Ok(DocAttribute::Hidden);
20833d722a9Sopenharmony_ci        }
20933d722a9Sopenharmony_ci        Meta::Path(_) => {}
21033d722a9Sopenharmony_ci    }
21133d722a9Sopenharmony_ci    Err(Error::new_spanned(meta, "unsupported doc attribute"))
21233d722a9Sopenharmony_ci}
21333d722a9Sopenharmony_ci
21433d722a9Sopenharmony_cifn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
21533d722a9Sopenharmony_ci    let paths = input.parse_terminated(Path::parse_mod_style, Token![,])?;
21633d722a9Sopenharmony_ci
21733d722a9Sopenharmony_ci    let mut derives = Vec::new();
21833d722a9Sopenharmony_ci    for path in paths {
21933d722a9Sopenharmony_ci        if let Some(ident) = path.get_ident() {
22033d722a9Sopenharmony_ci            if let Some(derive) = Derive::from(ident) {
22133d722a9Sopenharmony_ci                derives.push(derive);
22233d722a9Sopenharmony_ci                continue;
22333d722a9Sopenharmony_ci            }
22433d722a9Sopenharmony_ci        }
22533d722a9Sopenharmony_ci        cx.error(path, "unsupported derive");
22633d722a9Sopenharmony_ci    }
22733d722a9Sopenharmony_ci    Ok(derives)
22833d722a9Sopenharmony_ci}
22933d722a9Sopenharmony_ci
23033d722a9Sopenharmony_cifn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
23133d722a9Sopenharmony_ci    let begin = input.cursor();
23233d722a9Sopenharmony_ci    let ident: Ident = input.parse()?;
23333d722a9Sopenharmony_ci    if let Some(atom) = Atom::from(&ident) {
23433d722a9Sopenharmony_ci        match atom {
23533d722a9Sopenharmony_ci            U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
23633d722a9Sopenharmony_ci                return Ok(atom);
23733d722a9Sopenharmony_ci            }
23833d722a9Sopenharmony_ci            _ => {}
23933d722a9Sopenharmony_ci        }
24033d722a9Sopenharmony_ci    }
24133d722a9Sopenharmony_ci    Err(Error::new_spanned(
24233d722a9Sopenharmony_ci        begin.token_stream(),
24333d722a9Sopenharmony_ci        "unrecognized repr",
24433d722a9Sopenharmony_ci    ))
24533d722a9Sopenharmony_ci}
24633d722a9Sopenharmony_ci
24733d722a9Sopenharmony_cifn parse_cxx_name_attribute(meta: &Meta) -> Result<ForeignName> {
24833d722a9Sopenharmony_ci    if let Meta::NameValue(meta) = meta {
24933d722a9Sopenharmony_ci        match &meta.value {
25033d722a9Sopenharmony_ci            Expr::Lit(expr) => {
25133d722a9Sopenharmony_ci                if let Lit::Str(lit) = &expr.lit {
25233d722a9Sopenharmony_ci                    return ForeignName::parse(&lit.value(), lit.span());
25333d722a9Sopenharmony_ci                }
25433d722a9Sopenharmony_ci            }
25533d722a9Sopenharmony_ci            Expr::Path(expr) => {
25633d722a9Sopenharmony_ci                if let Some(ident) = expr.path.get_ident() {
25733d722a9Sopenharmony_ci                    return ForeignName::parse(&ident.to_string(), ident.span());
25833d722a9Sopenharmony_ci                }
25933d722a9Sopenharmony_ci            }
26033d722a9Sopenharmony_ci            _ => {}
26133d722a9Sopenharmony_ci        }
26233d722a9Sopenharmony_ci    }
26333d722a9Sopenharmony_ci    Err(Error::new_spanned(meta, "unsupported cxx_name attribute"))
26433d722a9Sopenharmony_ci}
26533d722a9Sopenharmony_ci
26633d722a9Sopenharmony_cifn parse_rust_name_attribute(meta: &Meta) -> Result<Ident> {
26733d722a9Sopenharmony_ci    if let Meta::NameValue(meta) = meta {
26833d722a9Sopenharmony_ci        match &meta.value {
26933d722a9Sopenharmony_ci            Expr::Lit(expr) => {
27033d722a9Sopenharmony_ci                if let Lit::Str(lit) = &expr.lit {
27133d722a9Sopenharmony_ci                    return lit.parse();
27233d722a9Sopenharmony_ci                }
27333d722a9Sopenharmony_ci            }
27433d722a9Sopenharmony_ci            Expr::Path(expr) => {
27533d722a9Sopenharmony_ci                if let Some(ident) = expr.path.get_ident() {
27633d722a9Sopenharmony_ci                    return Ok(ident.clone());
27733d722a9Sopenharmony_ci                }
27833d722a9Sopenharmony_ci            }
27933d722a9Sopenharmony_ci            _ => {}
28033d722a9Sopenharmony_ci        }
28133d722a9Sopenharmony_ci    }
28233d722a9Sopenharmony_ci    Err(Error::new_spanned(meta, "unsupported rust_name attribute"))
28333d722a9Sopenharmony_ci}
28433d722a9Sopenharmony_ci
28533d722a9Sopenharmony_ci#[derive(Clone)]
28633d722a9Sopenharmony_cipub struct OtherAttrs(Vec<Attribute>);
28733d722a9Sopenharmony_ci
28833d722a9Sopenharmony_ciimpl OtherAttrs {
28933d722a9Sopenharmony_ci    pub fn none() -> Self {
29033d722a9Sopenharmony_ci        OtherAttrs(Vec::new())
29133d722a9Sopenharmony_ci    }
29233d722a9Sopenharmony_ci
29333d722a9Sopenharmony_ci    pub fn extend(&mut self, other: Self) {
29433d722a9Sopenharmony_ci        self.0.extend(other.0);
29533d722a9Sopenharmony_ci    }
29633d722a9Sopenharmony_ci}
29733d722a9Sopenharmony_ci
29833d722a9Sopenharmony_ciimpl ToTokens for OtherAttrs {
29933d722a9Sopenharmony_ci    fn to_tokens(&self, tokens: &mut TokenStream) {
30033d722a9Sopenharmony_ci        for attr in &self.0 {
30133d722a9Sopenharmony_ci            let Attribute {
30233d722a9Sopenharmony_ci                pound_token,
30333d722a9Sopenharmony_ci                style,
30433d722a9Sopenharmony_ci                bracket_token,
30533d722a9Sopenharmony_ci                meta,
30633d722a9Sopenharmony_ci            } = attr;
30733d722a9Sopenharmony_ci            pound_token.to_tokens(tokens);
30833d722a9Sopenharmony_ci            let _ = style; // ignore; render outer and inner attrs both as outer
30933d722a9Sopenharmony_ci            bracket_token.surround(tokens, |tokens| meta.to_tokens(tokens));
31033d722a9Sopenharmony_ci        }
31133d722a9Sopenharmony_ci    }
31233d722a9Sopenharmony_ci}
313