diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 80cf081992cc..dd00536c9ef4 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -119,6 +119,12 @@ impl<'tcx> Visitor<'tcx> for UnsafeExprCollector<'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if expr.span.in_external_macro(self.tcx.sess.source_map()) { + // Check the content of the expanded code to still catch the unsafe + // operation passed as macro arguments. + return walk_expr(self, expr); + } + match expr.kind { // The `await` itself will desugar to two unsafe calls, but we should ignore those. // Instead, check the expression that is `await`ed diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index c1512ba3e269..d73e2673f517 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -312,4 +312,38 @@ fn check_closures() { } } +fn issue16116() { + unsafe fn foo() -> u32 { + 0 + } + + unsafe { + // Do not lint even though `format!` expansion + // contains unsafe calls. + let _ = format!("{}", foo()); + } + + unsafe { + //~^ multiple_unsafe_ops_per_block + let _ = format!("{}", foo()); + let _ = format!("{}", foo()); + } + + unsafe { + // Do not lint: only one `assert!()` argument is unsafe + assert!(foo() == 0, "{}", 1 + 2); + } + + unsafe { + //~^ multiple_unsafe_ops_per_block + assert!(foo() == 0, "{}", 1 + 2); + assert!(foo() == 0, "{}", 1 + 2); + } + + unsafe { + //~^ multiple_unsafe_ops_per_block + assert!(foo() == 0, "{}", foo()); + } +} + fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index 63f7742b734b..309f53d7f5e9 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -359,5 +359,67 @@ note: unsafe function call occurs here LL | apply(|| f(0)); | ^^^^ -error: aborting due to 16 previous errors +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:326:5 + | +LL | / unsafe { +LL | | +LL | | let _ = format!("{}", foo()); +LL | | let _ = format!("{}", foo()); +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:328:31 + | +LL | let _ = format!("{}", foo()); + | ^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:329:31 + | +LL | let _ = format!("{}", foo()); + | ^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:337:5 + | +LL | / unsafe { +LL | | +LL | | assert!(foo() == 0, "{}", 1 + 2); +LL | | assert!(foo() == 0, "{}", 1 + 2); +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:339:17 + | +LL | assert!(foo() == 0, "{}", 1 + 2); + | ^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:340:17 + | +LL | assert!(foo() == 0, "{}", 1 + 2); + | ^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:343:5 + | +LL | / unsafe { +LL | | +LL | | assert!(foo() == 0, "{}", foo()); +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:345:17 + | +LL | assert!(foo() == 0, "{}", foo()); + | ^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:345:35 + | +LL | assert!(foo() == 0, "{}", foo()); + | ^^^^^ + +error: aborting due to 19 previous errors