1use crate::syntax::cfg::CfgExpr;
2use crate::syntax::namespace::Namespace;
3use crate::syntax::report::Errors;
4use crate::syntax::Atom::{self, *};
5use crate::syntax::{cfg, Derive, Doc, ForeignName};
6use proc_macro2::{Ident, TokenStream};
7use quote::ToTokens;
8use syn::parse::ParseStream;
9use syn::{Attribute, Error, Expr, Lit, LitStr, Meta, Path, Result, Token};
10
11#[derive(Default)]
30pub(crate) struct Parser<'a> {
31 pub cfg: Option<&'a mut CfgExpr>,
32 pub doc: Option<&'a mut Doc>,
33 pub derives: Option<&'a mut Vec<Derive>>,
34 pub repr: Option<&'a mut Option<Atom>>,
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 variants_from_header: Option<&'a mut Option<Attribute>>,
39 pub ignore_unrecognized: bool,
40
41 pub(crate) _more: (),
45}
46
47pub(crate) fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
48 let mut passthrough_attrs = Vec::new();
49 for attr in attrs {
50 let attr_path = attr.path();
51 if attr_path.is_ident("doc") {
52 match parse_doc_attribute(&attr.meta) {
53 Ok(attr) => {
54 if let Some(doc) = &mut parser.doc {
55 match attr {
56 DocAttribute::Doc(lit) => doc.push(lit),
57 DocAttribute::Hidden => doc.hidden = true,
58 }
59 continue;
60 }
61 }
62 Err(err) => {
63 cx.push(err);
64 break;
65 }
66 }
67 } else if attr_path.is_ident("derive") {
68 match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
69 Ok(attr) => {
70 if let Some(derives) = &mut parser.derives {
71 derives.extend(attr);
72 continue;
73 }
74 }
75 Err(err) => {
76 cx.push(err);
77 break;
78 }
79 }
80 } else if attr_path.is_ident("repr") {
81 match attr.parse_args_with(parse_repr_attribute) {
82 Ok(attr) => {
83 if let Some(repr) = &mut parser.repr {
84 **repr = Some(attr);
85 continue;
86 }
87 }
88 Err(err) => {
89 cx.push(err);
90 break;
91 }
92 }
93 } else if attr_path.is_ident("namespace") {
94 match Namespace::parse_meta(&attr.meta) {
95 Ok(attr) => {
96 if let Some(namespace) = &mut parser.namespace {
97 **namespace = attr;
98 continue;
99 }
100 }
101 Err(err) => {
102 cx.push(err);
103 break;
104 }
105 }
106 } else if attr_path.is_ident("cxx_name") {
107 match parse_cxx_name_attribute(&attr.meta) {
108 Ok(attr) => {
109 if let Some(cxx_name) = &mut parser.cxx_name {
110 **cxx_name = Some(attr);
111 continue;
112 }
113 }
114 Err(err) => {
115 cx.push(err);
116 break;
117 }
118 }
119 } else if attr_path.is_ident("rust_name") {
120 match parse_rust_name_attribute(&attr.meta) {
121 Ok(attr) => {
122 if let Some(rust_name) = &mut parser.rust_name {
123 **rust_name = Some(attr);
124 continue;
125 }
126 }
127 Err(err) => {
128 cx.push(err);
129 break;
130 }
131 }
132 } else if attr_path.is_ident("cfg") {
133 match cfg::parse_attribute(&attr) {
134 Ok(cfg_expr) => {
135 if let Some(cfg) = &mut parser.cfg {
136 cfg.merge(cfg_expr);
137 passthrough_attrs.push(attr);
138 continue;
139 }
140 }
141 Err(err) => {
142 cx.push(err);
143 break;
144 }
145 }
146 } else if attr_path.is_ident("variants_from_header")
147 && cfg!(feature = "experimental-enum-variants-from-header")
148 {
149 if let Err(err) = attr.meta.require_path_only() {
150 cx.push(err);
151 }
152 if let Some(variants_from_header) = &mut parser.variants_from_header {
153 **variants_from_header = Some(attr);
154 continue;
155 }
156 } else if attr_path.is_ident("allow")
157 || attr_path.is_ident("warn")
158 || attr_path.is_ident("deny")
159 || attr_path.is_ident("forbid")
160 || attr_path.is_ident("deprecated")
161 || attr_path.is_ident("must_use")
162 {
163 passthrough_attrs.push(attr);
165 continue;
166 } else if attr_path.is_ident("serde") {
167 passthrough_attrs.push(attr);
168 continue;
169 } else if attr_path.segments.len() > 1 {
170 let tool = &attr_path.segments.first().unwrap().ident;
171 if tool == "rustfmt" {
172 continue;
174 } else if tool == "clippy" {
175 passthrough_attrs.push(attr);
176 continue;
177 }
178 }
179 if !parser.ignore_unrecognized {
180 cx.error(attr, "unsupported attribute");
181 break;
182 }
183 }
184 OtherAttrs(passthrough_attrs)
185}
186
187enum DocAttribute {
188 Doc(LitStr),
189 Hidden,
190}
191
192mod kw {
193 syn::custom_keyword!(hidden);
194}
195
196fn parse_doc_attribute(meta: &Meta) -> Result<DocAttribute> {
197 match meta {
198 Meta::NameValue(meta) => {
199 if let Expr::Lit(expr) = &meta.value {
200 if let Lit::Str(lit) = &expr.lit {
201 return Ok(DocAttribute::Doc(lit.clone()));
202 }
203 }
204 }
205 Meta::List(meta) => {
206 meta.parse_args::<kw::hidden>()?;
207 return Ok(DocAttribute::Hidden);
208 }
209 Meta::Path(_) => {}
210 }
211 Err(Error::new_spanned(meta, "unsupported doc attribute"))
212}
213
214fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
215 let paths = input.parse_terminated(Path::parse_mod_style, Token![,])?;
216
217 let mut derives = Vec::new();
218 for path in paths {
219 if let Some(ident) = path.get_ident() {
220 if let Some(derive) = Derive::from(ident) {
221 derives.push(derive);
222 continue;
223 }
224 }
225 cx.error(path, "unsupported derive");
226 }
227 Ok(derives)
228}
229
230fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
231 let begin = input.cursor();
232 let ident: Ident = input.parse()?;
233 if let Some(atom) = Atom::from(&ident) {
234 match atom {
235 U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
236 return Ok(atom);
237 }
238 _ => {}
239 }
240 }
241 Err(Error::new_spanned(
242 begin.token_stream(),
243 "unrecognized repr",
244 ))
245}
246
247fn parse_cxx_name_attribute(meta: &Meta) -> Result<ForeignName> {
248 if let Meta::NameValue(meta) = meta {
249 match &meta.value {
250 Expr::Lit(expr) => {
251 if let Lit::Str(lit) = &expr.lit {
252 return ForeignName::parse(&lit.value(), lit.span());
253 }
254 }
255 Expr::Path(expr) => {
256 if let Some(ident) = expr.path.get_ident() {
257 return ForeignName::parse(&ident.to_string(), ident.span());
258 }
259 }
260 _ => {}
261 }
262 }
263 Err(Error::new_spanned(meta, "unsupported cxx_name attribute"))
264}
265
266fn parse_rust_name_attribute(meta: &Meta) -> Result<Ident> {
267 if let Meta::NameValue(meta) = meta {
268 match &meta.value {
269 Expr::Lit(expr) => {
270 if let Lit::Str(lit) = &expr.lit {
271 return lit.parse();
272 }
273 }
274 Expr::Path(expr) => {
275 if let Some(ident) = expr.path.get_ident() {
276 return Ok(ident.clone());
277 }
278 }
279 _ => {}
280 }
281 }
282 Err(Error::new_spanned(meta, "unsupported rust_name attribute"))
283}
284
285#[derive(Clone)]
286pub(crate) struct OtherAttrs(Vec<Attribute>);
287
288impl OtherAttrs {
289 pub(crate) fn none() -> Self {
290 OtherAttrs(Vec::new())
291 }
292
293 pub(crate) fn extend(&mut self, other: Self) {
294 self.0.extend(other.0);
295 }
296}
297
298impl ToTokens for OtherAttrs {
299 fn to_tokens(&self, tokens: &mut TokenStream) {
300 for attr in &self.0 {
301 let Attribute {
302 pound_token,
303 style,
304 bracket_token,
305 meta,
306 } = attr;
307 pound_token.to_tokens(tokens);
308 let _ = style; bracket_token.surround(tokens, |tokens| meta.to_tokens(tokens));
310 }
311 }
312}