@@ -574,51 +574,81 @@ pub(crate) fn impl_pyclass(attr: PunctuatedNestedMeta, item: Item) -> Result<Tok
574574 ) ?;
575575
576576 const ALLOWED_TRAVERSE_OPTS : & [ & str ] = & [ "manual" ] ;
577- // try to know if it have a `#[pyclass(trace)]` exist on this struct
578- // TODO(discord9): rethink on auto detect `#[Derive(PyTrace)]`
579-
580- // 1. no `traverse` at all: generate a dummy try_traverse
581- // 2. `traverse = "manual"`: generate a try_traverse, but not #[derive(Traverse)]
582- // 3. `traverse`: generate a try_traverse, and #[derive(Traverse)]
583- let ( maybe_trace_code, derive_trace) = {
584- if class_meta. inner ( ) . _has_key ( "traverse" ) ? {
585- let maybe_trace_code = quote ! {
586- impl :: rustpython_vm:: object:: MaybeTraverse for #ident {
587- const IS_TRACE : bool = true ;
588- fn try_traverse( & self , tracer_fn: & mut :: rustpython_vm:: object:: TraverseFn ) {
589- :: rustpython_vm:: object:: Traverse :: traverse( self , tracer_fn) ;
590- }
577+ // Generate MaybeTraverse impl with both traverse and clear support
578+ //
579+ // For traverse:
580+ // 1. no `traverse` at all: HAS_TRAVERSE = false, try_traverse does nothing
581+ // 2. `traverse = "manual"`: HAS_TRAVERSE = true, but no #[derive(Traverse)]
582+ // 3. `traverse`: HAS_TRAVERSE = true, and #[derive(Traverse)]
583+ //
584+ // For clear (tp_clear):
585+ // 1. no `clear`: HAS_CLEAR = HAS_TRAVERSE (default: same as traverse)
586+ // 2. `clear` or `clear = true`: HAS_CLEAR = true, try_clear calls Traverse::clear
587+ // 3. `clear = false`: HAS_CLEAR = false (rare: traverse without clear)
588+ let has_traverse = class_meta. inner ( ) . _has_key ( "traverse" ) ?;
589+ let has_clear = if class_meta. inner ( ) . _has_key ( "clear" ) ? {
590+ // If clear attribute is present, use its value
591+ class_meta. inner ( ) . _bool ( "clear" ) ?
592+ } else {
593+ // If clear attribute is absent, default to same as traverse
594+ has_traverse
595+ } ;
596+
597+ let derive_trace = if has_traverse {
598+ // _optional_str returns Err when key exists without value (e.g., `traverse` vs `traverse = "manual"`)
599+ // We want to derive Traverse in that case, so we handle Err as Ok(None)
600+ let value = class_meta. inner ( ) . _optional_str ( "traverse" ) . ok ( ) . flatten ( ) ;
601+ if let Some ( s) = value {
602+ if !ALLOWED_TRAVERSE_OPTS . contains ( & s. as_str ( ) ) {
603+ bail_span ! (
604+ item,
605+ "traverse attribute only accept {ALLOWED_TRAVERSE_OPTS:?} as value or no value at all" ,
606+ ) ;
607+ }
608+ assert_eq ! ( s, "manual" ) ;
609+ quote ! { }
610+ } else {
611+ quote ! { #[ derive( Traverse ) ] }
612+ }
613+ } else {
614+ quote ! { }
615+ } ;
616+
617+ let maybe_traverse_code = {
618+ let HAS_TRAVERSE = has_traverse;
619+ let try_traverse_body = if has_traverse {
620+ quote ! {
621+ :: rustpython_vm:: object:: Traverse :: traverse( self , tracer_fn) ;
622+ }
623+ } else {
624+ quote ! {
625+ // do nothing
626+ }
627+ } ;
628+
629+ let try_clear_body = if has_clear {
630+ quote ! {
631+ :: rustpython_vm:: object:: Traverse :: clear( self , out) ;
632+ }
633+ } else {
634+ quote ! {
635+ // do nothing
636+ }
637+ } ;
638+
639+ quote ! {
640+ impl :: rustpython_vm:: object:: MaybeTraverse for #ident {
641+ const HAS_TRAVERSE : bool = #HAS_TRAVERSE ;
642+ const HAS_CLEAR : bool = #has_clear;
643+
644+ fn try_traverse( & self , tracer_fn: & mut :: rustpython_vm:: object:: TraverseFn ) {
645+ #try_traverse_body
591646 }
592- } ;
593- // if the key `traverse` exist but not as key-value, _optional_str return Err(...)
594- // so we need to check if it is Ok(Some(...))
595- let value = class_meta. inner ( ) . _optional_str ( "traverse" ) ;
596- let derive_trace = if let Ok ( Some ( s) ) = value {
597- if !ALLOWED_TRAVERSE_OPTS . contains ( & s. as_str ( ) ) {
598- bail_span ! (
599- item,
600- "traverse attribute only accept {ALLOWED_TRAVERSE_OPTS:?} as value or no value at all" ,
601- ) ;
647+
648+ fn try_clear( & mut self , out: & mut :: std:: vec:: Vec <:: rustpython_vm:: PyObjectRef >) {
649+ #try_clear_body
602650 }
603- assert_eq ! ( s, "manual" ) ;
604- quote ! { }
605- } else {
606- quote ! { #[ derive( Traverse ) ] }
607- } ;
608- ( maybe_trace_code, derive_trace)
609- } else {
610- (
611- // a dummy impl, which do nothing
612- // #attrs
613- quote ! {
614- impl :: rustpython_vm:: object:: MaybeTraverse for #ident {
615- fn try_traverse( & self , tracer_fn: & mut :: rustpython_vm:: object:: TraverseFn ) {
616- // do nothing
617- }
618- }
619- } ,
620- quote ! { } ,
621- )
651+ }
622652 }
623653 } ;
624654
@@ -675,7 +705,7 @@ pub(crate) fn impl_pyclass(attr: PunctuatedNestedMeta, item: Item) -> Result<Tok
675705 let ret = quote ! {
676706 #derive_trace
677707 #item
678- #maybe_trace_code
708+ #maybe_traverse_code
679709 #class_def
680710 #impl_payload
681711 #empty_impl
0 commit comments