• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::syntax::namespace::Namespace;
2 use crate::syntax::report::Errors;
3 use crate::syntax::Atom::{self, *};
4 use crate::syntax::{Derive, Doc, ForeignName};
5 use proc_macro2::{Ident, TokenStream};
6 use quote::ToTokens;
7 use syn::parse::{Nothing, Parse, ParseStream, Parser as _};
8 use syn::{Attribute, Error, LitStr, 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(Default)]
29 pub struct Parser<'a> {
30     pub doc: Option<&'a mut Doc>,
31     pub derives: Option<&'a mut Vec<Derive>>,
32     pub repr: Option<&'a mut Option<Atom>>,
33     pub namespace: Option<&'a mut Namespace>,
34     pub cxx_name: Option<&'a mut Option<ForeignName>>,
35     pub rust_name: Option<&'a mut Option<Ident>>,
36     pub variants_from_header: Option<&'a mut Option<Attribute>>,
37 
38     // Suppress clippy needless_update lint ("struct update has no effect, all
39     // the fields in the struct have already been specified") when preemptively
40     // writing `..Default::default()`.
41     pub(crate) _more: (),
42 }
43 
parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs44 pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
45     let mut passthrough_attrs = Vec::new();
46     for attr in attrs {
47         if attr.path.is_ident("doc") {
48             match parse_doc_attribute.parse2(attr.tokens.clone()) {
49                 Ok(lit) => {
50                     if let Some(doc) = &mut parser.doc {
51                         doc.push(lit);
52                         continue;
53                     }
54                 }
55                 Err(err) => {
56                     cx.push(err);
57                     break;
58                 }
59             }
60         } else if attr.path.is_ident("derive") {
61             match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
62                 Ok(attr) => {
63                     if let Some(derives) = &mut parser.derives {
64                         derives.extend(attr);
65                         continue;
66                     }
67                 }
68                 Err(err) => {
69                     cx.push(err);
70                     break;
71                 }
72             }
73         } else if attr.path.is_ident("repr") {
74             match attr.parse_args_with(parse_repr_attribute) {
75                 Ok(attr) => {
76                     if let Some(repr) = &mut parser.repr {
77                         **repr = Some(attr);
78                         continue;
79                     }
80                 }
81                 Err(err) => {
82                     cx.push(err);
83                     break;
84                 }
85             }
86         } else if attr.path.is_ident("namespace") {
87             match parse_namespace_attribute.parse2(attr.tokens.clone()) {
88                 Ok(attr) => {
89                     if let Some(namespace) = &mut parser.namespace {
90                         **namespace = attr;
91                         continue;
92                     }
93                 }
94                 Err(err) => {
95                     cx.push(err);
96                     break;
97                 }
98             }
99         } else if attr.path.is_ident("cxx_name") {
100             match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
101                 Ok(attr) => {
102                     if let Some(cxx_name) = &mut parser.cxx_name {
103                         **cxx_name = Some(attr);
104                         continue;
105                     }
106                 }
107                 Err(err) => {
108                     cx.push(err);
109                     break;
110                 }
111             }
112         } else if attr.path.is_ident("rust_name") {
113             match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
114                 Ok(attr) => {
115                     if let Some(rust_name) = &mut parser.rust_name {
116                         **rust_name = Some(attr);
117                         continue;
118                     }
119                 }
120                 Err(err) => {
121                     cx.push(err);
122                     break;
123                 }
124             }
125         } else if attr.path.is_ident("variants_from_header") && cfg!(feature = "experimental") {
126             if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) {
127                 cx.push(err);
128             }
129             if let Some(variants_from_header) = &mut parser.variants_from_header {
130                 **variants_from_header = Some(attr);
131                 continue;
132             }
133         } else if attr.path.is_ident("allow")
134             || attr.path.is_ident("warn")
135             || attr.path.is_ident("deny")
136             || attr.path.is_ident("forbid")
137             || attr.path.is_ident("deprecated")
138             || attr.path.is_ident("must_use")
139         {
140             // https://doc.rust-lang.org/reference/attributes/diagnostics.html
141             passthrough_attrs.push(attr);
142             continue;
143         } else if attr.path.segments.len() > 1 {
144             let tool = &attr.path.segments.first().unwrap().ident;
145             if tool == "rustfmt" {
146                 // Skip, rustfmt only needs to find it in the pre-expansion source file.
147                 continue;
148             } else if tool == "clippy" {
149                 passthrough_attrs.push(attr);
150                 continue;
151             }
152         }
153         cx.error(attr, "unsupported attribute");
154         break;
155     }
156     OtherAttrs(passthrough_attrs)
157 }
158 
parse_doc_attribute(input: ParseStream) -> Result<LitStr>159 fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
160     input.parse::<Token![=]>()?;
161     let lit: LitStr = input.parse()?;
162     Ok(lit)
163 }
164 
parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>>165 fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
166     let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
167 
168     let mut derives = Vec::new();
169     for path in paths {
170         if let Some(ident) = path.get_ident() {
171             if let Some(derive) = Derive::from(ident) {
172                 derives.push(derive);
173                 continue;
174             }
175         }
176         cx.error(path, "unsupported derive");
177     }
178     Ok(derives)
179 }
180 
parse_repr_attribute(input: ParseStream) -> Result<Atom>181 fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
182     let begin = input.cursor();
183     let ident: Ident = input.parse()?;
184     if let Some(atom) = Atom::from(&ident) {
185         match atom {
186             U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
187                 return Ok(atom);
188             }
189             _ => {}
190         }
191     }
192     Err(Error::new_spanned(
193         begin.token_stream(),
194         "unrecognized repr",
195     ))
196 }
197 
parse_namespace_attribute(input: ParseStream) -> Result<Namespace>198 fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
199     input.parse::<Token![=]>()?;
200     let namespace = input.parse::<Namespace>()?;
201     Ok(namespace)
202 }
203 
parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName>204 fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
205     input.parse::<Token![=]>()?;
206     if input.peek(LitStr) {
207         let lit: LitStr = input.parse()?;
208         ForeignName::parse(&lit.value(), lit.span())
209     } else {
210         let ident: Ident = input.parse()?;
211         ForeignName::parse(&ident.to_string(), ident.span())
212     }
213 }
214 
parse_rust_name_attribute(input: ParseStream) -> Result<Ident>215 fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
216     input.parse::<Token![=]>()?;
217     if input.peek(LitStr) {
218         let lit: LitStr = input.parse()?;
219         lit.parse()
220     } else {
221         input.parse()
222     }
223 }
224 
225 pub struct OtherAttrs(Vec<Attribute>);
226 
227 impl OtherAttrs {
none() -> Self228     pub fn none() -> Self {
229         OtherAttrs(Vec::new())
230     }
231 }
232 
233 impl ToTokens for OtherAttrs {
to_tokens(&self, tokens: &mut TokenStream)234     fn to_tokens(&self, tokens: &mut TokenStream) {
235         for attr in &self.0 {
236             let Attribute {
237                 pound_token,
238                 style,
239                 bracket_token,
240                 path,
241                 tokens: attr_tokens,
242             } = attr;
243             pound_token.to_tokens(tokens);
244             let _ = style; // ignore; render outer and inner attrs both as outer
245             bracket_token.surround(tokens, |tokens| {
246                 path.to_tokens(tokens);
247                 attr_tokens.to_tokens(tokens);
248             });
249         }
250     }
251 }
252