1#![deny(clippy::unnecessary_wraps)]
3#![deny(clippy::print_stderr)]
4#![deny(clippy::print_stdout)]
5
6use proc_macro2::Ident;
7use proc_macro2::TokenStream;
8use quote::format_ident;
9use quote::quote;
10use quote::ToTokens;
11use syn::parse::Parse;
12use syn::parse::ParseStream;
13use syn::parse2;
14use syn::spanned::Spanned;
15use syn::Attribute;
16use syn::Data;
17use syn::DeriveInput;
18use syn::Error;
19use syn::Field;
20use syn::Fields;
21use syn::LitStr;
22use syn::Member;
23use syn::Meta;
24use syn::Token;
25use syn::Type;
26
27const IDENTIFIABLE_ERRORS: [&str; 7] = [
28 "Error",
29 "RangeError",
30 "TypeError",
31 "SyntaxError",
32 "URIError",
33 "ReferenceError",
34 "NotSupportedError",
35];
36
37#[proc_macro_derive(JsError, attributes(class, property, properties, inherit))]
38pub fn derive_js_error(
39 item: proc_macro::TokenStream,
40) -> proc_macro::TokenStream {
41 match js_error(item.into()) {
42 Ok(output) => output.into(),
43 Err(err) => err.into_compile_error().into(),
44 }
45}
46
47fn js_error(item: TokenStream) -> Result<TokenStream, Error> {
48 let input = parse2::<DeriveInput>(item)?;
49
50 let additional_properties = input
51 .attrs
52 .iter()
53 .filter_map(|attr| {
54 if attr.path().is_ident("property") {
55 Some(attr.parse_args())
56 } else {
57 None
58 }
59 })
60 .collect::<Result<Vec<AdditionalProperty>, Error>>()?;
61
62 let (class, out_properties) = match input.data {
63 Data::Enum(data) => {
64 let top_class_attr = input
65 .attrs
66 .into_iter()
67 .find_map(|attr| ClassAttrValue::from_attribute(attr).transpose())
68 .transpose()?;
69 if let Some(top_class_attr) = &top_class_attr {
70 if matches!(top_class_attr, ClassAttrValue::Inherit(_)) {
71 return Err(Error::new(
72 top_class_attr.to_tokens(&None).unwrap_err().span(),
73 "top level class attribute cannot be inherit",
74 ));
75 }
76 }
77
78 let mut get_class = vec![];
79 let mut get_properties = vec![];
80
81 for variant in data.variants {
82 let variant_additional_properties = variant
83 .attrs
84 .iter()
85 .filter_map(|attr| {
86 if attr.path().is_ident("property") {
87 Some(attr.parse_args())
88 } else {
89 None
90 }
91 })
92 .collect::<Result<Vec<AdditionalProperty>, Error>>()?;
93
94 let inherit_properties = variant
95 .attrs
96 .iter()
97 .find_map(|attr| {
98 if attr.path().is_ident("properties") {
99 Some(attr.parse_args::<InheritProperties>())
100 } else {
101 None
102 }
103 })
104 .transpose()?;
105
106 let class_attr = variant
107 .attrs
108 .into_iter()
109 .find_map(|attr| ClassAttrValue::from_attribute(attr).transpose())
110 .unwrap_or_else(|| {
111 top_class_attr.clone().ok_or_else(|| {
112 Error::new(variant.ident.span(), "class attribute is missing")
113 })
114 })?;
115
116 let (
117 class,
118 properties,
119 _inherit_class_member,
120 inherit_property_member,
121 parsed_properties,
122 ) = handle_variant_or_struct(
123 inherit_properties,
124 class_attr,
125 variant_additional_properties,
126 variant.fields,
127 )?;
128
129 let variant_ident = variant.ident;
130
131 let class_match_arm_identifiers = {
132 let mut parsed_properties = parsed_properties
133 .iter()
134 .enumerate()
135 .map(|(i, property)| {
136 let i = format_ident!("__{i}");
137 let member = &property.ident;
138 quote!(#member: #i,)
139 })
140 .collect::<Vec<_>>();
141
142 if let Some((member, _)) = &_inherit_class_member {
143 parsed_properties.push(quote!(#member: inherit,));
144 }
145
146 parsed_properties
147 };
148
149 let class_match_arm =
150 quote!(Self::#variant_ident { #(#class_match_arm_identifiers)* .. });
151
152 let match_arm_identifiers = {
153 let mut parsed_properties = parsed_properties
154 .into_iter()
155 .enumerate()
156 .map(|(i, property)| {
157 let i = format_ident!("__{i}");
158 let member = property.ident;
159 quote!(#member: #i,)
160 })
161 .collect::<Vec<_>>();
162
163 if let Some((member, _)) = &inherit_property_member {
164 parsed_properties.push(quote!(#member: inherit,));
165 }
166
167 parsed_properties
168 };
169
170 let match_arm =
171 quote!(Self::#variant_ident { #(#match_arm_identifiers)* .. });
172
173 get_class.push(quote! {
174 #class_match_arm => #class,
175 });
176
177 let properties =
178 properties.unwrap_or_else(|| quote!(std::iter::empty()));
179 get_properties.push(quote! {
180 #match_arm => Box::new(#properties),
181 });
182 }
183
184 (
185 quote! {
186 match self {
187 #(#get_class)*
188 }
189 },
190 Some(quote! {
191 match self {
192 #(#get_properties)*
193 }
194 }),
195 )
196 }
197 Data::Struct(data) => {
198 let inherit_properties = input
199 .attrs
200 .iter()
201 .find_map(|attr| {
202 if attr.path().is_ident("properties") {
203 Some(attr.parse_args::<InheritProperties>())
204 } else {
205 None
206 }
207 })
208 .transpose()?;
209
210 let class_attr = input
211 .attrs
212 .into_iter()
213 .find_map(|attr| ClassAttrValue::from_attribute(attr).transpose())
214 .unwrap_or_else(|| {
215 if data.fields.len() == 1 {
216 Ok(ClassAttrValue::Inherit(kw::inherit::default()))
217 } else {
218 Err(Error::new(
219 input.ident.span(),
220 "class attribute is missing and could not be inferred",
221 ))
222 }
223 })?;
224
225 let (
226 class,
227 properties,
228 inherit_class_member,
229 inherit_property_member,
230 parsed_properties,
231 ) = handle_variant_or_struct(
232 inherit_properties,
233 class_attr,
234 vec![],
235 data.fields,
236 )?;
237
238 let class_specifier_var = inherit_class_member.map(|(member, _)| {
239 quote! {
240 let inherit = &self.#member;
241 }
242 });
243
244 let property_specifier_var =
245 inherit_property_member.map(|(member, _)| {
246 quote! {
247 let inherit = &self.#member;
248 }
249 });
250
251 let parsed_properties = parsed_properties
252 .into_iter()
253 .enumerate()
254 .map(|(i, property)| {
255 let i = format_ident!("__{i}");
256 let member = property.ident;
257 quote! {
258 let #i = &self.#member;
259 }
260 })
261 .collect::<Vec<_>>();
262
263 let out_properties = if property_specifier_var.is_none()
264 && parsed_properties.is_empty()
265 && properties.is_none()
266 {
267 None
268 } else {
269 let properties =
270 properties.unwrap_or_else(|| quote!(std::iter::empty()));
271 Some(quote! {
272 Box::new({
273 #property_specifier_var
274 #(#parsed_properties)*
275 #properties
276 })
277 })
278 };
279
280 (
281 quote! {
282 #class_specifier_var
283 #class
284 },
285 out_properties,
286 )
287 }
288 Data::Union(_) => {
289 return Err(Error::new(input.span(), "Unions are not supported"))
290 }
291 };
292
293 let properties = if !additional_properties.is_empty() {
294 let additional_properties = additional_properties
295 .into_iter()
296 .map(|AdditionalProperty { name, value, .. }| quote!((#name.into(), ::deno_error::PropertyValue::from(#value))));
297
298 let additional_properties =
299 quote!([#(#additional_properties),*].into_iter());
300 if let Some(out_properties) = out_properties {
301 quote!(Box::new({ *#out_properties }.chain(#additional_properties)))
302 } else {
303 quote!(Box::new(#additional_properties))
304 }
305 } else {
306 let out_properties =
307 out_properties.unwrap_or_else(|| quote!(Box::new(std::iter::empty())));
308 quote!(#out_properties)
309 };
310
311 let ident = input.ident;
312
313 Ok(quote! {
314 #[allow(unused_qualifications)]
315 impl ::deno_error::JsErrorClass for #ident {
316 fn get_class(&self) -> ::std::borrow::Cow<'static, str> {
317 #class
318 }
319 fn get_message(&self) -> ::std::borrow::Cow<'static, str> {
320 self.to_string().into()
321 }
322 fn get_additional_properties(
323 &self
324 ) -> ::deno_error::AdditionalProperties {
325 #properties
326 }
327 fn get_ref(&self) -> &(dyn ::std::error::Error + Send + Sync + 'static) {
328 self
329 }
330 }
331 })
332}
333
334#[allow(clippy::type_complexity)]
335fn handle_variant_or_struct(
336 inherit_properties: Option<InheritProperties>,
337 class_attr: ClassAttrValue,
338 additional_properties: Vec<AdditionalProperty>,
339 fields: Fields,
340) -> Result<
341 (
342 TokenStream,
343 Option<TokenStream>,
344 Option<(Member, TokenStream)>,
345 Option<(Member, TokenStream)>,
346 Vec<ParsedFieldProperty>,
347 ),
348 Error,
349> {
350 let parsed_properties = get_properties_from_fields(&fields)?;
351
352 let inherit_properties =
353 inherit_properties.unwrap_or_else(|| match &class_attr {
354 ClassAttrValue::Inherit(kw) => InheritProperties::Inherit(*kw),
355 _ => InheritProperties::NoInherit(Default::default()),
356 });
357
358 let properties = if !parsed_properties.is_empty() {
359 let properties = parsed_properties
360 .iter()
361 .enumerate()
362 .map(|(i, property)| {
363 let i = format_ident!("__{i}");
364 let ident_str = &property.name;
365
366 quote! {
367 (::std::borrow::Cow::Borrowed(#ident_str), #i.into())
368 }
369 })
370 .collect::<Vec<_>>();
371
372 Some(quote!([#(#properties),*].into_iter()))
373 } else {
374 None
375 };
376
377 let (inherit_class_member, inherit_property_member) = match fields {
378 Fields::Named(fields_named) => {
379 let class_field = if fields_named.named.len() == 1
380 && matches!(class_attr, ClassAttrValue::Inherit(_))
381 {
382 fields_named.named.first()
383 } else {
384 fields_named.named.iter().find(get_inherit_attr_field)
385 };
386
387 let class_field = class_field.map(|field| {
388 (
389 Member::Named(field.ident.clone().unwrap()),
390 field_inherit_reference(field),
391 )
392 });
393
394 let property_field = if fields_named.named.len() == 1
395 && matches!(inherit_properties, InheritProperties::Inherit(_))
396 {
397 fields_named.named.first()
398 } else {
399 fields_named.named.iter().find(get_inherit_attr_field)
400 };
401
402 let property_field = property_field.map(|field| {
403 (
404 Member::Named(field.ident.clone().unwrap()),
405 field_inherit_reference(field),
406 )
407 });
408
409 (class_field, property_field)
410 }
411 Fields::Unnamed(fields_unnamed) => {
412 let class_field = if fields_unnamed.unnamed.len() == 1
413 && matches!(class_attr, ClassAttrValue::Inherit(_))
414 {
415 fields_unnamed.unnamed.first().map(|field| (0, field))
416 } else {
417 fields_unnamed
418 .unnamed
419 .iter()
420 .enumerate()
421 .find(|(_, field)| get_inherit_attr_field(field))
422 };
423
424 let class_field = class_field.map(|(i, field)| {
425 (
426 Member::Unnamed(syn::Index::from(i)),
427 field_inherit_reference(field),
428 )
429 });
430
431 let property_field = if fields_unnamed.unnamed.len() == 1
432 && matches!(inherit_properties, InheritProperties::Inherit(_))
433 {
434 fields_unnamed.unnamed.first().map(|field| (0, field))
435 } else {
436 fields_unnamed
437 .unnamed
438 .iter()
439 .enumerate()
440 .find(|(_, field)| get_inherit_attr_field(field))
441 };
442
443 let property_field = property_field.map(|(i, field)| {
444 (
445 Member::Unnamed(syn::Index::from(i)),
446 field_inherit_reference(field),
447 )
448 });
449
450 (class_field, property_field)
451 }
452 Fields::Unit => (None, None),
453 };
454
455 let class = class_attr.to_tokens(&inherit_class_member)?;
456
457 let properties = if let Some((_, tokens)) = &inherit_property_member {
458 let inherited_properties = quote!(::deno_error::JsErrorClass::get_additional_properties(
459 #tokens
460 ));
461
462 if let Some(properties) = properties {
463 Some(quote!(#properties.chain(#inherited_properties)))
464 } else {
465 Some(inherited_properties)
466 }
467 } else {
468 properties
469 };
470
471 let properties = if !additional_properties.is_empty() {
472 let additional_properties = additional_properties
473 .into_iter()
474 .map(|AdditionalProperty { name, value, .. }| {
475 match value {
477 syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(int_lit), .. }) => {
478 quote!((#name.into(), ::deno_error::PropertyValue::Number(#int_lit as f64)))
479 }
480 syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Float(float_lit), .. }) => {
481 quote!((#name.into(), ::deno_error::PropertyValue::Number(#float_lit)))
482 }
483 _ => {
484 quote!((#name.into(), #value.into()))
485 }
486 }
487 });
488
489 let additional_properties =
490 quote!([#(#additional_properties),*].into_iter());
491
492 if let Some(properties) = properties {
493 Some(quote!(#properties.chain(#additional_properties)))
494 } else {
495 Some(additional_properties)
496 }
497 } else {
498 properties
499 };
500
501 Ok((
502 class,
503 properties,
504 inherit_class_member,
505 inherit_property_member,
506 parsed_properties,
507 ))
508}
509
510fn get_inherit_attr_field(field: &&Field) -> bool {
511 field
512 .attrs
513 .iter()
514 .any(|attr| attr.path().is_ident("inherit"))
515}
516
517mod kw {
518 syn::custom_keyword!(class);
519 syn::custom_keyword!(property);
520 syn::custom_keyword!(inherit);
521 syn::custom_keyword!(no_inherit);
522}
523
524#[derive(Debug, Clone)]
525enum ClassAttrValue {
526 Lit(syn::LitStr),
527 Ident(Ident),
528 Inherit(kw::inherit),
529}
530
531impl ClassAttrValue {
532 fn from_attribute(attr: Attribute) -> Result<Option<Self>, Error> {
533 if attr.path().is_ident("class") {
534 let list = attr.meta.require_list()?;
535 let value = list.parse_args::<Self>()?;
536
537 match &value {
538 ClassAttrValue::Lit(lit) => {
539 if IDENTIFIABLE_ERRORS.contains(&lit.value().as_str()) {
540 return Err(Error::new(
541 lit.span(),
542 format!("An identifier can be used instead of '{}'", lit.value()),
543 ));
544 }
545 }
546 ClassAttrValue::Ident(ident) => {
547 let ident_str = ident.to_string();
548
549 if ident_str.to_lowercase() != ident_str {
552 return Err(Error::new(
553 ident.span(),
554 "Identifier passed is not lowercase",
555 ));
556 }
557 }
558 ClassAttrValue::Inherit(_) => {}
559 }
560
561 return Ok(Some(value));
562 }
563
564 Ok(None)
565 }
566
567 fn to_tokens(
568 &self,
569 inherit_member: &Option<(Member, TokenStream)>,
570 ) -> Result<TokenStream, Error> {
571 let class_tokens = match self {
572 ClassAttrValue::Lit(lit) => quote!(::std::borrow::Cow::Borrowed(#lit)),
573 ClassAttrValue::Ident(ident) => {
574 let error_name =
575 format_ident!("{}_ERROR", ident.to_string().to_uppercase());
576 quote!(::std::borrow::Cow::Borrowed(::deno_error::builtin_classes::#error_name))
577 }
578 ClassAttrValue::Inherit(inherit) => {
579 let (_, tokens) = inherit_member.as_ref().ok_or_else(|| {
580 Error::new(
581 inherit.span,
582 "class attribute was set to inherit, but multiple fields are available and none was marked as inherit",
583 )
584 })?;
585
586 quote!(::deno_error::JsErrorClass::get_class(#tokens))
587 }
588 };
589
590 Ok(class_tokens)
591 }
592}
593
594impl Parse for ClassAttrValue {
595 fn parse(input: ParseStream) -> syn::Result<Self> {
596 let lookahead = input.lookahead1();
597
598 if lookahead.peek(syn::LitStr) {
599 Ok(Self::Lit(input.parse()?))
600 } else if lookahead.peek(kw::inherit) {
601 Ok(Self::Inherit(input.parse()?))
602 } else if lookahead.peek(syn::Ident) {
603 Ok(Self::Ident(input.parse()?))
604 } else if lookahead.peek(Token![type]) {
605 let type_token = input.parse::<Token![type]>()?;
606 Ok(Self::Ident(Ident::new("type", type_token.span)))
607 } else {
608 Err(lookahead.error())
609 }
610 }
611}
612
613#[derive(Debug)]
614struct ParsedFieldProperty {
615 ident: Member,
616 name: String,
617}
618
619fn get_properties_from_fields(
620 fields: &Fields,
621) -> Result<Vec<ParsedFieldProperty>, Error> {
622 const PROPERTY_IDENT: &str = "property";
623 let mut out_fields = vec![];
624
625 match fields {
626 Fields::Named(named) => {
627 for field in &named.named {
628 for attr in &field.attrs {
629 if attr.path().is_ident(PROPERTY_IDENT) {
630 let name = match &attr.meta {
631 Meta::Path(_) => None,
632 Meta::List(list) => {
633 return Err(Error::new(
634 list.delimiter.span().open(),
635 "expected `=`",
636 ));
637 }
638 Meta::NameValue(meta) => {
639 Some(parse2::<LitStr>(meta.value.to_token_stream())?.value())
640 }
641 };
642
643 let ident = field.ident.clone().unwrap();
644 let name = name.unwrap_or_else(|| ident.to_string());
645 let ident = Member::Named(field.ident.clone().unwrap());
646 out_fields.push(ParsedFieldProperty { name, ident });
647
648 break;
649 }
650 }
651 }
652 }
653 Fields::Unnamed(unnamed) => {
654 for (i, field) in unnamed.unnamed.iter().enumerate() {
655 for attr in &field.attrs {
656 if attr.path().is_ident(PROPERTY_IDENT) {
657 let name_value = attr.meta.require_name_value()?;
658 let name =
659 parse2::<LitStr>(name_value.value.to_token_stream())?.value();
660
661 let ident = Member::Unnamed(syn::Index::from(i));
662 out_fields.push(ParsedFieldProperty { name, ident });
663
664 break;
665 }
666 }
667 }
668 }
669 Fields::Unit => {}
670 }
671
672 Ok(out_fields)
673}
674
675#[derive(Debug)]
676struct AdditionalProperty {
677 name: LitStr,
678 _eq: Token![=],
679 value: syn::Expr,
680}
681
682impl Parse for AdditionalProperty {
683 fn parse(input: ParseStream) -> syn::Result<Self> {
684 Ok(Self {
685 name: input.parse()?,
686 _eq: input.parse()?,
687 value: input.parse()?,
688 })
689 }
690}
691
692#[derive(Debug)]
693#[allow(dead_code)]
694enum InheritProperties {
695 Inherit(kw::inherit),
696 NoInherit(kw::no_inherit),
697}
698
699impl Parse for InheritProperties {
700 fn parse(input: ParseStream) -> syn::Result<Self> {
701 let lookahead = input.lookahead1();
702
703 if lookahead.peek(kw::inherit) {
704 Ok(InheritProperties::Inherit(input.parse()?))
705 } else if lookahead.peek(kw::no_inherit) {
706 Ok(InheritProperties::NoInherit(input.parse()?))
707 } else {
708 Err(lookahead.error())
709 }
710 }
711}
712
713fn field_inherit_reference(field: &Field) -> TokenStream {
714 let is_wrapped = match &field.ty {
715 Type::Path(e) => {
716 if let Some(first) = e.path.segments.last() {
717 matches!(first.ident.to_string().as_str(), "Box" | "Rc" | "Arc")
718 } else {
719 false
720 }
721 }
722 _ => false,
723 };
724
725 if is_wrapped {
726 quote!(&**inherit)
727 } else {
728 quote!(inherit)
729 }
730}