@@ -25,6 +25,27 @@ use super::pseudocode::{
2525} ;
2626use super :: state:: DecompilerState ;
2727
28+ // ═══════════════════════════════════════════════════════════════════════════
29+ // Helper Functions
30+ // ═══════════════════════════════════════════════════════════════════════════
31+
32+ /// Extract just the header span from a loop's full span.
33+ ///
34+ /// For `while.true`, this returns a span covering "while.true" (10 chars).
35+ /// For `repeat.N`, this returns a span covering "repeat.N" (7 + digit count chars).
36+ fn loop_header_span ( op : & Op ) -> SourceSpan {
37+ let span = op. span ( ) ;
38+ let header_len = match op {
39+ Op :: While { .. } => 10 , // "while.true"
40+ Op :: Repeat { count, .. } => {
41+ // "repeat." (7) + digits
42+ 7 + if * count == 0 { 1 } else { ( * count as f64 ) . log10 ( ) . floor ( ) as u32 + 1 }
43+ }
44+ _ => return span, // Not a loop, return full span
45+ } ;
46+ SourceSpan :: new ( span. source_id ( ) , span. start ( ) ..span. start ( ) + header_len)
47+ }
48+
2849// ═══════════════════════════════════════════════════════════════════════════
2950// Hint Collection Types
3051// ═══════════════════════════════════════════════════════════════════════════
@@ -250,7 +271,7 @@ impl<'a> DecompilationCollector<'a> {
250271 // Fail decompilation - we have dynamic stack content from a previous loop
251272 if let Some ( ref mut state) = self . state {
252273 state. fail_tracking (
253- op . span ( ) ,
274+ loop_header_span ( op ) ,
254275 "loop with variable iteration count preceded by another loop that modified stack size"
255276 ) ;
256277 }
@@ -278,7 +299,7 @@ impl<'a> DecompilationCollector<'a> {
278299
279300 // Mark stack as dynamic for subsequent loops
280301 if let Some ( ref mut state) = self . state {
281- state. dynamic_stack_source = Some ( op . span ( ) ) ;
302+ state. dynamic_stack_source = Some ( loop_header_span ( op ) ) ;
282303 }
283304 } else {
284305 // Zero net effect: stack shape is preserved, use renaming for consistency
@@ -365,7 +386,7 @@ impl<'a> DecompilationCollector<'a> {
365386 // Fail decompilation - we have dynamic stack content from a previous loop
366387 if let Some ( ref mut state) = self . state {
367388 state. fail_tracking (
368- op . span ( ) ,
389+ loop_header_span ( op ) ,
369390 "loop with non-zero stack effect preceded by another loop that modified stack size"
370391 ) ;
371392 }
@@ -384,7 +405,7 @@ impl<'a> DecompilationCollector<'a> {
384405
385406 // Mark stack as dynamic for subsequent loops
386407 if let Some ( ref mut state) = self . state {
387- state. dynamic_stack_source = Some ( op . span ( ) ) ;
408+ state. dynamic_stack_source = Some ( loop_header_span ( op ) ) ;
388409 }
389410 } else {
390411 // Zero net effect: stack shape is preserved, use renaming for consistency
0 commit comments