@@ -574,51 +574,80 @@ 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 try_traverse_body = if has_traverse {
619+ quote ! {
620+ :: rustpython_vm:: object:: Traverse :: traverse( self , tracer_fn) ;
621+ }
622+ } else {
623+ quote ! {
624+ // do nothing
625+ }
626+ } ;
627+
628+ let try_clear_body = if has_clear {
629+ quote ! {
630+ :: rustpython_vm:: object:: Traverse :: clear( self , out) ;
631+ }
632+ } else {
633+ quote ! {
634+ // do nothing
635+ }
636+ } ;
637+
638+ quote ! {
639+ impl :: rustpython_vm:: object:: MaybeTraverse for #ident {
640+ const HAS_TRAVERSE : bool = #has_traverse;
641+ const HAS_CLEAR : bool = #has_clear;
642+
643+ fn try_traverse( & self , tracer_fn: & mut :: rustpython_vm:: object:: TraverseFn ) {
644+ #try_traverse_body
591645 }
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- ) ;
646+
647+ fn try_clear( & mut self , out: & mut :: std:: vec:: Vec <:: rustpython_vm:: PyObjectRef >) {
648+ #try_clear_body
602649 }
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- )
650+ }
622651 }
623652 } ;
624653
@@ -675,7 +704,7 @@ pub(crate) fn impl_pyclass(attr: PunctuatedNestedMeta, item: Item) -> Result<Tok
675704 let ret = quote ! {
676705 #derive_trace
677706 #item
678- #maybe_trace_code
707+ #maybe_traverse_code
679708 #class_def
680709 #impl_payload
681710 #empty_impl
0 commit comments