From 4094c581eaf0ad082861e92e61b7b5d28a66568f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20Rouleau?= Date: Sat, 10 Oct 2026 03:05:45 -0400 Subject: [PATCH 1/2] log: format a message that has no arguments after it `log!`'s first arm took a lone string, checked it with `format!` and then logged the string itself. That arm predates inline format arguments: `warn!("failed to load {path}")` compiles, and the check formats it correctly, but the log shows `failed to load {path}`. An escaped brace, as in `"{{"`, came out doubled too. The arm is gone, so a lone string goes through the same `format!` as a string with arguments after it, and prints as `println!` would. `test_logs` now logs an inline argument at each level. Co-Authored-By: Claude Opus 5.5 --- src/log.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/log.rs b/src/log.rs index 429df2995..c3a362795 100644 --- a/src/log.rs +++ b/src/log.rs @@ -68,18 +68,6 @@ impl Ord for Level { #[macro_export(local_inner_macros)] macro_rules! log { - (target: $target:expr, $lvl:expr, $message:expr) => ({ - let lvl = $lvl; - //if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { - // ensure that $message is a valid format string literal - let _ = __log_format_args!($message); - $crate::log::__private_api_log_lit( - $message, - lvl, - &($target, __log_module_path!(), __log_file!(), __log_line!()), - ); - //} - }); (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ let lvl = $lvl; //if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { @@ -239,18 +227,25 @@ pub fn __private_api_log_lit( #[test] fn test_logs() { + let value = 1; + trace!("info"); trace!("info: {}", 1); + trace!("info: {value}"); debug!("info"); debug!("info: {}", 1); + debug!("info: {value}"); info!("info"); info!("info: {}", 1); + info!("info: {value}"); warn!("info"); warn!("info: {}", 1); + warn!("info: {value}"); error!("info"); error!("info: {}", 1); + error!("info: {value}"); } From 6c83777fa848bb69ebbcc78db88b4cae8f6d1962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20Rouleau?= Date: Sat, 10 Oct 2026 10:08:12 -0400 Subject: [PATCH 2/2] log: check what test_logs logs `test_logs` printed its messages without checking them, so it passed with the lone-string bug in place. The desktop logger now also records each message under `cfg(test)`, and the test compares them with what `format!` gives, an escaped brace included. Co-Authored-By: Claude Opus 5.5 --- src/log.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/log.rs b/src/log.rs index c3a362795..8d1f4f914 100644 --- a/src/log.rs +++ b/src/log.rs @@ -172,6 +172,8 @@ pub fn __private_api_log_lit( _level: Level, &(_target, _module_path, _file, _line): &(&str, &'static str, &'static str, u32), ) { + #[cfg(test)] + LOGGED.with(|logged| logged.borrow_mut().push(message.to_owned())); eprintln!("{}", message); } @@ -225,6 +227,12 @@ pub fn __private_api_log_lit( crate::native::ios::log(message); } +#[cfg(test)] +thread_local! { + /// Every message the desktop logger was given, so `test_logs` can check them. + static LOGGED: std::cell::RefCell> = std::cell::RefCell::new(Vec::new()); +} + #[test] fn test_logs() { let value = 1; @@ -248,4 +256,10 @@ fn test_logs() { error!("info"); error!("info: {}", 1); error!("info: {value}"); + + info!("{{value}}"); + + let mut expected = ["info", "info: 1", "info: 1"].repeat(5); + expected.push("{value}"); + LOGGED.with(|logged| assert_eq!(*logged.borrow(), expected)); }