sema_eval/
debug_session.rs1use std::cell::Cell;
15
16thread_local! {
17 static DEBUG_SESSION_ACTIVE: Cell<bool> = const { Cell::new(false) };
19 static WARNED_LOAD_BYPASS: Cell<bool> = const { Cell::new(false) };
22}
23
24pub fn set_debug_session_active(active: bool) {
29 DEBUG_SESSION_ACTIVE.with(|c| c.set(active));
30 if active {
31 WARNED_LOAD_BYPASS.with(|c| c.set(false));
32 }
33}
34
35pub fn is_debug_session_active() -> bool {
37 DEBUG_SESSION_ACTIVE.with(|c| c.get())
38}
39
40pub fn warn_load_bypass_once(form: &str, path: &str) {
46 if !is_debug_session_active() {
47 return;
48 }
49 let already = WARNED_LOAD_BYPASS.with(|c| c.replace(true));
50 if already {
51 return;
52 }
53 sema_core::write_stderr(&format!(
54 "Debugger: code reached via ({form} \"{path}\") is not stepped by the \
55 debugger (it runs outside the attached debug session), so breakpoints \
56 set in dynamically loaded or imported files are not hit. Stepping the \
57 main program is unaffected. (This warning is shown once per debug \
58 session.)\n"
59 ));
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use std::sync::{Arc, Mutex};
66
67 fn capture_stderr(f: impl FnOnce()) -> String {
69 let buf = Arc::new(Mutex::new(String::new()));
70 let buf_hook = buf.clone();
71 sema_core::set_stderr_hook(Some(Box::new(move |s: &str| {
72 buf_hook.lock().unwrap().push_str(s);
73 })));
74 f();
75 sema_core::set_stderr_hook(None);
76 let out = buf.lock().unwrap().clone();
77 out
78 }
79
80 #[test]
81 fn no_warning_when_session_inactive() {
82 set_debug_session_active(false);
83 let out = capture_stderr(|| {
84 warn_load_bypass_once("load", "helpers.sema");
85 });
86 assert!(out.is_empty(), "should not warn outside a debug session");
87 }
88
89 #[test]
90 fn warns_once_per_session() {
91 set_debug_session_active(true);
92 let out = capture_stderr(|| {
93 warn_load_bypass_once("load", "helpers.sema");
94 warn_load_bypass_once("import", "other.sema");
95 });
96 assert!(out.contains("not stepped by the debugger"));
97 assert!(out.contains("helpers.sema"));
98 assert!(!out.contains("other.sema"));
100 set_debug_session_active(true);
102 let out2 = capture_stderr(|| {
103 warn_load_bypass_once("import", "again.sema");
104 });
105 assert!(out2.contains("again.sema"));
106 set_debug_session_active(false);
107 }
108}