@@ -6,6 +6,7 @@ extern crate rustc_data_structures;
66extern crate rustc_driver;
77extern crate rustc_hir;
88extern crate rustc_hir_analysis;
9+ extern crate rustc_index;
910extern crate rustc_interface;
1011extern crate rustc_log;
1112extern crate rustc_middle;
@@ -19,13 +20,15 @@ use std::path::PathBuf;
1920use miri:: * ;
2021use rustc_driver:: Compilation ;
2122use rustc_hir:: attrs:: CrateType ;
23+ use rustc_index:: IndexVec ;
2224use rustc_interface:: interface;
2325use rustc_middle:: mir;
26+ use rustc_middle:: mir:: { Local , VarDebugInfoContents } ;
2427use rustc_middle:: ty:: TyCtxt ;
2528use rustc_session:: EarlyDiagCtxt ;
2629use rustc_session:: config:: ErrorOutputType ;
27- use rustc_span:: Span ;
2830use rustc_span:: source_map:: SourceMap ;
31+ use rustc_span:: { Span , Symbol } ;
2932
3033fn find_sysroot ( ) -> String {
3134 std:: env:: var ( "MIRI_SYSROOT" )
@@ -129,6 +132,11 @@ struct PrirodaContext<'tcx> {
129132 last_location : Option < SourceLocation > ,
130133}
131134
135+ struct LocalDesc {
136+ name : Option < Symbol > ,
137+ local : Local ,
138+ ty : String ,
139+ }
132140/// Controls when execution returns to the frontend.
133141enum ResumeMode {
134142 /// Stop at the next visible MIR instruction.
@@ -336,15 +344,49 @@ impl<'tcx> PrirodaContext<'tcx> {
336344 }
337345 }
338346
339- /// Returns the names of all user-visible locals in the innermost stack frame.
347+ /// Returns structured descriptions for locals in the innermost stack frame.
340348 ///
341- /// Uses `var_debug_info` from the MIR body, which is the same source that
342- /// DWARF debug info is built from, so the names match what the user wrote .
343- fn list_locals ( & self ) -> Vec < String > {
349+ /// Starts from all MIR locals, then enriches them with source names from
350+ /// `var_debug_info` when a debug entry maps directly to a whole local .
351+ fn list_locals ( & self ) -> Vec < LocalDesc > {
344352 let Some ( frame) = self . ecx . active_thread_stack ( ) . last ( ) else {
345353 return Vec :: new ( ) ;
346354 } ;
347- frame. body ( ) . var_debug_info . iter ( ) . map ( |info| info. name . to_string ( ) ) . collect ( )
355+
356+ self . local_desc_map ( frame) . into_iter ( ) . collect ( )
357+ }
358+
359+ fn local_desc_map (
360+ & self ,
361+ frame : & Frame < ' tcx , Provenance , FrameExtra < ' tcx > > ,
362+ ) -> IndexVec < Local , LocalDesc > {
363+ // Initialize one description per MIR local so the table can be indexed by Local.
364+ let mut locals: IndexVec < Local , LocalDesc > = frame
365+ . body ( )
366+ . local_decls
367+ . iter_enumerated ( )
368+ . map ( |( id, local_decl) | {
369+ LocalDesc { name : None , local : id, ty : local_decl. ty . to_string ( ) }
370+ } )
371+ . collect ( ) ;
372+
373+ // FIXME: Some debug-info entries do not have a backing MIR local, for example
374+ // because the source variable was optimized out or is represented as a
375+ // projection. This local-indexed table cannot represent those entries yet;
376+ // the final locals list should become a `Vec<LocalDesc>` with `id : Option<Local>`, `id`
377+ // could be renamed to `local`.
378+
379+ // Attach source names from debug info when the debug entry maps directly to a whole MIR local.
380+ for var_debug_info in & frame. body ( ) . var_debug_info {
381+ if let VarDebugInfoContents :: Place ( place) = var_debug_info. value
382+ && let Some ( local) = place. as_local ( )
383+ && locals[ local] . name . is_none ( )
384+ {
385+ locals[ local] . name = Some ( var_debug_info. name ) ;
386+ }
387+ }
388+
389+ locals
348390 }
349391}
350392
@@ -366,7 +408,7 @@ enum BreakpointSetResult {
366408enum CommandResult {
367409 ExecutionStopped ( StepResult ) ,
368410 BreakpointResult ( BreakpointSetResult ) ,
369- Locals ( Vec < String > ) ,
411+ Locals ( Vec < LocalDesc > ) ,
370412 // FIXME: distinguish terminating the debugger session from disconnecting a
371413 // frontend and terminating the interpreted program once multiple frontends exist.
372414 TerminateSession ,
@@ -403,12 +445,21 @@ impl Cli {
403445
404446 BreakpointSetResult :: Duplicate => println ! ( "Duplicate breakpoint" ) ,
405447 } ,
406- CommandResult :: Locals ( names ) =>
407- if names . is_empty ( ) {
448+ CommandResult :: Locals ( locals_desc ) =>
449+ if locals_desc . is_empty ( ) {
408450 println ! ( "no locals" ) ;
409451 } else {
410- for name in & names {
411- println ! ( "{name}" ) ;
452+ for local_desc in & locals_desc {
453+ let mut name_str = "None" . to_string ( ) ;
454+ if let Some ( name) = local_desc. name {
455+ name_str = name. to_string ( ) ;
456+ }
457+ println ! (
458+ "Name: {}, Id: _{}, Ty: {}" ,
459+ name_str,
460+ local_desc. local. index( ) ,
461+ local_desc. ty,
462+ ) ;
412463 }
413464 } ,
414465 CommandResult :: TerminateSession => {
0 commit comments