1use std::cell::{Cell, RefCell};
2use std::collections::HashMap;
3
4use napi_derive_backend::{bail_span, BindgenResult, Diagnostic};
5use proc_macro2::{Delimiter, Ident, Span, TokenTree};
6use quote::ToTokens;
7use syn::parse::{Parse, ParseStream};
8use syn::spanned::Spanned;
9use syn::Attribute;
10
11use crate::parser::AnyIdent;
12
13thread_local! {
14 static ATTRS: AttributeParseState = Default::default();
15 static STRUCTS: StructParseState = Default::default();
16}
17
18#[derive(Default)]
19struct StructParseState {
20 parsed: RefCell<HashMap<String, ParsedStruct>>,
21}
22
23struct ParsedStruct {
24 js_name: String,
25 ctor_defined: bool,
26}
27
28#[derive(Default)]
29struct AttributeParseState {
30 parsed: Cell<usize>,
31 #[allow(unused)]
32 checks: Cell<usize>,
33}
34
35#[derive(Debug)]
36/// Parsed attributes from a `#[napi(..)]`.
37pub struct BindgenAttrs {
38 /// Whether `#[napi]` attribute exists
39 pub exists: bool,
40 /// List of parsed attributes
41 pub attrs: Vec<(Cell<bool>, BindgenAttr)>,
42 /// Span of original attribute
43 pub span: Span,
44}
45
46// NOTE: borrowed from wasm-bindgen
47// some of them may useless in #[napi] macro
48macro_rules! attrgen {
49 ($mac:ident) => {
50 $mac! {
51 (catch_unwind, CatchUnwind(Span)),
52 (js_name, JsName(Span, String, Span)),
53 (constructor, Constructor(Span)),
54 (factory, Factory(Span)),
55 (getter, Getter(Span, Option<Ident>)),
56 (setter, Setter(Span, Option<Ident>)),
57 (readonly, Readonly(Span)),
58 (enumerable, Enumerable(Span, Option<bool>), true),
59 (writable, Writable(Span, Option<bool>), true),
60 (configurable, Configurable(Span, Option<bool>), true),
61 (skip, Skip(Span)),
62 (strict, Strict(Span)),
63 (return_if_invalid, ReturnIfInvalid(Span)),
64 (object, Object(Span)),
65 (object_from_js, ObjectFromJs(Span, Option<bool>), true),
66 (object_to_js, ObjectToJs(Span, Option<bool>), true),
67 (custom_finalize, CustomFinalize(Span)),
68 (namespace, Namespace(Span, String, Span)),
69 (iterator, Iterator(Span)),
70 (ts_args_type, TsArgsType(Span, String, Span)),
71 (ts_return_type, TsReturnType(Span, String, Span)),
72 (ts_type, TsType(Span, String, Span)),
73 (ts_generic_types, TsGenericTypes(Span, String, Span)),
74 (string_enum, StringEnum(Span, Option<(String, Span)>)),
75 (use_nullable, UseNullable(Span, Option<bool>), false),
76
77 // impl later
78 // (inspectable, Inspectable(Span)),
79 // (typescript_custom_section, TypescriptCustomSection(Span)),
80 (skip_typescript, SkipTypescript(Span)),
81 // (getter_with_clone, GetterWithClone(Span)),
82
83 // For testing purposes only.
84 // (assert_no_shim, AssertNoShim(Span)),
85 }
86 };
87}
88
89macro_rules! methods {
90 ($(($name:ident, $variant:ident($($contents:tt)*) $($extra_tokens:tt)*),)*) => {
91 $(methods!(@method $name, $variant($($contents)*) $($extra_tokens)*);)*
92
93 #[cfg(feature = "strict")]
94 #[allow(unused)]
95 pub fn check_used(&self) -> Result<(), Diagnostic> {
96 // Account for the fact this method was called
97 ATTRS.with(|state| state.checks.set(state.checks.get() + 1));
98
99 let mut errors = Vec::new();
100 for (used, attr) in self.attrs.iter() {
101 if used.get() {
102 continue
103 }
104 let span = match attr {
105 $(BindgenAttr::$variant(span, ..) => span,)*
106 };
107 errors.push(Diagnostic::span_error(*span, "unused #[napi] attribute"));
108 }
109 Diagnostic::from_vec(errors)
110 }
111
112 #[cfg(not(feature = "strict"))]
113 pub fn check_used(&self) -> Result<(), Diagnostic> {
114 // Account for the fact this method was called
115 ATTRS.with(|state| state.checks.set(state.checks.get() + 1));
116 Ok(())
117 }
118 };
119
120 (@method $name:ident, $variant:ident(Span, String, Span)) => {
121 pub fn $name(&self) -> Option<(&str, Span)> {
122 self.attrs
123 .iter()
124 .filter_map(|a| match &a.1 {
125 BindgenAttr::$variant(_, s, span) => {
126 a.0.set(true);
127 Some((&s[..], *span))
128 }
129 _ => None,
130 })
131 .next()
132 }
133 };
134
135 (@method $name:ident, $variant:ident(Span, Option<(String, Span)>)) => {
136 pub fn $name(&self) -> Option<Option<&(String, Span)>> {
137 self.attrs
138 .iter()
139 .filter_map(|a| match &a.1 {
140 BindgenAttr::$variant(_, s) => {
141 a.0.set(true);
142 Some(s.as_ref())
143 }
144 _ => None,
145 })
146 .next()
147 }
148 };
149
150 (@method $name:ident, $variant:ident(Span, Option<bool>), $default_value:literal) => {
151 pub fn $name(&self) -> bool {
152 self.attrs
153 .iter()
154 .filter_map(|a| match &a.1 {
155 BindgenAttr::$variant(_, s) => {
156 a.0.set(true);
157 *s
158 }
159 _ => None,
160 })
161 .next()
162 .unwrap_or($default_value)
163 }
164 };
165
166 (@method $name:ident, $variant:ident(Span, Vec<String>, Vec<Span>)) => {
167 pub fn $name(&self) -> Option<(&[String], &[Span])> {
168 self.attrs
169 .iter()
170 .filter_map(|a| match &a.1 {
171 BindgenAttr::$variant(_, ss, spans) => {
172 a.0.set(true);
173 Some((&ss[..], &spans[..]))
174 }
175 _ => None,
176 })
177 .next()
178 }
179 };
180
181 (@method $name:ident, $variant:ident(Span, $($other:tt)*)) => {
182 #[allow(unused)]
183 pub fn $name(&self) -> Option<&$($other)*> {
184 self.attrs
185 .iter()
186 .filter_map(|a| match &a.1 {
187 BindgenAttr::$variant(_, s) => {
188 a.0.set(true);
189 Some(s)
190 }
191 _ => None,
192 })
193 .next()
194 }
195 };
196
197 (@method $name:ident, $variant:ident($($other:tt)*)) => {
198 #[allow(unused)]
199 pub fn $name(&self) -> Option<&$($other)*> {
200 self.attrs
201 .iter()
202 .filter_map(|a| match &a.1 {
203 BindgenAttr::$variant(s) => {
204 a.0.set(true);
205 Some(s)
206 }
207 _ => None,
208 })
209 .next()
210 }
211 };
212}
213
214impl BindgenAttrs {
215 /// Find and parse the napi attributes.
216 pub fn find(attrs: &mut Vec<syn::Attribute>) -> Result<BindgenAttrs, Diagnostic> {
217 for (index: usize, attr: &Attribute) in attrs.iter().enumerate() {
218 let attr: BindgenAttrs = BindgenAttrs::try_from(attr)?;
219 if attr.exists {
220 attrs.remove(index);
221
222 return Ok(attr);
223 }
224 }
225
226 Ok(BindgenAttrs::default())
227 }
228
229 attrgen!(methods);
230}
231
232impl TryFrom<&Attribute> for BindgenAttrs {
233 type Error = Diagnostic;
234
235 fn try_from(attr: &Attribute) -> Result<Self, Self::Error> {
236 let mut ret = BindgenAttrs {
237 exists: false,
238 attrs: vec![],
239 span: Span::call_site(),
240 };
241
242 if attr.path().is_ident("napi") {
243 ret.exists = true;
244 ret.span = attr.span();
245
246 let tts = attr.meta.to_token_stream().into_iter();
247 let group = match tts.last() {
248 // #[napi(xxx)]
249 // ^^^^^^^^^
250 Some(TokenTree::Group(d)) => d,
251 // #[napi]
252 // ^^^^
253 Some(TokenTree::Ident(_)) => parse_quote!(()),
254 _ => bail_span!(attr, "invalid #[napi] attribute"),
255 };
256
257 if group.delimiter() != Delimiter::Parenthesis {
258 bail_span!(attr, "malformed #[napi] attribute");
259 }
260
261 let mut attrs: BindgenAttrs = syn::parse2(group.stream())?;
262 ret.attrs.append(&mut attrs.attrs);
263 }
264
265 Ok(ret)
266 }
267}
268
269impl Default for BindgenAttrs {
270 fn default() -> BindgenAttrs {
271 // Add 1 to the list of parsed attribute sets. We'll use this counter to
272 // sanity check that we call `check_used` an appropriate number of
273 // times.
274 ATTRS.with(|state: &AttributeParseState| state.parsed.set(val:state.parsed.get() + 1));
275 BindgenAttrs {
276 span: Span::call_site(),
277 attrs: Vec::new(),
278 exists: false,
279 }
280 }
281}
282
283macro_rules! gen_bindgen_attr {
284 ($( ($method:ident, $variant:ident($($associated_data:tt)*) $($extra_tokens:tt)*) ,)*) => {
285 /// The possible attributes in the `#[napi]`.
286 #[derive(Debug)]
287 pub enum BindgenAttr {
288 $($variant($($associated_data)*)),*
289 }
290 }
291}
292
293attrgen!(gen_bindgen_attr);
294
295pub fn record_struct(ident: &Ident, js_name: String, opts: &BindgenAttrs) {
296 STRUCTS.with(|state: &StructParseState| {
297 let struct_name: String = ident.to_string();
298
299 let mut map: RefMut<'_, HashMap> = state.parsed.borrow_mut();
300
301 map.insert(
302 k:struct_name,
303 v:ParsedStruct {
304 js_name,
305 ctor_defined: opts.constructor().is_some(),
306 },
307 );
308 });
309}
310
311pub fn check_recorded_struct_for_impl(ident: &Ident, opts: &BindgenAttrs) -> BindgenResult<String> {
312 STRUCTS.with(|state| {
313 let struct_name = ident.to_string();
314 let mut map = state.parsed.borrow_mut();
315 if let Some(parsed) = map.get_mut(&struct_name) {
316 if opts.constructor().is_some() && !cfg!(debug_assertions) {
317 if parsed.ctor_defined {
318 bail_span!(
319 ident,
320 "Constructor has already been defined for struct `{}`",
321 &struct_name
322 );
323 } else {
324 parsed.ctor_defined = true;
325 }
326 }
327
328 Ok(parsed.js_name.clone())
329 } else {
330 bail_span!(
331 ident,
332 "Did not find struct `{}` parsed before expand #[napi] for impl",
333 &struct_name,
334 )
335 }
336 })
337}
338
339impl Parse for BindgenAttrs {
340 fn parse(input: ParseStream) -> syn::Result<Self> {
341 let mut attrs: BindgenAttrs = BindgenAttrs::default();
342 if input.is_empty() {
343 return Ok(attrs);
344 }
345
346 let opts: Punctuated = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?;
347 attrs.attrs = opts.into_iter().map(|c: BindgenAttr| (Cell::new(false), c)).collect();
348 Ok(attrs)
349 }
350}
351
352impl Parse for BindgenAttr {
353 fn parse(input: ParseStream) -> syn::Result<Self> {
354 let original = input.fork();
355 let attr: AnyIdent = input.parse()?;
356 let attr = attr.0;
357 let attr_span = attr.span();
358 let attr_string = attr.to_string();
359 let raw_attr_string = format!("r#{}", attr_string);
360
361 macro_rules! parsers {
362 ($(($name:ident, $($contents:tt)*),)*) => {
363 $(
364 if attr_string == stringify!($name) || raw_attr_string == stringify!($name) {
365 parsers!(
366 @parser
367 $($contents)*
368 );
369 }
370 )*
371 };
372
373 (@parser $variant:ident(Span)) => ({
374 return Ok(BindgenAttr::$variant(attr_span));
375 });
376
377 (@parser $variant:ident(Span, Ident)) => ({
378 input.parse::<Token![=]>()?;
379 let ident = input.parse::<AnyIdent>()?.0;
380 return Ok(BindgenAttr::$variant(attr_span, ident))
381 });
382
383 (@parser $variant:ident(Span, Option<Ident>)) => ({
384 if input.parse::<Token![=]>().is_ok() {
385 let ident = input.parse::<AnyIdent>()?.0;
386 return Ok(BindgenAttr::$variant(attr_span, Some(ident)))
387 } else {
388 return Ok(BindgenAttr::$variant(attr_span, None));
389 }
390 });
391
392 (@parser $variant:ident(Span, syn::Path)) => ({
393 input.parse::<Token![=]>()?;
394 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
395 });
396
397 (@parser $variant:ident(Span, syn::Expr)) => ({
398 input.parse::<Token![=]>()?;
399 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
400 });
401
402 (@parser $variant:ident(Span, String, Span)) => ({
403 input.parse::<Token![=]>()?;
404 let (val, span) = match input.parse::<syn::LitStr>() {
405 Ok(str) => (str.value(), str.span()),
406 Err(_) => {
407 let ident = input.parse::<AnyIdent>()?.0;
408 (ident.to_string(), ident.span())
409 }
410 };
411 return Ok(BindgenAttr::$variant(attr_span, val, span))
412 });
413
414 (@parser $variant:ident(Span, Option<(String, Span)>)) => ({
415 if let Ok(_) = input.parse::<Token![=]>() {
416 let val = match input.parse::<syn::LitStr>() {
417 Ok(str) => Some((str.value(), str.span())),
418 Err(_) => {
419 let ident = input.parse::<AnyIdent>()?.0;
420 Some((ident.to_string(), ident.span()))
421 }
422 };
423 return Ok(BindgenAttr::$variant(attr_span, val))
424 } else {
425 return Ok(BindgenAttr::$variant(attr_span, None))
426 }
427 });
428
429 (@parser $variant:ident(Span, Option<bool>), $default_value:literal) => ({
430 if let Ok(_) = input.parse::<Token![=]>() {
431 let (val, _) = match input.parse::<syn::LitBool>() {
432 Ok(str) => (str.value(), str.span()),
433 Err(_) => {
434 let ident = input.parse::<AnyIdent>()?.0;
435 (true, ident.span())
436 }
437 };
438 return Ok::<BindgenAttr, syn::Error>(BindgenAttr::$variant(attr_span, Some(val)))
439 } else {
440 return Ok(BindgenAttr::$variant(attr_span, Some($default_value)))
441 }
442 });
443
444 (@parser $variant:ident(Span, Vec<String>, Vec<Span>)) => ({
445 input.parse::<Token![=]>()?;
446 let (vals, spans) = match input.parse::<syn::ExprArray>() {
447 Ok(exprs) => {
448 let mut vals = vec![];
449 let mut spans = vec![];
450
451 for expr in exprs.elems.iter() {
452 if let syn::Expr::Lit(syn::ExprLit {
453 lit: syn::Lit::Str(ref str),
454 ..
455 }) = expr {
456 vals.push(str.value());
457 spans.push(str.span());
458 } else {
459 return Err(syn::Error::new(expr.span(), "expected string literals"));
460 }
461 }
462
463 (vals, spans)
464 },
465 Err(_) => {
466 let ident = input.parse::<AnyIdent>()?.0;
467 (vec![ident.to_string()], vec![ident.span()])
468 }
469 };
470 return Ok(BindgenAttr::$variant(attr_span, vals, spans))
471 });
472 }
473
474 attrgen!(parsers);
475
476 Err(original.error("unknown attribute"))
477 }
478}
479