blob: 7f50dcb74942f71ceff2960e7bf8616014b124d0 [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 Tolnayddf69e22020-05-10 22:08:20 -07005use proc_macro2::Ident;
David Tolnayb129ea72020-05-10 14:29:30 -07006use syn::parse::{ParseStream, Parser as _};
David Tolnay7db73692019-10-20 14:51:12 -04007use syn::{Attribute, Error, LitStr, Path, Result, Token};
8
David Tolnayb11c9ef2020-12-21 16:26:22 -08009// Intended usage:
10//
11// let mut doc = Doc::new();
12// let mut cxx_name = None;
13// let mut rust_name = None;
14// /* ... */
15// attrs::parse(
16// cx,
17// &item.attrs,
18// attrs::Parser {
19// doc: Some(&mut doc),
20// cxx_name: Some(&mut cxx_name),
21// rust_name: Some(&mut rust_name),
22// /* ... */
23// ..Default::default()
24// },
25// );
26//
David Tolnayb129ea72020-05-10 14:29:30 -070027#[derive(Default)]
28pub struct Parser<'a> {
29 pub doc: Option<&'a mut Doc>,
30 pub derives: Option<&'a mut Vec<Derive>>,
David Tolnayddf69e22020-05-10 22:08:20 -070031 pub repr: Option<&'a mut Option<Atom>>,
David Tolnaya2dfcbf2020-12-21 15:46:24 -080032 pub namespace: Option<&'a mut Namespace>,
David Tolnay1039a242020-10-09 19:18:12 -070033 pub cxx_name: Option<&'a mut Option<Ident>>,
34 pub rust_name: Option<&'a mut Option<Ident>>,
David Tolnayd25033c2020-12-21 16:24:19 -080035
36 // Suppress clippy needless_update lint ("struct update has no effect, all
37 // the fields in the struct have already been specified") when preemptively
38 // writing `..Default::default()`.
39 pub(crate) _more: (),
David Tolnay7db73692019-10-20 14:51:12 -040040}
41
David Tolnay067638e2020-12-30 16:18:05 -080042pub(super) fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> Vec<Attribute> {
43 let mut passthrough_attrs = Vec::new();
David Tolnay7db73692019-10-20 14:51:12 -040044 for attr in attrs {
45 if attr.path.is_ident("doc") {
David Tolnay3e628882020-05-10 15:30:14 -070046 match parse_doc_attribute.parse2(attr.tokens.clone()) {
47 Ok(lit) => {
48 if let Some(doc) = &mut parser.doc {
49 doc.push(lit);
50 continue;
51 }
52 }
David Tolnay067638e2020-12-30 16:18:05 -080053 Err(err) => {
54 cx.push(err);
55 break;
56 }
David Tolnayb129ea72020-05-10 14:29:30 -070057 }
David Tolnay7db73692019-10-20 14:51:12 -040058 } else if attr.path.is_ident("derive") {
David Tolnayf4b93342020-11-27 13:59:42 -080059 match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
David Tolnay3e628882020-05-10 15:30:14 -070060 Ok(attr) => {
61 if let Some(derives) = &mut parser.derives {
62 derives.extend(attr);
63 continue;
64 }
65 }
David Tolnay067638e2020-12-30 16:18:05 -080066 Err(err) => {
67 cx.push(err);
68 break;
69 }
David Tolnay7db73692019-10-20 14:51:12 -040070 }
David Tolnayddf69e22020-05-10 22:08:20 -070071 } else if attr.path.is_ident("repr") {
72 match attr.parse_args_with(parse_repr_attribute) {
73 Ok(attr) => {
74 if let Some(repr) = &mut parser.repr {
75 **repr = Some(attr);
76 continue;
77 }
78 }
David Tolnay067638e2020-12-30 16:18:05 -080079 Err(err) => {
80 cx.push(err);
81 break;
82 }
David Tolnayddf69e22020-05-10 22:08:20 -070083 }
David Tolnaya2dfcbf2020-12-21 15:46:24 -080084 } else if attr.path.is_ident("namespace") {
85 match parse_namespace_attribute.parse2(attr.tokens.clone()) {
86 Ok(attr) => {
87 if let Some(namespace) = &mut parser.namespace {
88 **namespace = attr;
89 continue;
90 }
91 }
David Tolnay067638e2020-12-30 16:18:05 -080092 Err(err) => {
93 cx.push(err);
94 break;
95 }
David Tolnaya2dfcbf2020-12-21 15:46:24 -080096 }
David Tolnay1039a242020-10-09 19:18:12 -070097 } else if attr.path.is_ident("cxx_name") {
98 match parse_function_alias_attribute.parse2(attr.tokens.clone()) {
99 Ok(attr) => {
100 if let Some(cxx_name) = &mut parser.cxx_name {
101 **cxx_name = Some(attr);
102 continue;
103 }
104 }
David Tolnay067638e2020-12-30 16:18:05 -0800105 Err(err) => {
106 cx.push(err);
107 break;
108 }
David Tolnay1039a242020-10-09 19:18:12 -0700109 }
110 } else if attr.path.is_ident("rust_name") {
111 match parse_function_alias_attribute.parse2(attr.tokens.clone()) {
112 Ok(attr) => {
113 if let Some(rust_name) = &mut parser.rust_name {
114 **rust_name = Some(attr);
115 continue;
116 }
117 }
David Tolnay067638e2020-12-30 16:18:05 -0800118 Err(err) => {
119 cx.push(err);
120 break;
121 }
David Tolnay1039a242020-10-09 19:18:12 -0700122 }
David Tolnay20d980b2020-12-30 15:56:05 -0800123 } else if attr.path.is_ident("allow")
124 || attr.path.is_ident("warn")
125 || attr.path.is_ident("deny")
126 || attr.path.is_ident("forbid")
127 {
128 // https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes
David Tolnay067638e2020-12-30 16:18:05 -0800129 passthrough_attrs.push(attr);
David Tolnay20d980b2020-12-30 15:56:05 -0800130 continue;
David Tolnay7db73692019-10-20 14:51:12 -0400131 }
David Tolnay067638e2020-12-30 16:18:05 -0800132 cx.error(attr, "unsupported attribute");
133 break;
David Tolnay7db73692019-10-20 14:51:12 -0400134 }
David Tolnay067638e2020-12-30 16:18:05 -0800135 passthrough_attrs
David Tolnay7db73692019-10-20 14:51:12 -0400136}
137
138fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
139 input.parse::<Token![=]>()?;
140 let lit: LitStr = input.parse()?;
141 Ok(lit)
142}
143
David Tolnayf4b93342020-11-27 13:59:42 -0800144fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
145 let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
146
147 let mut derives = Vec::new();
148 for path in paths {
149 if let Some(ident) = path.get_ident() {
150 if let Some(derive) = Derive::from(ident) {
151 derives.push(derive);
152 continue;
David Tolnaye86b9cf2020-05-10 14:24:29 -0700153 }
David Tolnayf4b93342020-11-27 13:59:42 -0800154 }
155 cx.error(path, "unsupported derive");
156 }
157 Ok(derives)
David Tolnay7db73692019-10-20 14:51:12 -0400158}
David Tolnayddf69e22020-05-10 22:08:20 -0700159
160fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
161 let begin = input.cursor();
162 let ident: Ident = input.parse()?;
163 if let Some(atom) = Atom::from(&ident) {
164 match atom {
David Tolnay8155e582020-05-11 00:05:02 -0700165 U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
166 return Ok(atom);
167 }
David Tolnayddf69e22020-05-10 22:08:20 -0700168 _ => {}
169 }
170 }
171 Err(Error::new_spanned(
172 begin.token_stream(),
173 "unrecognized repr",
174 ))
175}
David Tolnay1039a242020-10-09 19:18:12 -0700176
David Tolnaya2dfcbf2020-12-21 15:46:24 -0800177fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
178 input.parse::<Token![=]>()?;
179 let namespace = input.parse::<Namespace>()?;
180 Ok(namespace)
181}
182
David Tolnay1039a242020-10-09 19:18:12 -0700183fn parse_function_alias_attribute(input: ParseStream) -> Result<Ident> {
184 input.parse::<Token![=]>()?;
185 if input.peek(LitStr) {
186 let lit: LitStr = input.parse()?;
187 lit.parse()
188 } else {
189 input.parse()
190 }
191}