blob: a1ad03cc2ec5e19dbcb1bf545b6913af793ef683 [file] [log] [blame]
Adrian Taylorc8713432020-10-21 18:20:55 -07001use crate::syntax::namespace::Namespace;
David Tolnay3e628882020-05-10 15:30:14 -07002use crate::syntax::report::Errors;
David Tolnayddf69e22020-05-10 22:08:20 -07003use crate::syntax::Atom::{self, *};
David Tolnayed6ba4a2021-01-01 14:59:40 -08004use crate::syntax::{Derive, Doc, ForeignName};
David Tolnaycba3d442020-12-30 16:39:21 -08005use proc_macro2::{Ident, TokenStream};
6use quote::ToTokens;
David Tolnay15bf5092021-04-21 18:16:38 -07007use syn::parse::{Nothing, Parse, ParseStream, Parser as _};
8use syn::{AttrStyle, Attribute, Error, LitStr, Path, Result, Token};
David Tolnay7db73692019-10-20 14:51:12 -04009
David Tolnayb11c9ef2020-12-21 16:26:22 -080010// Intended usage:
11//
12// let mut doc = Doc::new();
13// let mut cxx_name = None;
14// let mut rust_name = None;
15// /* ... */
David Tolnayf08e94d2020-12-30 16:46:21 -080016// let attrs = attrs::parse(
David Tolnayb11c9ef2020-12-21 16:26:22 -080017// cx,
David Tolnayf08e94d2020-12-30 16:46:21 -080018// item.attrs,
David Tolnayb11c9ef2020-12-21 16:26:22 -080019// 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//
David Tolnayb129ea72020-05-10 14:29:30 -070028#[derive(Default)]
29pub struct Parser<'a> {
30 pub doc: Option<&'a mut Doc>,
31 pub derives: Option<&'a mut Vec<Derive>>,
David Tolnayddf69e22020-05-10 22:08:20 -070032 pub repr: Option<&'a mut Option<Atom>>,
David Tolnaya2dfcbf2020-12-21 15:46:24 -080033 pub namespace: Option<&'a mut Namespace>,
David Tolnayed6ba4a2021-01-01 14:59:40 -080034 pub cxx_name: Option<&'a mut Option<ForeignName>>,
David Tolnay1039a242020-10-09 19:18:12 -070035 pub rust_name: Option<&'a mut Option<Ident>>,
David Tolnay15bf5092021-04-21 18:16:38 -070036 pub variants_from_header: Option<&'a mut bool>,
David Tolnayd25033c2020-12-21 16:24:19 -080037
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: (),
David Tolnay7db73692019-10-20 14:51:12 -040042}
43
David Tolnay41e147c2020-12-30 17:06:39 -080044pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
David Tolnay067638e2020-12-30 16:18:05 -080045 let mut passthrough_attrs = Vec::new();
David Tolnay7db73692019-10-20 14:51:12 -040046 for attr in attrs {
47 if attr.path.is_ident("doc") {
David Tolnay3e628882020-05-10 15:30:14 -070048 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 }
David Tolnay067638e2020-12-30 16:18:05 -080055 Err(err) => {
56 cx.push(err);
57 break;
58 }
David Tolnayb129ea72020-05-10 14:29:30 -070059 }
David Tolnay7db73692019-10-20 14:51:12 -040060 } else if attr.path.is_ident("derive") {
David Tolnayf4b93342020-11-27 13:59:42 -080061 match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
David Tolnay3e628882020-05-10 15:30:14 -070062 Ok(attr) => {
63 if let Some(derives) = &mut parser.derives {
64 derives.extend(attr);
65 continue;
66 }
67 }
David Tolnay067638e2020-12-30 16:18:05 -080068 Err(err) => {
69 cx.push(err);
70 break;
71 }
David Tolnay7db73692019-10-20 14:51:12 -040072 }
David Tolnayddf69e22020-05-10 22:08:20 -070073 } 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 }
David Tolnay067638e2020-12-30 16:18:05 -080081 Err(err) => {
82 cx.push(err);
83 break;
84 }
David Tolnayddf69e22020-05-10 22:08:20 -070085 }
David Tolnaya2dfcbf2020-12-21 15:46:24 -080086 } 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 }
David Tolnay067638e2020-12-30 16:18:05 -080094 Err(err) => {
95 cx.push(err);
96 break;
97 }
David Tolnaya2dfcbf2020-12-21 15:46:24 -080098 }
David Tolnay1039a242020-10-09 19:18:12 -070099 } else if attr.path.is_ident("cxx_name") {
David Tolnayed6ba4a2021-01-01 14:59:40 -0800100 match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
David Tolnay1039a242020-10-09 19:18:12 -0700101 Ok(attr) => {
102 if let Some(cxx_name) = &mut parser.cxx_name {
103 **cxx_name = Some(attr);
104 continue;
105 }
106 }
David Tolnay067638e2020-12-30 16:18:05 -0800107 Err(err) => {
108 cx.push(err);
109 break;
110 }
David Tolnay1039a242020-10-09 19:18:12 -0700111 }
112 } else if attr.path.is_ident("rust_name") {
David Tolnayed6ba4a2021-01-01 14:59:40 -0800113 match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
David Tolnay1039a242020-10-09 19:18:12 -0700114 Ok(attr) => {
115 if let Some(rust_name) = &mut parser.rust_name {
116 **rust_name = Some(attr);
117 continue;
118 }
119 }
David Tolnay067638e2020-12-30 16:18:05 -0800120 Err(err) => {
121 cx.push(err);
122 break;
123 }
David Tolnay1039a242020-10-09 19:18:12 -0700124 }
David Tolnay15bf5092021-04-21 18:16:38 -0700125 } else if attr.path.is_ident("variants_from_header")
126 && matches!(attr.style, AttrStyle::Inner(_))
127 && cfg!(feature = "experimental")
128 {
129 if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) {
130 cx.push(err);
131 }
132 if let Some(variants_from_header) = &mut parser.variants_from_header {
133 **variants_from_header = true;
134 continue;
135 }
David Tolnay20d980b2020-12-30 15:56:05 -0800136 } else if attr.path.is_ident("allow")
137 || attr.path.is_ident("warn")
138 || attr.path.is_ident("deny")
139 || attr.path.is_ident("forbid")
David Tolnay511c6fd2020-12-30 16:55:04 -0800140 || attr.path.is_ident("deprecated")
141 || attr.path.is_ident("must_use")
David Tolnay20d980b2020-12-30 15:56:05 -0800142 {
David Tolnay511c6fd2020-12-30 16:55:04 -0800143 // https://doc.rust-lang.org/reference/attributes/diagnostics.html
David Tolnay067638e2020-12-30 16:18:05 -0800144 passthrough_attrs.push(attr);
David Tolnay20d980b2020-12-30 15:56:05 -0800145 continue;
David Tolnaydc182792020-12-30 16:58:16 -0800146 } else if attr.path.segments.len() > 1 {
147 let tool = &attr.path.segments.first().unwrap().ident;
148 if tool == "rustfmt" {
149 // Skip, rustfmt only needs to find it in the pre-expansion source file.
150 continue;
151 } else if tool == "clippy" {
152 passthrough_attrs.push(attr);
153 continue;
154 }
David Tolnay7db73692019-10-20 14:51:12 -0400155 }
David Tolnay067638e2020-12-30 16:18:05 -0800156 cx.error(attr, "unsupported attribute");
157 break;
David Tolnay7db73692019-10-20 14:51:12 -0400158 }
David Tolnaycba3d442020-12-30 16:39:21 -0800159 OtherAttrs(passthrough_attrs)
David Tolnay7db73692019-10-20 14:51:12 -0400160}
161
162fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
163 input.parse::<Token![=]>()?;
164 let lit: LitStr = input.parse()?;
165 Ok(lit)
166}
167
David Tolnayf4b93342020-11-27 13:59:42 -0800168fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
169 let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
170
171 let mut derives = Vec::new();
172 for path in paths {
173 if let Some(ident) = path.get_ident() {
174 if let Some(derive) = Derive::from(ident) {
175 derives.push(derive);
176 continue;
David Tolnaye86b9cf2020-05-10 14:24:29 -0700177 }
David Tolnayf4b93342020-11-27 13:59:42 -0800178 }
179 cx.error(path, "unsupported derive");
180 }
181 Ok(derives)
David Tolnay7db73692019-10-20 14:51:12 -0400182}
David Tolnayddf69e22020-05-10 22:08:20 -0700183
184fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
185 let begin = input.cursor();
186 let ident: Ident = input.parse()?;
187 if let Some(atom) = Atom::from(&ident) {
188 match atom {
David Tolnay8155e582020-05-11 00:05:02 -0700189 U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
190 return Ok(atom);
191 }
David Tolnayddf69e22020-05-10 22:08:20 -0700192 _ => {}
193 }
194 }
195 Err(Error::new_spanned(
196 begin.token_stream(),
197 "unrecognized repr",
198 ))
199}
David Tolnay1039a242020-10-09 19:18:12 -0700200
David Tolnaya2dfcbf2020-12-21 15:46:24 -0800201fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
202 input.parse::<Token![=]>()?;
203 let namespace = input.parse::<Namespace>()?;
204 Ok(namespace)
205}
206
David Tolnayed6ba4a2021-01-01 14:59:40 -0800207fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
208 input.parse::<Token![=]>()?;
209 if input.peek(LitStr) {
210 let lit: LitStr = input.parse()?;
211 ForeignName::parse(&lit.value(), lit.span())
212 } else {
213 let ident: Ident = input.parse()?;
214 ForeignName::parse(&ident.to_string(), ident.span())
215 }
216}
217
218fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
David Tolnay1039a242020-10-09 19:18:12 -0700219 input.parse::<Token![=]>()?;
220 if input.peek(LitStr) {
221 let lit: LitStr = input.parse()?;
222 lit.parse()
223 } else {
224 input.parse()
225 }
226}
David Tolnaycba3d442020-12-30 16:39:21 -0800227
228pub struct OtherAttrs(Vec<Attribute>);
229
230impl OtherAttrs {
231 pub fn none() -> Self {
232 OtherAttrs(Vec::new())
233 }
234}
235
236impl ToTokens for OtherAttrs {
237 fn to_tokens(&self, tokens: &mut TokenStream) {
238 for attr in &self.0 {
David Tolnay757f7ff2021-04-13 20:52:10 -0700239 let Attribute {
240 pound_token,
241 style,
242 bracket_token,
243 path,
244 tokens: attr_tokens,
245 } = attr;
246 pound_token.to_tokens(tokens);
247 let _ = style; // ignore; render outer and inner attrs both as outer
248 bracket_token.surround(tokens, |tokens| {
249 path.to_tokens(tokens);
250 attr_tokens.to_tokens(tokens);
251 });
David Tolnaycba3d442020-12-30 16:39:21 -0800252 }
253 }
254}