cxx_build/syntax/
attrs.rs

1use crate::syntax::cfg::CfgExpr;
2use crate::syntax::namespace::Namespace;
3use crate::syntax::report::Errors;
4use crate::syntax::repr::Repr;
5use crate::syntax::{cfg, Derive, Doc, ForeignName};
6use proc_macro2::Ident;
7use syn::parse::ParseStream;
8use syn::{Attribute, Error, Expr, Lit, LitStr, Meta, Path, Result, Token};
9
10// Intended usage:
11//
12//     let mut doc = Doc::new();
13//     let mut cxx_name = None;
14//     let mut rust_name = None;
15//     /* ... */
16//     let attrs = attrs::parse(
17//         cx,
18//         item.attrs,
19//         attrs::Parser {
20//             doc: Some(&mut doc),
21//             cxx_name: Some(&mut cxx_name),
22//             rust_name: Some(&mut rust_name),
23//             /* ... */
24//             ..Default::default()
25//         },
26//     );
27//
28#[derive(#[automatically_derived]
impl<'a> ::core::default::Default for Parser<'a> {
    #[inline]
    fn default() -> Parser<'a> {
        Parser {
            cfg: ::core::default::Default::default(),
            doc: ::core::default::Default::default(),
            derives: ::core::default::Default::default(),
            repr: ::core::default::Default::default(),
            default: ::core::default::Default::default(),
            namespace: ::core::default::Default::default(),
            cxx_name: ::core::default::Default::default(),
            rust_name: ::core::default::Default::default(),
            self_type: ::core::default::Default::default(),
            ignore_unrecognized: ::core::default::Default::default(),
            _more: ::core::default::Default::default(),
        }
    }
}Default)]
29pub(crate) struct Parser<'a> {
30    pub cfg: Option<&'a mut CfgExpr>,
31    pub doc: Option<&'a mut Doc>,
32    pub derives: Option<&'a mut Vec<Derive>>,
33    pub repr: Option<&'a mut Option<Repr>>,
34    pub default: Option<&'a mut bool>,
35    pub namespace: Option<&'a mut Namespace>,
36    pub cxx_name: Option<&'a mut Option<ForeignName>>,
37    pub rust_name: Option<&'a mut Option<Ident>>,
38    pub self_type: Option<&'a mut Option<Ident>>,
39    pub ignore_unrecognized: bool,
40
41    // Suppress clippy needless_update lint ("struct update has no effect, all
42    // the fields in the struct have already been specified") when preemptively
43    // writing `..Default::default()`.
44    pub(crate) _more: (),
45}
46
47#[must_use]
48pub(crate) fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
49    let mut other_attrs = OtherAttrs::new();
50    for attr in attrs {
51        let attr_path = attr.path();
52        if attr_path.is_ident("doc") {
53            match parse_doc_attribute(&attr.meta) {
54                Ok(attr) => {
55                    if let Some(doc) = &mut parser.doc {
56                        match attr {
57                            DocAttribute::Doc(lit) => doc.push(lit),
58                            DocAttribute::Hidden => doc.hidden = true,
59                        }
60                        continue;
61                    }
62                }
63                Err(err) => {
64                    cx.push(err);
65                    break;
66                }
67            }
68        } else if attr_path.is_ident("derive") {
69            match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
70                Ok(attr) => {
71                    if let Some(derives) = &mut parser.derives {
72                        derives.extend(attr);
73                        continue;
74                    }
75                }
76                Err(err) => {
77                    cx.push(err);
78                    break;
79                }
80            }
81        } else if attr_path.is_ident("repr") {
82            match attr.parse_args::<Repr>() {
83                Ok(attr) => {
84                    if let Some(repr) = &mut parser.repr {
85                        **repr = Some(attr);
86                        continue;
87                    }
88                }
89                Err(err) => {
90                    cx.push(err);
91                    break;
92                }
93            }
94        } else if attr_path.is_ident("default") {
95            match parse_default_attribute(&attr.meta) {
96                Ok(()) => {
97                    if let Some(default) = &mut parser.default {
98                        **default = true;
99                        continue;
100                    }
101                }
102                Err(err) => {
103                    cx.push(err);
104                    break;
105                }
106            }
107        } else if attr_path.is_ident("namespace") {
108            match Namespace::parse_meta(&attr.meta) {
109                Ok(attr) => {
110                    if let Some(namespace) = &mut parser.namespace {
111                        **namespace = attr;
112                        continue;
113                    }
114                }
115                Err(err) => {
116                    cx.push(err);
117                    break;
118                }
119            }
120        } else if attr_path.is_ident("cxx_name") {
121            match parse_cxx_name_attribute(&attr.meta) {
122                Ok(attr) => {
123                    if let Some(cxx_name) = &mut parser.cxx_name {
124                        **cxx_name = Some(attr);
125                        continue;
126                    }
127                }
128                Err(err) => {
129                    cx.push(err);
130                    break;
131                }
132            }
133        } else if attr_path.is_ident("rust_name") {
134            match parse_rust_ident_attribute(&attr.meta) {
135                Ok(attr) => {
136                    if let Some(rust_name) = &mut parser.rust_name {
137                        **rust_name = Some(attr);
138                        continue;
139                    }
140                }
141                Err(err) => {
142                    cx.push(err);
143                    break;
144                }
145            }
146        } else if attr_path.is_ident("Self") {
147            match parse_rust_ident_attribute(&attr.meta) {
148                Ok(attr) => {
149                    if let Some(self_type) = &mut parser.self_type {
150                        **self_type = Some(attr);
151                        continue;
152                    }
153                }
154                Err(err) => {
155                    cx.push(err);
156                    break;
157                }
158            }
159        } else if attr_path.is_ident("cfg") {
160            match cfg::parse_attribute(&attr) {
161                Ok(cfg_expr) => {
162                    if let Some(cfg) = &mut parser.cfg {
163                        cfg.merge_and(cfg_expr);
164                        other_attrs.cfg.push(attr);
165                        continue;
166                    }
167                }
168                Err(err) => {
169                    cx.push(err);
170                    break;
171                }
172            }
173        } else if attr_path.is_ident("allow")
174            || attr_path.is_ident("warn")
175            || attr_path.is_ident("deny")
176            || attr_path.is_ident("forbid")
177        {
178            other_attrs.lint.push(attr);
179            continue;
180        } else if attr_path.is_ident("deprecated")
181            || attr_path.is_ident("must_use")
182            || attr_path.is_ident("serde")
183        {
184            other_attrs.passthrough.push(attr);
185            continue;
186        } else if attr_path.segments.len() > 1 {
187            let tool = &attr_path.segments.first().unwrap().ident;
188            if tool == "rustfmt" {
189                // Skip, rustfmt only needs to find it in the pre-expansion source file.
190                continue;
191            } else if tool == "clippy" {
192                other_attrs.lint.push(attr);
193                continue;
194            }
195        }
196        if !parser.ignore_unrecognized {
197            cx.error(attr, "unsupported attribute");
198            break;
199        }
200    }
201    other_attrs
202}
203
204enum DocAttribute {
205    Doc(LitStr),
206    Hidden,
207}
208
209mod kw {
210    #[allow(non_camel_case_types)]
pub struct hidden {
    #[allow(dead_code)]
    pub span: ::syn::__private::Span,
}
#[doc(hidden)]
#[allow(dead_code, non_snake_case)]
pub fn hidden<__S: ::syn::__private::IntoSpans<::syn::__private::Span>>(span:
        __S) -> hidden {
    hidden { span: ::syn::__private::IntoSpans::into_spans(span) }
}
const _: () =
    {
        impl ::syn::__private::Default for hidden {
            fn default() -> Self {
                hidden { span: ::syn::__private::Span::call_site() }
            }
        }
        impl ::syn::__private::CustomToken for hidden {
            fn peek(cursor: ::syn::buffer::Cursor) -> ::syn::__private::bool {
                if let ::syn::__private::Some((ident, _rest)) = cursor.ident()
                    {
                    ident == "hidden"
                } else { false }
            }
            fn display() -> &'static ::syn::__private::str { "`hidden`" }
        }
        impl ::syn::parse::Parse for hidden {
            fn parse(input: ::syn::parse::ParseStream)
                -> ::syn::parse::Result<hidden> {
                input.step(|cursor|
                        {
                            if let ::syn::__private::Some((ident, rest)) =
                                    cursor.ident() {
                                if ident == "hidden" {
                                    return ::syn::__private::Ok((hidden { span: ident.span() },
                                                rest));
                                }
                            }
                            ::syn::__private::Err(cursor.error("expected `hidden`"))
                        })
            }
        }
        impl ::syn::__private::ToTokens for hidden {
            fn to_tokens(&self, tokens: &mut ::syn::__private::TokenStream2) {
                let ident = ::syn::Ident::new("hidden", self.span);
                ::syn::__private::TokenStreamExt::append(tokens, ident);
            }
        }
        impl ::syn::__private::Copy for hidden {}
        #[allow(clippy :: expl_impl_clone_on_copy)]
        impl ::syn::__private::Clone for hidden {
            fn clone(&self) -> Self { *self }
        }
        ;
    };syn::custom_keyword!(hidden);
211}
212
213fn parse_doc_attribute(meta: &Meta) -> Result<DocAttribute> {
214    match meta {
215        Meta::NameValue(meta) => {
216            if let Expr::Lit(expr) = &meta.value {
217                if let Lit::Str(lit) = &expr.lit {
218                    return Ok(DocAttribute::Doc(lit.clone()));
219                }
220            }
221        }
222        Meta::List(meta) => {
223            meta.parse_args::<kw::hidden>()?;
224            return Ok(DocAttribute::Hidden);
225        }
226        Meta::Path(_) => {}
227    }
228    Err(Error::new_spanned(meta, "unsupported doc attribute"))
229}
230
231fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
232    let paths = input.parse_terminated(Path::parse_mod_style, ::syn::token::CommaToken![,])?;
233
234    let mut derives = Vec::new();
235    for path in paths {
236        if let Some(ident) = path.get_ident() {
237            if let Some(derive) = Derive::from(ident) {
238                derives.push(derive);
239                continue;
240            }
241        }
242        cx.error(path, "unsupported derive");
243    }
244    Ok(derives)
245}
246
247fn parse_default_attribute(meta: &Meta) -> Result<()> {
248    let error_span = match meta {
249        Meta::Path(_) => return Ok(()),
250        Meta::List(meta) => meta.delimiter.span().open(),
251        Meta::NameValue(meta) => meta.eq_token.span,
252    };
253    Err(Error::new(
254        error_span,
255        "#[default] attribute does not accept an argument",
256    ))
257}
258
259fn parse_cxx_name_attribute(meta: &Meta) -> Result<ForeignName> {
260    if let Meta::NameValue(meta) = meta {
261        match &meta.value {
262            Expr::Lit(expr) => {
263                if let Lit::Str(lit) = &expr.lit {
264                    return ForeignName::parse(&lit.value(), lit.span());
265                }
266            }
267            Expr::Path(expr) => {
268                if let Some(ident) = expr.path.get_ident() {
269                    return ForeignName::parse(&ident.to_string(), ident.span());
270                }
271            }
272            _ => {}
273        }
274    }
275    Err(Error::new_spanned(meta, "unsupported cxx_name attribute"))
276}
277
278fn parse_rust_ident_attribute(meta: &Meta) -> Result<Ident> {
279    if let Meta::NameValue(meta) = meta {
280        match &meta.value {
281            Expr::Lit(expr) => {
282                if let Lit::Str(lit) = &expr.lit {
283                    return lit.parse();
284                }
285            }
286            Expr::Path(expr) => {
287                if let Some(ident) = expr.path.get_ident() {
288                    return Ok(ident.clone());
289                }
290            }
291            _ => {}
292        }
293    }
294    Err(Error::new_spanned(
295        meta,
296        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("unsupported `{0}` attribute",
                meta.path().get_ident().unwrap()))
    })format!(
297            "unsupported `{}` attribute",
298            meta.path().get_ident().unwrap(),
299        ),
300    ))
301}
302
303#[derive(#[automatically_derived]
impl ::core::clone::Clone for OtherAttrs {
    #[inline]
    fn clone(&self) -> OtherAttrs {
        OtherAttrs {
            cfg: ::core::clone::Clone::clone(&self.cfg),
            lint: ::core::clone::Clone::clone(&self.lint),
            passthrough: ::core::clone::Clone::clone(&self.passthrough),
        }
    }
}Clone)]
304pub(crate) struct OtherAttrs {
305    pub cfg: Vec<Attribute>,
306    pub lint: Vec<Attribute>,
307    pub passthrough: Vec<Attribute>,
308}
309
310impl OtherAttrs {
311    pub(crate) fn new() -> Self {
312        OtherAttrs {
313            cfg: Vec::new(),
314            lint: Vec::new(),
315            passthrough: Vec::new(),
316        }
317    }
318
319    pub(crate) fn extend(&mut self, other: Self) {
320        self.cfg.extend(other.cfg);
321        self.lint.extend(other.lint);
322        self.passthrough.extend(other.passthrough);
323    }
324}