33use proc_macro2:: TokenStream ;
44use quote:: { format_ident, quote} ;
55
6- use crate :: schema:: { Def , EnumDef , Schema , StructDef } ;
6+ use crate :: {
7+ schema:: { Def , EnumDef , Schema , StructDef , TypeDef } ,
8+ Result ,
9+ } ;
710
8- use super :: { define_derive , Derive , StructOrEnum } ;
9-
10- const IGNORE_FIELD_TYPES : [ & str ; 4 ] = [ "Span" , "ScopeId" , "SymbolId" , "ReferenceId" ] ;
11+ use super :: {
12+ attr_positions , define_derive , AttrLocation , AttrPart , AttrPositions , Derive , StructOrEnum ,
13+ } ;
1114
1215/// Derive for `ContentEq` trait.
1316pub struct DeriveContentEq ;
@@ -23,6 +26,31 @@ impl Derive for DeriveContentEq {
2326 "oxc_span"
2427 }
2528
29+ /// Register that accept `#[content_eq]` attr on structs, enums, or struct fields.
30+ /// Allow attr on structs and enums which don't derive this trait.
31+ fn attrs ( & self ) -> & [ ( & ' static str , AttrPositions ) ] {
32+ & [ ( "content_eq" , attr_positions ! ( StructMaybeDerived | EnumMaybeDerived | StructField ) ) ]
33+ }
34+
35+ /// Parse `#[content_eq(skip)]` attr.
36+ fn parse_attr ( & self , _attr_name : & str , location : AttrLocation , part : AttrPart ) -> Result < ( ) > {
37+ // No need to check attr name is `content_eq`, because that's the only attribute this derive handles.
38+ if !matches ! ( part, AttrPart :: Tag ( "skip" ) ) {
39+ return Err ( ( ) ) ;
40+ }
41+
42+ match location {
43+ AttrLocation :: Struct ( struct_def) => struct_def. content_eq . skip = true ,
44+ AttrLocation :: Enum ( enum_def) => enum_def. content_eq . skip = true ,
45+ AttrLocation :: StructField ( struct_def, field_index) => {
46+ struct_def. fields [ field_index] . content_eq . skip = true ;
47+ }
48+ _ => return Err ( ( ) ) ,
49+ }
50+
51+ Ok ( ( ) )
52+ }
53+
2654 fn prelude ( & self ) -> TokenStream {
2755 quote ! {
2856 #![ allow( clippy:: match_same_arms) ]
@@ -41,30 +69,49 @@ impl Derive for DeriveContentEq {
4169}
4270
4371fn derive_struct ( struct_def : & StructDef , schema : & Schema ) -> TokenStream {
44- let fields = struct_def
45- . fields
46- . iter ( )
47- . filter ( |field| {
48- let innermost_type = field. type_def ( schema) . innermost_type ( schema) ;
49- !IGNORE_FIELD_TYPES . contains ( & innermost_type. name ( ) )
50- } )
51- . map ( |field| {
52- let ident = field. ident ( ) ;
53- quote ! ( ContentEq :: content_eq( & self . #ident, & other. #ident) )
54- } ) ;
55-
56- let mut body = quote ! ( #( #fields) &&* ) ;
5772 let mut other_name = "other" ;
58- if body. is_empty ( ) {
59- body = quote ! ( true ) ;
73+
74+ let body = if struct_def. content_eq . skip {
75+ // Struct has `#[content_eq(skip)]` attr. So `content_eq` always returns true.
6076 other_name = "_" ;
77+ quote ! ( true )
78+ } else {
79+ let fields = struct_def
80+ . fields
81+ . iter ( )
82+ . filter ( |field| !field. content_eq . skip )
83+ . filter ( |field| {
84+ let innermost_type = field. type_def ( schema) . innermost_type ( schema) ;
85+ match innermost_type {
86+ TypeDef :: Struct ( struct_def) => !struct_def. content_eq . skip ,
87+ TypeDef :: Enum ( enum_def) => !enum_def. content_eq . skip ,
88+ _ => true ,
89+ }
90+ } )
91+ . map ( |field| {
92+ let ident = field. ident ( ) ;
93+ quote ! ( ContentEq :: content_eq( & self . #ident, & other. #ident) )
94+ } ) ;
95+
96+ let mut body = quote ! ( #( #fields) &&* ) ;
97+ if body. is_empty ( ) {
98+ body = quote ! ( true ) ;
99+ other_name = "_" ;
100+ } ;
101+ body
61102 } ;
62103
63104 generate_impl ( & struct_def. ty_anon ( schema) , other_name, & body)
64105}
65106
66107fn derive_enum ( enum_def : & EnumDef , schema : & Schema ) -> TokenStream {
67- let body = if enum_def. is_fieldless ( ) {
108+ let mut other_name = "other" ;
109+
110+ let body = if enum_def. content_eq . skip {
111+ // Enum has `#[content_eq(skip)]` attr. So `content_eq` always returns true.
112+ other_name = "_" ;
113+ quote ! ( true )
114+ } else if enum_def. is_fieldless ( ) {
68115 // We assume fieldless enums implement `PartialEq`
69116 quote ! ( self == other)
70117 } else {
@@ -76,6 +123,7 @@ fn derive_enum(enum_def: &EnumDef, schema: &Schema) -> TokenStream {
76123 quote ! ( ( Self :: #ident( a) , Self :: #ident( b) ) => a. content_eq( b) )
77124 }
78125 } ) ;
126+
79127 quote ! {
80128 match ( self , other) {
81129 #( #matches, ) *
@@ -84,7 +132,7 @@ fn derive_enum(enum_def: &EnumDef, schema: &Schema) -> TokenStream {
84132 }
85133 } ;
86134
87- generate_impl ( & enum_def. ty_anon ( schema) , "other" , & body)
135+ generate_impl ( & enum_def. ty_anon ( schema) , other_name , & body)
88136}
89137
90138fn generate_impl ( ty : & TokenStream , other_name : & str , body : & TokenStream ) -> TokenStream {
0 commit comments