blob: ccb1c511a12e34f92da1a8895909c522c24b54d2 [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 Tolnay7db73692019-10-20 14:51:12 -04004use crate::syntax::{Derive, Doc};
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 Tolnay1039a242020-10-09 19:18:12 -070034 pub cxx_name: Option<&'a mut Option<Ident>>,
35 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 Tolnaycba3d442020-12-30 16:39:21 -080043pub(super) 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") {
99 match parse_function_alias_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 }
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") {
112 match parse_function_alias_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 }
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 Tolnay7db73692019-10-20 14:51:12 -0400134 }
David Tolnay067638e2020-12-30 16:18:05 -0800135 cx.error(attr, "unsupported attribute");
136 break;
David Tolnay7db73692019-10-20 14:51:12 -0400137 }
David Tolnaycba3d442020-12-30 16:39:21 -0800138 OtherAttrs(passthrough_attrs)
David Tolnay7db73692019-10-20 14:51:12 -0400139}
140
141fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
142 input.parse::<Token![=]>()?;
143 let lit: LitStr = input.parse()?;
144 Ok(lit)
145}
146
David Tolnayf4b93342020-11-27 13:59:42 -0800147fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
148 let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
149
150 let mut derives = Vec::new();
151 for path in paths {
152 if let Some(ident) = path.get_ident() {
153 if let Some(derive) = Derive::from(ident) {
154 derives.push(derive);
155 continue;
David Tolnaye86b9cf2020-05-10 14:24:29 -0700156 }
David Tolnayf4b93342020-11-27 13:59:42 -0800157 }
158 cx.error(path, "unsupported derive");
159 }
160 Ok(derives)
David Tolnay7db73692019-10-20 14:51:12 -0400161}
David Tolnayddf69e22020-05-10 22:08:20 -0700162
163fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
164 let begin = input.cursor();
165 let ident: Ident = input.parse()?;
166 if let Some(atom) = Atom::from(&ident) {
167 match atom {
David Tolnay8155e582020-05-11 00:05:02 -0700168 U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
169 return Ok(atom);
170 }
David Tolnayddf69e22020-05-10 22:08:20 -0700171 _ => {}
172 }
173 }
174 Err(Error::new_spanned(
175 begin.token_stream(),
176 "unrecognized repr",
177 ))
178}
David Tolnay1039a242020-10-09 19:18:12 -0700179
David Tolnaya2dfcbf2020-12-21 15:46:24 -0800180fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
181 input.parse::<Token![=]>()?;
182 let namespace = input.parse::<Namespace>()?;
183 Ok(namespace)
184}
185
David Tolnay1039a242020-10-09 19:18:12 -0700186fn parse_function_alias_attribute(input: ParseStream) -> Result<Ident> {
187 input.parse::<Token![=]>()?;
188 if input.peek(LitStr) {
189 let lit: LitStr = input.parse()?;
190 lit.parse()
191 } else {
192 input.parse()
193 }
194}
David Tolnaycba3d442020-12-30 16:39:21 -0800195
196pub struct OtherAttrs(Vec<Attribute>);
197
198impl OtherAttrs {
199 pub fn none() -> Self {
200 OtherAttrs(Vec::new())
201 }
202}
203
204impl ToTokens for OtherAttrs {
205 fn to_tokens(&self, tokens: &mut TokenStream) {
206 for attr in &self.0 {
207 attr.to_tokens(tokens);
208 }
209 }
210}