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