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::Attribute;
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 (ts_args_type, TsArgsType(Span, String, Span)),
75 (ts_return_type, TsReturnType(Span, String, Span)),
76 (ts_type, TsType(Span, String, Span)),
77 (ts_generic_types, TsGenericTypes(Span, String, Span)),
78 (string_enum, StringEnum(Span, Option<(String, Span)>)),
79 (use_nullable, UseNullable(Span, Option<bool>), false),
80 (discriminant, Discriminant(Span, String, Span)),
81 (discriminant_case, DiscriminantCase(Span, String, Span)),
82 (transparent, Transparent(Span)),
83 (array, Array(Span)),
84 (no_export, NoExport(Span)),
85
86 (skip_typescript, SkipTypescript(Span)),
90 }
95 };
96}
97
98macro_rules! methods {
99 ($(($name:ident, $variant:ident($($contents:tt)*) $($extra_tokens:tt)*),)*) => {
100 $(methods!(@method $name, $variant($($contents)*) $($extra_tokens)*);)*
101
102 #[cfg(feature = "strict")]
103 #[allow(unused)]
104 pub fn check_used(&self) -> Result<(), Diagnostic> {
105 let attrs = ATTRS.get_or_init(|| AttributeParseState::default());
107 attrs.checks.fetch_add(1, Ordering::SeqCst);
108
109 let mut errors = Vec::new();
110 for (used, attr) in self.attrs.iter() {
111 if used.get() {
112 continue
113 }
114 let span = match attr {
115 $(BindgenAttr::$variant(span, ..) => span,)*
116 };
117 errors.push(Diagnostic::span_error(*span, "unused #[napi] attribute"));
118 }
119 Diagnostic::from_vec(errors)
120 }
121
122 #[cfg(not(feature = "strict"))]
123 #[allow(unused)]
124 pub fn check_used(&self) -> Result<(), Diagnostic> {
125 let attrs = ATTRS.get_or_init(AttributeParseState::default);
127 attrs.checks.fetch_add(1, Ordering::SeqCst);
128 Ok(())
129 }
130 };
131
132 (@method $name:ident, $variant:ident(Span, String, Span)) => {
133 #[allow(unused)]
134 pub fn $name(&self) -> Option<(&str, Span)> {
135 self.attrs
136 .iter()
137 .filter_map(|a| match &a.1 {
138 BindgenAttr::$variant(_, s, span) => {
139 a.0.set(true);
140 Some((&s[..], *span))
141 }
142 _ => None,
143 })
144 .next()
145 }
146 };
147
148 (@method $name:ident, $variant:ident(Span, Option<(String, Span)>)) => {
149 #[allow(unused)]
150 pub fn $name(&self) -> Option<Option<&(String, Span)>> {
151 self.attrs
152 .iter()
153 .filter_map(|a| match &a.1 {
154 BindgenAttr::$variant(_, s) => {
155 a.0.set(true);
156 Some(s.as_ref())
157 }
158 _ => None,
159 })
160 .next()
161 }
162 };
163
164 (@method $name:ident, $variant:ident(Span, Option<bool>), $default_value:literal) => {
165 #[allow(unused)]
166 pub fn $name(&self) -> bool {
167 self.attrs
168 .iter()
169 .filter_map(|a| match &a.1 {
170 BindgenAttr::$variant(_, s) => {
171 a.0.set(true);
172 *s
173 }
174 _ => None,
175 })
176 .next()
177 .unwrap_or($default_value)
178 }
179 };
180
181 (@method $name:ident, $variant:ident(Span, Vec<String>, Vec<Span>)) => {
182 #[allow(unused)]
183 pub fn $name(&self) -> Option<(&[String], &[Span])> {
184 self.attrs
185 .iter()
186 .filter_map(|a| match &a.1 {
187 BindgenAttr::$variant(_, ss, spans) => {
188 a.0.set(true);
189 Some((&ss[..], &spans[..]))
190 }
191 _ => None,
192 })
193 .next()
194 }
195 };
196
197 (@method $name:ident, $variant:ident(Span, $($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 (@method $name:ident, $variant:ident($($other:tt)*)) => {
214 #[allow(unused)]
215 pub fn $name(&self) -> Option<&$($other)*> {
216 self.attrs
217 .iter()
218 .filter_map(|a| match &a.1 {
219 BindgenAttr::$variant(s) => {
220 a.0.set(true);
221 Some(s)
222 }
223 _ => None,
224 })
225 .next()
226 }
227 };
228}
229
230impl BindgenAttrs {
231 pub fn find(attrs: &mut Vec<syn::Attribute>) -> Result<BindgenAttrs, Diagnostic> {
233 for (index, attr) in attrs.iter().enumerate() {
234 let attr = BindgenAttrs::try_from(attr)?;
235 if attr.exists {
236 attrs.remove(index);
237
238 return Ok(attr);
239 }
240 }
241
242 Ok(BindgenAttrs::default())
243 }
244
245 attrgen!(methods);
246}
247
248impl TryFrom<&Attribute> for BindgenAttrs {
249 type Error = Diagnostic;
250
251 fn try_from(attr: &Attribute) -> Result<Self, Self::Error> {
252 let mut ret = BindgenAttrs {
253 exists: false,
254 attrs: vec![],
255 span: Span::call_site(),
256 };
257
258 let is_napi =
259 attr.path().segments.last().map(|s| s.ident.to_string()) == Some("napi".to_string());
260 let is_cfg_attr = attr
261 .meta
262 .path()
263 .segments
264 .first()
265 .map(|s| s.ident.to_string())
266 == Some("cfg_attr".to_string());
267
268 if is_napi {
269 ret.exists = true;
270 ret.span = attr.span();
271
272 let tts = attr.meta.to_token_stream().into_iter();
273 let group = match tts.last() {
274 Some(TokenTree::Group(d)) => d,
277 Some(TokenTree::Ident(_)) => parse_quote!(()),
280 _ => bail_span!(attr, "invalid #[napi] attribute"),
281 };
282
283 if group.delimiter() != Delimiter::Parenthesis {
284 bail_span!(attr, "malformed #[napi] attribute");
285 }
286
287 let mut attrs: BindgenAttrs = syn::parse2(group.stream())?;
288 ret.attrs.append(&mut attrs.attrs);
289 }
290
291 if is_cfg_attr {
292 let cfg_attr_list = attr.meta.require_list()?;
293 let mut args_iter = cfg_attr_list
296 .parse_args_with(syn::punctuated::Punctuated::<syn::Meta, Token![,]>::parse_terminated)?
297 .into_iter();
298 if let Some(arg) = args_iter.next_back() {
299 if arg.path().segments.last().map(|s| s.ident.to_string()) == Some("napi".to_string()) {
300 ret.exists = true;
301 ret.span = arg.span();
302 let tts = arg.to_token_stream().into_iter();
303 let group = match tts.last() {
304 Some(TokenTree::Group(d)) => d,
307 Some(TokenTree::Ident(_)) => parse_quote!(()),
310 _ => bail_span!(attr, "invalid #[napi] attribute"),
311 };
312
313 if group.delimiter() != Delimiter::Parenthesis {
314 bail_span!(attr, "malformed #[napi] attribute");
315 }
316
317 let mut attrs: BindgenAttrs = syn::parse2(group.stream())?;
318 ret.attrs.append(&mut attrs.attrs);
319 }
320 }
321 }
322
323 Ok(ret)
324 }
325}
326
327impl Default for BindgenAttrs {
328 fn default() -> BindgenAttrs {
329 let attrs = ATTRS.get_or_init(AttributeParseState::default);
333 attrs.parsed.fetch_add(1, Ordering::SeqCst);
334 BindgenAttrs {
335 span: Span::call_site(),
336 attrs: Vec::new(),
337 exists: false,
338 }
339 }
340}
341
342macro_rules! gen_bindgen_attr {
343 ($( ($method:ident, $variant:ident($($associated_data:tt)*) $($extra_tokens:tt)*) ,)*) => {
344 #[derive(Debug)]
346 #[allow(unused)]
347 pub enum BindgenAttr {
348 $($variant($($associated_data)*)),*
349 }
350 }
351}
352
353attrgen!(gen_bindgen_attr);
354
355pub fn record_struct(ident: &Ident, js_name: String, opts: &BindgenAttrs) {
356 let state = STRUCTS.get_or_init(StructParseState::default);
357 let mut map = state.parsed.lock().unwrap();
358 let struct_name = ident.to_string();
359
360 map.insert(
361 struct_name,
362 ParsedStruct {
363 js_name,
364 ctor_defined: opts.constructor().is_some(),
365 },
366 );
367}
368
369pub fn check_recorded_struct_for_impl(ident: &Ident, opts: &BindgenAttrs) -> BindgenResult<String> {
370 let state = STRUCTS.get_or_init(StructParseState::default);
371 let mut map = state.parsed.lock().unwrap();
372 let struct_name = ident.to_string();
373 if let Some(parsed) = map.get_mut(&struct_name) {
374 if opts.constructor().is_some() && !cfg!(debug_assertions) {
375 if parsed.ctor_defined {
376 bail_span!(
377 ident,
378 "Constructor has already been defined for struct `{}`",
379 &struct_name
380 );
381 } else {
382 parsed.ctor_defined = true;
383 }
384 }
385
386 Ok(parsed.js_name.clone())
387 } else {
388 bail_span!(
389 ident,
390 "Did not find struct `{}` parsed before expand #[napi] for impl",
391 &struct_name,
392 )
393 }
394}
395
396impl Parse for BindgenAttrs {
397 fn parse(input: ParseStream) -> syn::Result<Self> {
398 let mut attrs = BindgenAttrs::default();
399 if input.is_empty() {
400 return Ok(attrs);
401 }
402
403 let opts = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?;
404 attrs.attrs = opts.into_iter().map(|c| (Cell::new(false), c)).collect();
405 Ok(attrs)
406 }
407}
408
409impl Parse for BindgenAttr {
410 fn parse(input: ParseStream) -> syn::Result<Self> {
411 let original = input.fork();
412 let attr: AnyIdent = input.parse()?;
413 let attr = attr.0;
414 let attr_span = attr.span();
415 let attr_string = attr.to_string();
416 let raw_attr_string = format!("r#{attr_string}");
417
418 macro_rules! parsers {
419 ($(($name:ident, $($contents:tt)*),)*) => {
420 $(
421 if attr_string == stringify!($name) || raw_attr_string == stringify!($name) {
422 parsers!(
423 @parser
424 $($contents)*
425 );
426 }
427 )*
428 };
429
430 (@parser $variant:ident(Span)) => ({
431 return Ok(BindgenAttr::$variant(attr_span));
432 });
433
434 (@parser $variant:ident(Span, Ident)) => ({
435 input.parse::<Token![=]>()?;
436 let ident = input.parse::<AnyIdent>()?.0;
437 return Ok(BindgenAttr::$variant(attr_span, ident))
438 });
439
440 (@parser $variant:ident(Span, Option<Ident>)) => ({
441 if input.parse::<Token![=]>().is_ok() {
442 let ident = input.parse::<AnyIdent>()?.0;
443 return Ok(BindgenAttr::$variant(attr_span, Some(ident)))
444 } else {
445 return Ok(BindgenAttr::$variant(attr_span, None));
446 }
447 });
448
449 (@parser $variant:ident(Span, syn::Path)) => ({
450 input.parse::<Token![=]>()?;
451 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
452 });
453
454 (@parser $variant:ident(Span, syn::Expr)) => ({
455 input.parse::<Token![=]>()?;
456 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
457 });
458
459 (@parser $variant:ident(Span, String, Span)) => ({
460 input.parse::<Token![=]>()?;
461 let (val, span) = match input.parse::<syn::LitStr>() {
462 Ok(str) => (str.value(), str.span()),
463 Err(_) => {
464 let ident = input.parse::<AnyIdent>()?.0;
465 (ident.to_string(), ident.span())
466 }
467 };
468 return Ok(BindgenAttr::$variant(attr_span, val, span))
469 });
470
471 (@parser $variant:ident(Span, Option<(String, Span)>)) => ({
472 if let Ok(_) = input.parse::<Token![=]>() {
473 let val = match input.parse::<syn::LitStr>() {
474 Ok(str) => Some((str.value(), str.span())),
475 Err(_) => {
476 let ident = input.parse::<AnyIdent>()?.0;
477 Some((ident.to_string(), ident.span()))
478 }
479 };
480 return Ok(BindgenAttr::$variant(attr_span, val))
481 } else {
482 return Ok(BindgenAttr::$variant(attr_span, None))
483 }
484 });
485
486 (@parser $variant:ident(Span, Option<bool>), $default_value:literal) => ({
487 if let Ok(_) = input.parse::<Token![=]>() {
488 let (val, _) = match input.parse::<syn::LitBool>() {
489 Ok(str) => (str.value(), str.span()),
490 Err(_) => {
491 let ident = input.parse::<AnyIdent>()?.0;
492 (true, ident.span())
493 }
494 };
495 return Ok::<BindgenAttr, syn::Error>(BindgenAttr::$variant(attr_span, Some(val)))
496 } else {
497 return Ok(BindgenAttr::$variant(attr_span, Some($default_value)))
498 }
499 });
500
501 (@parser $variant:ident(Span, Vec<String>, Vec<Span>)) => ({
502 input.parse::<Token![=]>()?;
503 let (vals, spans) = match input.parse::<syn::ExprArray>() {
504 Ok(exprs) => {
505 let mut vals = vec![];
506 let mut spans = vec![];
507
508 for expr in exprs.elems.iter() {
509 if let syn::Expr::Lit(syn::ExprLit {
510 lit: syn::Lit::Str(ref str),
511 ..
512 }) = expr {
513 vals.push(str.value());
514 spans.push(str.span());
515 } else {
516 return Err(syn::Error::new(expr.span(), "expected string literals"));
517 }
518 }
519
520 (vals, spans)
521 },
522 Err(_) => {
523 let ident = input.parse::<AnyIdent>()?.0;
524 (vec![ident.to_string()], vec![ident.span()])
525 }
526 };
527 return Ok(BindgenAttr::$variant(attr_span, vals, spans))
528 });
529 }
530
531 attrgen!(parsers);
532
533 Err(original.error("unknown attribute"))
534 }
535}