1use std::cell::Cell;
2use std::collections::HashMap;
3use std::sync::{
4 atomic::{AtomicUsize, Ordering},
5 Mutex, OnceLock,
6};
7
8use napi_derive_backend::{bail_span, BindgenResult, Diagnostic};
9use proc_macro2::{Delimiter, Ident, Span, TokenTree};
10use quote::ToTokens;
11use syn::parse::{Parse, ParseStream};
12use syn::spanned::Spanned;
13use syn::{parse_quote, Attribute, Token};
14
15use crate::parser::AnyIdent;
16
17static ATTRS: OnceLock<AttributeParseState> = OnceLock::new();
18static STRUCTS: OnceLock<StructParseState> = OnceLock::new();
19
20#[derive(Default)]
21struct StructParseState {
22 parsed: Mutex<HashMap<String, ParsedStruct>>,
23}
24
25struct ParsedStruct {
26 js_name: String,
27 ctor_defined: bool,
28}
29
30#[derive(Default)]
31struct AttributeParseState {
32 parsed: AtomicUsize,
33 #[allow(unused)]
34 checks: AtomicUsize,
35}
36
37#[derive(Debug)]
38pub struct BindgenAttrs {
40 pub exists: bool,
42 pub attrs: Vec<(Cell<bool>, BindgenAttr)>,
44 pub span: Span,
46}
47
48macro_rules! attrgen {
51 ($mac:ident) => {
52 $mac! {
53 (catch_unwind, CatchUnwind(Span)),
54 (async_runtime, AsyncRuntime(Span)),
55 (module_exports, ModuleExports(Span)),
56 (js_name, JsName(Span, String, Span)),
57 (constructor, Constructor(Span)),
58 (factory, Factory(Span)),
59 (getter, Getter(Span, Option<Ident>)),
60 (setter, Setter(Span, Option<Ident>)),
61 (readonly, Readonly(Span)),
62 (enumerable, Enumerable(Span, Option<bool>), true),
63 (writable, Writable(Span, Option<bool>), true),
64 (configurable, Configurable(Span, Option<bool>), true),
65 (skip, Skip(Span)),
66 (strict, Strict(Span)),
67 (return_if_invalid, ReturnIfInvalid(Span)),
68 (object, Object(Span)),
69 (object_from_js, ObjectFromJs(Span, Option<bool>), true),
70 (object_to_js, ObjectToJs(Span, Option<bool>), true),
71 (custom_finalize, CustomFinalize(Span)),
72 (namespace, Namespace(Span, String, Span)),
73 (iterator, Iterator(Span)),
74 (async_iterator, AsyncIterator(Span)),
75 (ts_args_type, TsArgsType(Span, String, Span)),
76 (ts_return_type, TsReturnType(Span, String, Span)),
77 (ts_type, TsType(Span, String, Span)),
78 (ts_generic_types, TsGenericTypes(Span, String, Span)),
79 (string_enum, StringEnum(Span, Option<(String, Span)>)),
80 (use_nullable, UseNullable(Span, Option<bool>), false),
81 (discriminant, Discriminant(Span, String, Span)),
82 (discriminant_case, DiscriminantCase(Span, String, Span)),
83 (transparent, Transparent(Span)),
84 (array, Array(Span)),
85 (no_export, NoExport(Span)),
86
87 (skip_typescript, SkipTypescript(Span)),
91 }
96 };
97}
98
99macro_rules! methods {
100 ($(($name:ident, $variant:ident($($contents:tt)*) $($extra_tokens:tt)*),)*) => {
101 $(methods!(@method $name, $variant($($contents)*) $($extra_tokens)*);)*
102
103 #[cfg(feature = "strict")]
104 #[allow(unused)]
105 pub fn check_used(&self) -> Result<(), Diagnostic> {
106 let attrs = ATTRS.get_or_init(|| AttributeParseState::default());
108 attrs.checks.fetch_add(1, Ordering::SeqCst);
109
110 let mut errors = Vec::new();
111 for (used, attr) in self.attrs.iter() {
112 if used.get() {
113 continue
114 }
115 let span = match attr {
116 $(BindgenAttr::$variant(span, ..) => span,)*
117 };
118 errors.push(Diagnostic::span_error(*span, "unused #[napi] attribute"));
119 }
120 Diagnostic::from_vec(errors)
121 }
122
123 #[cfg(not(feature = "strict"))]
124 #[allow(unused)]
125 pub fn check_used(&self) -> Result<(), Diagnostic> {
126 let attrs = ATTRS.get_or_init(AttributeParseState::default);
128 attrs.checks.fetch_add(1, Ordering::SeqCst);
129 Ok(())
130 }
131 };
132
133 (@method $name:ident, $variant:ident(Span, String, Span)) => {
134 #[allow(unused)]
135 pub fn $name(&self) -> Option<(&str, Span)> {
136 self.attrs
137 .iter()
138 .filter_map(|a| match &a.1 {
139 BindgenAttr::$variant(_, s, span) => {
140 a.0.set(true);
141 Some((&s[..], *span))
142 }
143 _ => None,
144 })
145 .next()
146 }
147 };
148
149 (@method $name:ident, $variant:ident(Span, Option<(String, Span)>)) => {
150 #[allow(unused)]
151 pub fn $name(&self) -> Option<Option<&(String, Span)>> {
152 self.attrs
153 .iter()
154 .filter_map(|a| match &a.1 {
155 BindgenAttr::$variant(_, s) => {
156 a.0.set(true);
157 Some(s.as_ref())
158 }
159 _ => None,
160 })
161 .next()
162 }
163 };
164
165 (@method $name:ident, $variant:ident(Span, Option<bool>), $default_value:literal) => {
166 #[allow(unused)]
167 pub fn $name(&self) -> bool {
168 self.attrs
169 .iter()
170 .filter_map(|a| match &a.1 {
171 BindgenAttr::$variant(_, s) => {
172 a.0.set(true);
173 *s
174 }
175 _ => None,
176 })
177 .next()
178 .unwrap_or($default_value)
179 }
180 };
181
182 (@method $name:ident, $variant:ident(Span, Vec<String>, Vec<Span>)) => {
183 #[allow(unused)]
184 pub fn $name(&self) -> Option<(&[String], &[Span])> {
185 self.attrs
186 .iter()
187 .filter_map(|a| match &a.1 {
188 BindgenAttr::$variant(_, ss, spans) => {
189 a.0.set(true);
190 Some((&ss[..], &spans[..]))
191 }
192 _ => None,
193 })
194 .next()
195 }
196 };
197
198 (@method $name:ident, $variant:ident(Span, $($other:tt)*)) => {
199 #[allow(unused)]
200 pub fn $name(&self) -> Option<&$($other)*> {
201 self.attrs
202 .iter()
203 .filter_map(|a| match &a.1 {
204 BindgenAttr::$variant(_, s) => {
205 a.0.set(true);
206 Some(s)
207 }
208 _ => None,
209 })
210 .next()
211 }
212 };
213
214 (@method $name:ident, $variant:ident($($other:tt)*)) => {
215 #[allow(unused)]
216 pub fn $name(&self) -> Option<&$($other)*> {
217 self.attrs
218 .iter()
219 .filter_map(|a| match &a.1 {
220 BindgenAttr::$variant(s) => {
221 a.0.set(true);
222 Some(s)
223 }
224 _ => None,
225 })
226 .next()
227 }
228 };
229}
230
231impl BindgenAttrs {
232 pub fn find(attrs: &mut Vec<syn::Attribute>) -> Result<BindgenAttrs, Diagnostic> {
234 for (index, attr) in attrs.iter().enumerate() {
235 let attr = BindgenAttrs::try_from(attr)?;
236 if attr.exists {
237 attrs.remove(index);
238
239 return Ok(attr);
240 }
241 }
242
243 Ok(BindgenAttrs::default())
244 }
245
246 attrgen!(methods);
247}
248
249impl TryFrom<&Attribute> for BindgenAttrs {
250 type Error = Diagnostic;
251
252 fn try_from(attr: &Attribute) -> Result<Self, Self::Error> {
253 let mut ret = BindgenAttrs {
254 exists: false,
255 attrs: vec![],
256 span: Span::call_site(),
257 };
258
259 let is_napi =
260 attr.path().segments.last().map(|s| s.ident.to_string()) == Some("napi".to_string());
261 let is_cfg_attr = attr
262 .meta
263 .path()
264 .segments
265 .first()
266 .map(|s| s.ident.to_string())
267 == Some("cfg_attr".to_string());
268
269 if is_napi {
270 ret.exists = true;
271 ret.span = attr.span();
272
273 let tts = attr.meta.to_token_stream().into_iter();
274 let group = match tts.last() {
275 Some(TokenTree::Group(d)) => d,
278 Some(TokenTree::Ident(_)) => parse_quote!(()),
281 _ => bail_span!(attr, "invalid #[napi] attribute"),
282 };
283
284 if group.delimiter() != Delimiter::Parenthesis {
285 bail_span!(attr, "malformed #[napi] attribute");
286 }
287
288 let mut attrs: BindgenAttrs = syn::parse2(group.stream())?;
289 ret.attrs.append(&mut attrs.attrs);
290 }
291
292 if is_cfg_attr {
293 let cfg_attr_list = attr.meta.require_list()?;
294 let mut args_iter = cfg_attr_list
297 .parse_args_with(syn::punctuated::Punctuated::<syn::Meta, Token![,]>::parse_terminated)?
298 .into_iter();
299 if let Some(arg) = args_iter.next_back() {
300 if arg.path().segments.last().map(|s| s.ident.to_string()) == Some("napi".to_string()) {
301 ret.exists = true;
302 ret.span = arg.span();
303 let tts = arg.to_token_stream().into_iter();
304 let group = match tts.last() {
305 Some(TokenTree::Group(d)) => d,
308 Some(TokenTree::Ident(_)) => parse_quote!(()),
311 _ => bail_span!(attr, "invalid #[napi] attribute"),
312 };
313
314 if group.delimiter() != Delimiter::Parenthesis {
315 bail_span!(attr, "malformed #[napi] attribute");
316 }
317
318 let mut attrs: BindgenAttrs = syn::parse2(group.stream())?;
319 ret.attrs.append(&mut attrs.attrs);
320 }
321 }
322 }
323
324 Ok(ret)
325 }
326}
327
328impl Default for BindgenAttrs {
329 fn default() -> BindgenAttrs {
330 let attrs = ATTRS.get_or_init(AttributeParseState::default);
334 attrs.parsed.fetch_add(1, Ordering::SeqCst);
335 BindgenAttrs {
336 span: Span::call_site(),
337 attrs: Vec::new(),
338 exists: false,
339 }
340 }
341}
342
343macro_rules! gen_bindgen_attr {
344 ($( ($method:ident, $variant:ident($($associated_data:tt)*) $($extra_tokens:tt)*) ,)*) => {
345 #[derive(Debug)]
347 #[allow(unused)]
348 pub enum BindgenAttr {
349 $($variant($($associated_data)*)),*
350 }
351 }
352}
353
354attrgen!(gen_bindgen_attr);
355
356pub fn record_struct(ident: &Ident, js_name: String, opts: &BindgenAttrs) {
357 let state = STRUCTS.get_or_init(StructParseState::default);
358 let mut map = state.parsed.lock().unwrap();
359 let struct_name = ident.to_string();
360
361 map.insert(
362 struct_name,
363 ParsedStruct {
364 js_name,
365 ctor_defined: opts.constructor().is_some(),
366 },
367 );
368}
369
370pub fn check_recorded_struct_for_impl(ident: &Ident, opts: &BindgenAttrs) -> BindgenResult<String> {
371 let state = STRUCTS.get_or_init(StructParseState::default);
372 let mut map = state.parsed.lock().unwrap();
373 let struct_name = ident.to_string();
374 if let Some(parsed) = map.get_mut(&struct_name) {
375 if opts.constructor().is_some() && !cfg!(debug_assertions) {
376 if parsed.ctor_defined {
377 bail_span!(
378 ident,
379 "Constructor has already been defined for struct `{}`",
380 &struct_name
381 );
382 } else {
383 parsed.ctor_defined = true;
384 }
385 }
386
387 Ok(parsed.js_name.clone())
388 } else {
389 bail_span!(
390 ident,
391 "Did not find struct `{}` parsed before expand #[napi] for impl",
392 &struct_name,
393 )
394 }
395}
396
397impl Parse for BindgenAttrs {
398 fn parse(input: ParseStream) -> syn::Result<Self> {
399 let mut attrs = BindgenAttrs::default();
400 if input.is_empty() {
401 return Ok(attrs);
402 }
403
404 let opts = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?;
405 attrs.attrs = opts.into_iter().map(|c| (Cell::new(false), c)).collect();
406 Ok(attrs)
407 }
408}
409
410impl Parse for BindgenAttr {
411 fn parse(input: ParseStream) -> syn::Result<Self> {
412 let original = input.fork();
413 let attr: AnyIdent = input.parse()?;
414 let attr = attr.0;
415 let attr_span = attr.span();
416 let attr_string = attr.to_string();
417 let raw_attr_string = format!("r#{attr_string}");
418
419 macro_rules! parsers {
420 ($(($name:ident, $($contents:tt)*),)*) => {
421 $(
422 if attr_string == stringify!($name) || raw_attr_string == stringify!($name) {
423 parsers!(
424 @parser
425 $($contents)*
426 );
427 }
428 )*
429 };
430
431 (@parser $variant:ident(Span)) => ({
432 return Ok(BindgenAttr::$variant(attr_span));
433 });
434
435 (@parser $variant:ident(Span, Ident)) => ({
436 input.parse::<Token![=]>()?;
437 let ident = input.parse::<AnyIdent>()?.0;
438 return Ok(BindgenAttr::$variant(attr_span, ident))
439 });
440
441 (@parser $variant:ident(Span, Option<Ident>)) => ({
442 if input.parse::<Token![=]>().is_ok() {
443 let ident = input.parse::<AnyIdent>()?.0;
444 return Ok(BindgenAttr::$variant(attr_span, Some(ident)))
445 } else {
446 return Ok(BindgenAttr::$variant(attr_span, None));
447 }
448 });
449
450 (@parser $variant:ident(Span, syn::Path)) => ({
451 input.parse::<Token![=]>()?;
452 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
453 });
454
455 (@parser $variant:ident(Span, syn::Expr)) => ({
456 input.parse::<Token![=]>()?;
457 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
458 });
459
460 (@parser $variant:ident(Span, String, Span)) => ({
461 input.parse::<Token![=]>()?;
462 let (val, span) = match input.parse::<syn::LitStr>() {
463 Ok(str) => (str.value(), str.span()),
464 Err(_) => {
465 let ident = input.parse::<AnyIdent>()?.0;
466 (ident.to_string(), ident.span())
467 }
468 };
469 return Ok(BindgenAttr::$variant(attr_span, val, span))
470 });
471
472 (@parser $variant:ident(Span, Option<(String, Span)>)) => ({
473 if let Ok(_) = input.parse::<Token![=]>() {
474 let val = match input.parse::<syn::LitStr>() {
475 Ok(str) => Some((str.value(), str.span())),
476 Err(_) => {
477 let ident = input.parse::<AnyIdent>()?.0;
478 Some((ident.to_string(), ident.span()))
479 }
480 };
481 return Ok(BindgenAttr::$variant(attr_span, val))
482 } else {
483 return Ok(BindgenAttr::$variant(attr_span, None))
484 }
485 });
486
487 (@parser $variant:ident(Span, Option<bool>), $default_value:literal) => ({
488 if let Ok(_) = input.parse::<Token![=]>() {
489 let (val, _) = match input.parse::<syn::LitBool>() {
490 Ok(str) => (str.value(), str.span()),
491 Err(_) => {
492 let ident = input.parse::<AnyIdent>()?.0;
493 (true, ident.span())
494 }
495 };
496 return Ok::<BindgenAttr, syn::Error>(BindgenAttr::$variant(attr_span, Some(val)))
497 } else {
498 return Ok(BindgenAttr::$variant(attr_span, Some($default_value)))
499 }
500 });
501
502 (@parser $variant:ident(Span, Vec<String>, Vec<Span>)) => ({
503 input.parse::<Token![=]>()?;
504 let (vals, spans) = match input.parse::<syn::ExprArray>() {
505 Ok(exprs) => {
506 let mut vals = vec![];
507 let mut spans = vec![];
508
509 for expr in exprs.elems.iter() {
510 if let syn::Expr::Lit(syn::ExprLit {
511 lit: syn::Lit::Str(ref str),
512 ..
513 }) = expr {
514 vals.push(str.value());
515 spans.push(str.span());
516 } else {
517 return Err(syn::Error::new(expr.span(), "expected string literals"));
518 }
519 }
520
521 (vals, spans)
522 },
523 Err(_) => {
524 let ident = input.parse::<AnyIdent>()?.0;
525 (vec![ident.to_string()], vec![ident.span()])
526 }
527 };
528 return Ok(BindgenAttr::$variant(attr_span, vals, spans))
529 });
530 }
531
532 attrgen!(parsers);
533
534 Err(original.error("unknown attribute"))
535 }
536}