Skip to content

Commit c0939c3

Browse files
authored
attributes: fix #[instrument(err)] with mutable parameters (#1167)
## Motivation The closure generated by `#[instrument(err)]` for non-async functions is not mutable, preventing any captured variables from being mutated. If a function has any mutable parameters that are modified within the function, adding the `#[instrument(err)]` attribute will result in a compiler error. ## Solution This change makes it so the closure is executed directly as a temporary variable instead of storing it in a named variable prior to its use, avoiding the need to explicitly declare it as mutable altogether or to add an `#[allow(unused_mut)]` annotation on the closure declaration to silence lint warnings for closures that do not need to be mutable.
1 parent f05a0f2 commit c0939c3

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

tracing-attributes/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ fn gen_body(
446446
let __tracing_attr_span = #span;
447447
tracing::Instrument::instrument(async move {
448448
match async move { #block }.await {
449+
#[allow(clippy::unit_arg)]
449450
Ok(x) => Ok(x),
450451
Err(e) => {
451452
tracing::error!(error = %e);
@@ -468,8 +469,8 @@ fn gen_body(
468469
quote_spanned!(block.span()=>
469470
let __tracing_attr_span = #span;
470471
let __tracing_attr_guard = __tracing_attr_span.enter();
471-
let f = move || #return_type { #block };
472-
match f() {
472+
match move || #return_type #block () {
473+
#[allow(clippy::unit_arg)]
473474
Ok(x) => Ok(x),
474475
Err(e) => {
475476
tracing::error!(error = %e);

tracing-attributes/tests/err.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,57 @@ fn test_async() {
8383
});
8484
handle.assert_finished();
8585
}
86+
87+
#[instrument(err)]
88+
fn err_mut(out: &mut u8) -> Result<(), TryFromIntError> {
89+
*out = u8::try_from(1234)?;
90+
Ok(())
91+
}
92+
93+
#[test]
94+
fn test_mut() {
95+
let span = span::mock().named("err_mut");
96+
let (collector, handle) = collector::mock()
97+
.new_span(span.clone())
98+
.enter(span.clone())
99+
.event(event::mock().at_level(Level::ERROR))
100+
.exit(span.clone())
101+
.drop_span(span)
102+
.done()
103+
.run_with_handle();
104+
with_default(collector, || err_mut(&mut 0).ok());
105+
handle.assert_finished();
106+
}
107+
108+
#[instrument(err)]
109+
async fn err_mut_async(polls: usize, out: &mut u8) -> Result<(), TryFromIntError> {
110+
let future = PollN::new_ok(polls);
111+
tracing::trace!(awaiting = true);
112+
future.await.ok();
113+
*out = u8::try_from(1234)?;
114+
Ok(())
115+
}
116+
117+
#[test]
118+
fn test_mut_async() {
119+
let span = span::mock().named("err_mut_async");
120+
let (collector, handle) = collector::mock()
121+
.new_span(span.clone())
122+
.enter(span.clone())
123+
.event(
124+
event::mock()
125+
.with_fields(field::mock("awaiting").with_value(&true))
126+
.at_level(Level::TRACE),
127+
)
128+
.exit(span.clone())
129+
.enter(span.clone())
130+
.event(event::mock().at_level(Level::ERROR))
131+
.exit(span.clone())
132+
.drop_span(span)
133+
.done()
134+
.run_with_handle();
135+
with_default(collector, || {
136+
block_on_future(async { err_mut_async(2, &mut 0).await }).ok();
137+
});
138+
handle.assert_finished();
139+
}

0 commit comments

Comments
 (0)