blob: 9a6be92bd145a79f5e4d010b2f64c4e26270d73d [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")
128 {
129 // https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes
David Tolnay067638e2020-12-30 16:18:05 -0800130 passthrough_attrs.push(attr);
David Tolnay20d980b2020-12-30 15:56:05 -0800131 continue;
David Tolnay7db73692019-10-20 14:51:12 -0400132 }
David Tolnay067638e2020-12-30 16:18:05 -0800133 cx.error(attr, "unsupported attribute");
134 break;
David Tolnay7db73692019-10-20 14:51:12 -0400135 }
David Tolnaycba3d442020-12-30 16:39:21 -0800136 OtherAttrs(passthrough_attrs)
David Tolnay7db73692019-10-20 14:51:12 -0400137}
138
139fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
140 input.parse::<Token![=]>()?;
141 let lit: LitStr = input.parse()?;
142 Ok(lit)
143}
144
David Tolnayf4b93342020-11-27 13:59:42 -0800145fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
146 let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
147
148 let mut derives = Vec::new();
149 for path in paths {
150 if let Some(ident) = path.get_ident() {
151 if let Some(derive) = Derive::from(ident) {
152 derives.push(derive);
153 continue;
David Tolnaye86b9cf2020-05-10 14:24:29 -0700154 }
David Tolnayf4b93342020-11-27 13:59:42 -0800155 }
156 cx.error(path, "unsupported derive");
157 }
158 Ok(derives)
David Tolnay7db73692019-10-20 14:51:12 -0400159}
David Tolnayddf69e22020-05-10 22:08:20 -0700160
161fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
162 let begin = input.cursor();
163 let ident: Ident = input.parse()?;
164 if let Some(atom) = Atom::from(&ident) {
165 match atom {
David Tolnay8155e582020-05-11 00:05:02 -0700166 U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
167 return Ok(atom);
168 }
David Tolnayddf69e22020-05-10 22:08:20 -0700169 _ => {}
170 }
171 }
172 Err(Error::new_spanned(
173 begin.token_stream(),
174 "unrecognized repr",
175 ))
176}
David Tolnay1039a242020-10-09 19:18:12 -0700177
David Tolnaya2dfcbf2020-12-21 15:46:24 -0800178fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
179 input.parse::<Token![=]>()?;
180 let namespace = input.parse::<Namespace>()?;
181 Ok(namespace)
182}
183
David Tolnay1039a242020-10-09 19:18:12 -0700184fn parse_function_alias_attribute(input: ParseStream) -> Result<Ident> {
185 input.parse::<Token![=]>()?;
186 if input.peek(LitStr) {
187 let lit: LitStr = input.parse()?;
188 lit.parse()
189 } else {
190 input.parse()
191 }
192}
David Tolnaycba3d442020-12-30 16:39:21 -0800193
194pub struct OtherAttrs(Vec<Attribute>);
195
196impl OtherAttrs {
197 pub fn none() -> Self {
198 OtherAttrs(Vec::new())
199 }
200}
201
202impl ToTokens for OtherAttrs {
203 fn to_tokens(&self, tokens: &mut TokenStream) {
204 for attr in &self.0 {
205 attr.to_tokens(tokens);
206 }
207 }
208}