Skip to content

Conversation

@samueltardieu
Copy link
Member

@samueltardieu samueltardieu commented Nov 19, 2025

changelog: [multiple_unsafe_ops_per_block]: count unsafe operations only towards the innermost unsafe block

Fixes #16116

r? Jarcho

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Nov 19, 2025
@github-actions
Copy link

github-actions bot commented Nov 19, 2025

Lintcheck changes for 181cb9c

Lint Added Removed Changed
clippy::multiple_unsafe_ops_per_block 0 287 55

This comment will be updated if you push new changes

@Jarcho
Copy link
Contributor

Jarcho commented Nov 19, 2025

The actual issue is nested unsafe blocks, not macros. I feel like we should be skipping them.

unsafe f1() {}
unsafe f2() {}

fn f3() {
  unsafe {
    f1();
    unsafe { f2(); }
  }
}

This only has one unsafe operation in each block, just one of them happens to be nested in another.


We should still be considering a macro call to be a single thing. A macro expanding to multiple unsafe calls without an unsafe block shouldn't trigger the lint.

Also, macro calls are counted as at most one unsafe operation.
@samueltardieu samueltardieu changed the title Do not lint unsafe code coming from external macros Count unsafe operations and macro calls once towards the innermost unsafe block Nov 25, 2025
@samueltardieu
Copy link
Member Author

Done.

Comment on lines +135 to +148
_ if expr.span.from_expansion() => {
let mut inner_collector = Self {
tcx: self.tcx,
typeck_results: self.typeck_results,
unsafe_ops: vec![],
};
walk_expr(&mut inner_collector, expr);
if !inner_collector.unsafe_ops.is_empty() {
let span = expr.span.source_callsite();
self.unsafe_ops
.push(("macro call expanded to unsafe operation here", span));
}
return;
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't handle macro arguments properly.

print!("{}, {}", foo(), foo());

This has two unsafe operations added by the user, but this would currently be detected as one.


What you'll need to do is extend the visitor store what context the unsafe block is in. When detecting an unsafe operation in a different context walk the call chain up to the block's context and use the immediate callee's context as the source of the unsafe call with each callee counting for at most one unsafe operation.

If you can't walk to the block's context from the unsafe op then it's a macro argument. Not really sure the best way to count that.

when finding an unsafe operation, walk up the call chain to the unsafe block currently being checked.

Comment on lines +372 to +376
note: macro call expanded to unsafe operation here
--> tests/ui/multiple_unsafe_ops_per_block.rs:328:17
|
LL | let _ = format!("{}", foo());
| ^^^^^^^^^^^^^^^^^^^^
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also see the effect here with the macro being blamed for the foo call even though it's unrelated.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Nov 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

multiple_unsafe_ops_per_block triggering for format! on nightly

3 participants