blob: 4808f2e1e89f583b129af9ff0e39c37193dbe1d2 [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 Tolnayb129ea72020-05-10 14:29:30 -07007use syn::parse::{ParseStream, Parser as _};
David Tolnay7db73692019-10-20 14:51:12 -04008use syn::{Attribute, Error, LitStr, Path, Result, Token};
9
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 Tolnayd25033c2020-12-21 16:24:19 -080036
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: (),
David Tolnay7db73692019-10-20 14:51:12 -040041}
42
David Tolnay41e147c2020-12-30 17:06:39 -080043pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
David Tolnay067638e2020-12-30 16:18:05 -080044 let mut passthrough_attrs = Vec::new();
David Tolnay7db73692019-10-20 14:51:12 -040045 for attr in attrs {
46 if attr.path.is_ident("doc") {
David Tolnay3e628882020-05-10 15:30:14 -070047 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 }
David Tolnay067638e2020-12-30 16:18:05 -080054 Err(err) => {
55 cx.push(err);
56 break;
57 }
David Tolnayb129ea72020-05-10 14:29:30 -070058 }
David Tolnay7db73692019-10-20 14:51:12 -040059 } else if attr.path.is_ident("derive") {
David Tolnayf4b93342020-11-27 13:59:42 -080060 match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
David Tolnay3e628882020-05-10 15:30:14 -070061 Ok(attr) => {
62 if let Some(derives) = &mut parser.derives {
63 derives.extend(attr);
64 continue;
65 }
66 }
David Tolnay067638e2020-12-30 16:18:05 -080067 Err(err) => {
68 cx.push(err);
69 break;
70 }
David Tolnay7db73692019-10-20 14:51:12 -040071 }
David Tolnayddf69e22020-05-10 22:08:20 -070072 } 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 }
David Tolnay067638e2020-12-30 16:18:05 -080080 Err(err) => {
81 cx.push(err);
82 break;
83 }
David Tolnayddf69e22020-05-10 22:08:20 -070084 }
David Tolnaya2dfcbf2020-12-21 15:46:24 -080085 } 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 }
David Tolnay067638e2020-12-30 16:18:05 -080093 Err(err) => {
94 cx.push(err);
95 break;
96 }
David Tolnaya2dfcbf2020-12-21 15:46:24 -080097 }
David Tolnay1039a242020-10-09 19:18:12 -070098 } else if attr.path.is_ident("cxx_name") {
David Tolnayed6ba4a2021-01-01 14:59:40 -080099 match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
David Tolnay1039a242020-10-09 19:18:12 -0700100 Ok(attr) => {
101 if let Some(cxx_name) = &mut parser.cxx_name {
102 **cxx_name = Some(attr);
103 continue;
104 }
105 }
David Tolnay067638e2020-12-30 16:18:05 -0800106 Err(err) => {
107 cx.push(err);
108 break;
109 }
David Tolnay1039a242020-10-09 19:18:12 -0700110 }
111 } else if attr.path.is_ident("rust_name") {
David Tolnayed6ba4a2021-01-01 14:59:40 -0800112 match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
David Tolnay1039a242020-10-09 19:18:12 -0700113 Ok(attr) => {
114 if let Some(rust_name) = &mut parser.rust_name {
115 **rust_name = Some(attr);
116 continue;
117 }
118 }
David Tolnay067638e2020-12-30 16:18:05 -0800119 Err(err) => {
120 cx.push(err);
121 break;
122 }
David Tolnay1039a242020-10-09 19:18:12 -0700123 }
David Tolnay20d980b2020-12-30 15:56:05 -0800124 } 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")
David Tolnay511c6fd2020-12-30 16:55:04 -0800128 || attr.path.is_ident("deprecated")
129 || attr.path.is_ident("must_use")
David Tolnay20d980b2020-12-30 15:56:05 -0800130 {
David Tolnay511c6fd2020-12-30 16:55:04 -0800131 // https://doc.rust-lang.org/reference/attributes/diagnostics.html
David Tolnay067638e2020-12-30 16:18:05 -0800132 passthrough_attrs.push(attr);
David Tolnay20d980b2020-12-30 15:56:05 -0800133 continue;
David Tolnaydc182792020-12-30 16:58:16 -0800134 } 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 }
David Tolnay7db73692019-10-20 14:51:12 -0400143 }
David Tolnay067638e2020-12-30 16:18:05 -0800144 cx.error(attr, "unsupported attribute");
145 break;
David Tolnay7db73692019-10-20 14:51:12 -0400146 }
David Tolnaycba3d442020-12-30 16:39:21 -0800147 OtherAttrs(passthrough_attrs)
David Tolnay7db73692019-10-20 14:51:12 -0400148}
149
150fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
151 input.parse::<Token![=]>()?;
152 let lit: LitStr = input.parse()?;
153 Ok(lit)
154}
155
David Tolnayf4b93342020-11-27 13:59:42 -0800156fn 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;
David Tolnaye86b9cf2020-05-10 14:24:29 -0700165 }
David Tolnayf4b93342020-11-27 13:59:42 -0800166 }
167 cx.error(path, "unsupported derive");
168 }
169 Ok(derives)
David Tolnay7db73692019-10-20 14:51:12 -0400170}
David Tolnayddf69e22020-05-10 22:08:20 -0700171
172fn 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 {
David Tolnay8155e582020-05-11 00:05:02 -0700177 U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
178 return Ok(atom);
179 }
David Tolnayddf69e22020-05-10 22:08:20 -0700180 _ => {}
181 }
182 }
183 Err(Error::new_spanned(
184 begin.token_stream(),
185 "unrecognized repr",
186 ))
187}
David Tolnay1039a242020-10-09 19:18:12 -0700188
David Tolnaya2dfcbf2020-12-21 15:46:24 -0800189fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
190 input.parse::<Token![=]>()?;
191 let namespace = input.parse::<Namespace>()?;
192 Ok(namespace)
193}
194
David Tolnayed6ba4a2021-01-01 14:59:40 -0800195fn 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
206fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
David Tolnay1039a242020-10-09 19:18:12 -0700207 input.parse::<Token![=]>()?;
208 if input.peek(LitStr) {
209 let lit: LitStr = input.parse()?;
210 lit.parse()
211 } else {
212 input.parse()
213 }
214}
David Tolnaycba3d442020-12-30 16:39:21 -0800215
216pub struct OtherAttrs(Vec<Attribute>);
217
218impl OtherAttrs {
219 pub fn none() -> Self {
220 OtherAttrs(Vec::new())
221 }
222}
223
224impl ToTokens for OtherAttrs {
225 fn to_tokens(&self, tokens: &mut TokenStream) {
226 for attr in &self.0 {
227 attr.to_tokens(tokens);
228 }
229 }
230}