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