diff --git a/src/mir/join_ir/lowering/error_tags.rs b/src/mir/join_ir/lowering/error_tags.rs index b3c4e1ab..0b09e97c 100644 --- a/src/mir/join_ir/lowering/error_tags.rs +++ b/src/mir/join_ir/lowering/error_tags.rs @@ -34,6 +34,27 @@ pub fn freeze(diagnostic: &str) -> String { format!("[joinir/freeze] {}", diagnostic) } +/// Create error message with hint (Phase 109) +/// +/// Format: "[joinir//] Hint: " +/// +/// # Panics +/// Panics if hint is empty (must provide actionable fix suggestion) +/// +/// # Example +/// ```rust,ignore +/// return Err(freeze_with_hint( +/// "phase107/balanced_depth_scan/missing_tail_inc", +/// "tail increment not found", +/// "add 'i = i + 1' at top-level" +/// )); +/// // Output: "[joinir/phase107/balanced_depth_scan/missing_tail_inc] tail increment not found Hint: add 'i = i + 1' at top-level" +/// ``` +pub fn freeze_with_hint(tag: &str, msg: &str, hint: &str) -> String { + assert!(!hint.is_empty(), "hint must not be empty (tag: {})", tag); + format!("[joinir/{}] {} Hint: {}", tag, msg, hint) +} + /// ExitLine contract violation - Phase 81+ exit metadata contract broken /// /// Used when exit line reconnection or exit PHI construction violates @@ -133,4 +154,22 @@ mod tests { assert!(err.contains("[joinir/lowering/boundary]")); assert!(err.contains("Invalid block")); } + + #[test] + fn test_freeze_with_hint_format() { + let result = freeze_with_hint( + "phase107/balanced_depth_scan/missing_tail_inc", + "tail increment not found", + "add 'i = i + 1' at top-level", + ); + assert!(result.contains("[joinir/phase107/balanced_depth_scan/missing_tail_inc]")); + assert!(result.contains("Hint:")); + assert!(result.contains("add 'i = i + 1'")); + } + + #[test] + #[should_panic(expected = "hint must not be empty")] + fn test_freeze_with_hint_empty_hint_panics() { + freeze_with_hint("test/tag", "message", ""); + } }