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