Summary
In Rust-for-Linux we use a procedural macro to apply a rewrite before delegating to core::format_args!; we wrap all arguments in Adapter which allows us to implement Display for types that do not implement it in core e.g. core::ffi::CStr.
It turns out that annotated macros which internally apply this rewrite are somehow prevented from triggering uninlined_format_args. Note that the repro I've provided is not perfect; rewriting to inline format args there would produce broken code, but the actual macro in the kernel is smart enough to rewrite foo!("{i}") into format_args!("{i}", i = Adapter(&i)).
Please note there are good reasons for both decisions mentioned at the top: implementing Display implies being able to round trip through ToString and FromStr, but in the kernel the ergonomics of having Display on CStr are important. Let's refrain from litigating these decisions here.
Lint Name
uninlined_format_args
Reproducer
I tried this code playground:
#![warn(clippy::uninlined_format_args)]
use std::fmt;
struct Adapter<T>(T);
impl<T: fmt::Display> fmt::Display for Adapter<&T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[clippy::format_args]
macro_rules! forwarding_format_args {
($($args:tt)*) => {
format_args!($($args)*)
};
}
#[clippy::format_args]
macro_rules! adapted_format_args {
($fmt:literal, $arg:expr) => {
format_args!($fmt, Adapter(&($arg)))
};
}
#[clippy::format_args]
macro_rules! forwarding_adapted_format_args {
($($args:tt)*) => {
adapted_format_args!($($args)*)
};
}
pub fn demo(x: i32) {
#[expect(clippy::uninlined_format_args)]
let _ = format_args!("{}", x);
#[expect(clippy::uninlined_format_args)]
let _ = forwarding_format_args!("{}", x);
// Expected: `#[clippy::format_args]` should make this equivalent to a
// formatting macro invocation and warn that `x` can be inlined.
//
// Actual: Clippy does not warn because the macro expansion wraps `x` in
// `Adapter(&x)` before the eventual `format_args!` invocation.
#[expect(clippy::uninlined_format_args)]
let _ = adapted_format_args!("{}", x);
#[expect(clippy::uninlined_format_args)]
let _ = forwarding_adapted_format_args!("{}", x);
}
I expected to see this happen:
No warnings.
Instead, this happened:
Checking playground v0.0.1 (/playground)
warning: this lint expectation is unfulfilled
--> src/lib.rs:45:14
|
45 | #[expect(clippy::uninlined_format_args)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
warning: this lint expectation is unfulfilled
--> src/lib.rs:47:14
|
47 | #[expect(clippy::uninlined_format_args)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: `playground` (lib) generated 2 warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.61s
Version
Summary
In Rust-for-Linux we use a procedural macro to apply a rewrite before delegating to
core::format_args!; we wrap all arguments inAdapterwhich allows us to implementDisplayfor types that do not implement it incoree.g.core::ffi::CStr.It turns out that annotated macros which internally apply this rewrite are somehow prevented from triggering
uninlined_format_args. Note that the repro I've provided is not perfect; rewriting to inline format args there would produce broken code, but the actual macro in the kernel is smart enough to rewritefoo!("{i}")intoformat_args!("{i}", i = Adapter(&i)).Please note there are good reasons for both decisions mentioned at the top: implementing
Displayimplies being able to round trip throughToStringandFromStr, but in the kernel the ergonomics of havingDisplayonCStrare important. Let's refrain from litigating these decisions here.Lint Name
uninlined_format_args
Reproducer
I tried this code playground:
I expected to see this happen:
No warnings.
Instead, this happened:
Version