From 9747c80644fc9f705029c254ca1d14668a72f904 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 7 Jan 2024 00:42:31 +0100 Subject: [PATCH] new lint: `macro_metavars_in_unsafe` --- CHANGELOG.md | 2 + book/src/lint_configuration.md | 10 + clippy_config/src/conf.rs | 4 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 8 + clippy_lints/src/macro_metavars_in_unsafe.rs | 256 +++++++++++++++++ .../macro_metavars_in_unsafe/default/test.rs | 260 ++++++++++++++++++ .../default/test.stderr | 187 +++++++++++++ .../private/clippy.toml | 1 + .../macro_metavars_in_unsafe/private/test.rs | 15 + .../private/test.stderr | 17 ++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + 12 files changed, 764 insertions(+) create mode 100644 clippy_lints/src/macro_metavars_in_unsafe.rs create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/default/test.rs create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/private/test.rs create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 75879a99952..cfff0bb50a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5448,6 +5448,7 @@ Released 2018-09-13 [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal +[`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert @@ -6005,4 +6006,5 @@ Released 2018-09-13 [`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold [`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold [`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports +[`warn-unsafe-macro-metavars-in-private-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-unsafe-macro-metavars-in-private-macros diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index bd8c6d14cd5..b049a6106f5 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -922,3 +922,13 @@ Whether to allow certain wildcard imports (prelude, super in tests). * [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) +## `warn-unsafe-macro-metavars-in-private-macros` +Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. + +**Default Value:** `false` + +--- +**Affected lints:** +* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe) + + diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 642abd4f3e7..a4b29afa777 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -632,6 +632,10 @@ define_Conf! { /// default configuration of Clippy. By default, any configuration will replace the default value. (allow_renamed_params_for: Vec = DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()), + /// Lint: MACRO_METAVARS_IN_UNSAFE. + /// + /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. + (warn_unsafe_macro_metavars_in_private_macros: bool = false), } /// Search for the configuration file. diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index ed94eaee23f..6850ec821e8 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -296,6 +296,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::loops::WHILE_IMMUTABLE_CONDITION_INFO, crate::loops::WHILE_LET_LOOP_INFO, crate::loops::WHILE_LET_ON_ITERATOR_INFO, + crate::macro_metavars_in_unsafe::MACRO_METAVARS_IN_UNSAFE_INFO, crate::macro_use::MACRO_USE_IMPORTS_INFO, crate::main_recursion::MAIN_RECURSION_INFO, crate::manual_assert::MANUAL_ASSERT_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9bd40a711a..c3ae36a5597 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -193,6 +193,7 @@ mod lifetimes; mod lines_filter_map_ok; mod literal_representation; mod loops; +mod macro_metavars_in_unsafe; mod macro_use; mod main_recursion; mod manual_assert; @@ -599,6 +600,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { blacklisted_names: _, cyclomatic_complexity_threshold: _, + warn_unsafe_macro_metavars_in_private_macros, } = *conf; let msrv = || msrv.clone(); @@ -1155,6 +1157,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); + store.register_late_pass(move |_| { + Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe { + warn_unsafe_macro_metavars_in_private_macros, + ..Default::default() + }) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs new file mode 100644 index 00000000000..aea3d26e187 --- /dev/null +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -0,0 +1,256 @@ +use std::collections::btree_map::Entry; +use std::collections::BTreeMap; + +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::is_lint_allowed; +use itertools::Itertools; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::{sym, Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Looks for macros that expand metavariables in an unsafe block. + /// + /// ### Why is this bad? + /// This hides an unsafe block and allows the user of the macro to write unsafe code without an explicit + /// unsafe block at callsite, making it possible to perform unsafe operations in seemingly safe code. + /// + /// The macro should be restructured so that these metavariables are referenced outside of unsafe blocks + /// and that the usual unsafety checks apply to the macro argument. + /// + /// This is usually done by binding it to a variable outside of the unsafe block + /// and then using that variable inside of the block as shown in the example, or by referencing it a second time + /// in a safe context, e.g. `if false { $expr }`. + /// + /// ### Known limitations + /// Due to how macros are represented in the compiler at the time Clippy runs its lints, + /// it's not possible to look for metavariables in macro definitions directly. + /// + /// Instead, this lint looks at expansions of macros. + /// This leads to false negatives for macros that are never actually invoked. + /// + /// By default, this lint is rather conservative and will only emit warnings on publicly-exported + /// macros from the same crate, because oftentimes private internal macros are one-off macros where + /// this lint would just be noise (e.g. macros that generate `impl` blocks). + /// The default behavior should help with preventing a high number of such false positives, + /// however it can be configured to also emit warnings in private macros if desired. + /// + /// ### Example + /// ```no_run + /// /// Gets the first element of a slice + /// macro_rules! first { + /// ($slice:expr) => { + /// unsafe { + /// let slice = $slice; // ⚠️ expansion inside of `unsafe {}` + /// + /// assert!(!slice.is_empty()); + /// // SAFETY: slice is checked to have at least one element + /// slice.first().unwrap_unchecked() + /// } + /// } + /// } + /// + /// assert_eq!(*first!(&[1i32]), 1); + /// + /// // This will compile as a consequence (note the lack of `unsafe {}`) + /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1); + /// ``` + /// Use instead: + /// ```compile_fail + /// macro_rules! first { + /// ($slice:expr) => {{ + /// let slice = $slice; // ✅ outside of `unsafe {}` + /// unsafe { + /// assert!(!slice.is_empty()); + /// // SAFETY: slice is checked to have at least one element + /// slice.first().unwrap_unchecked() + /// } + /// }} + /// } + /// + /// assert_eq!(*first!(&[1]), 1); + /// + /// // This won't compile: + /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1); + /// ``` + #[clippy::version = "1.80.0"] + pub MACRO_METAVARS_IN_UNSAFE, + suspicious, + "expanding macro metavariables in an unsafe block" +} +impl_lint_pass!(ExprMetavarsInUnsafe => [MACRO_METAVARS_IN_UNSAFE]); + +#[derive(Clone, Debug)] +pub enum MetavarState { + ReferencedInUnsafe { unsafe_blocks: Vec }, + ReferencedInSafe, +} + +#[derive(Default)] +pub struct ExprMetavarsInUnsafe { + pub warn_unsafe_macro_metavars_in_private_macros: bool, + /// A metavariable can be expanded more than once, potentially across multiple bodies, so it + /// requires some state kept across HIR nodes to make it possible to delay a warning + /// and later undo: + /// + /// ```ignore + /// macro_rules! x { + /// ($v:expr) => { + /// unsafe { $v; } // unsafe context, it might be possible to emit a warning here, so add it to the map + /// + /// $v; // `$v` expanded another time but in a safe context, set to ReferencedInSafe to suppress + /// } + /// } + /// ``` + pub metavar_expns: BTreeMap, +} + +struct BodyVisitor<'a, 'tcx> { + /// Stack of unsafe blocks -- the top item always represents the last seen unsafe block from + /// within a relevant macro. + macro_unsafe_blocks: Vec, + /// When this is >0, it means that the node currently being visited is "within" a + /// macro definition. This is not necessary for correctness, it merely helps reduce the number + /// of spans we need to insert into the map, since only spans from macros are relevant. + expn_depth: u32, + cx: &'a LateContext<'tcx>, + lint: &'a mut ExprMetavarsInUnsafe, +} + +fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export)) + && !cx.tcx.is_doc_hidden(def_id) +} + +impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { + fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { + let from_expn = s.span.from_expansion(); + if from_expn { + self.expn_depth += 1; + } + walk_stmt(self, s); + if from_expn { + self.expn_depth -= 1; + } + } + + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + let ctxt = e.span.ctxt(); + + if let ExprKind::Block(block, _) = e.kind + && let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules + && !ctxt.is_root() + && let Some(macro_def_id) = ctxt.outer_expn_data().macro_def_id + && let Some(macro_def_id) = macro_def_id.as_local() + && (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id)) + { + self.macro_unsafe_blocks.push(block.hir_id); + walk_block(self, block); + self.macro_unsafe_blocks.pop(); + } else if ctxt.is_root() && self.expn_depth > 0 { + let unsafe_block = self.macro_unsafe_blocks.last().copied(); + + match (self.lint.metavar_expns.entry(e.span), unsafe_block) { + (Entry::Vacant(e), None) => { + e.insert(MetavarState::ReferencedInSafe); + }, + (Entry::Vacant(e), Some(unsafe_block)) => { + e.insert(MetavarState::ReferencedInUnsafe { + unsafe_blocks: vec![unsafe_block], + }); + }, + (Entry::Occupied(mut e), None) => { + if let MetavarState::ReferencedInUnsafe { .. } = *e.get() { + e.insert(MetavarState::ReferencedInSafe); + } + }, + (Entry::Occupied(mut e), Some(unsafe_block)) => { + if let MetavarState::ReferencedInUnsafe { unsafe_blocks } = e.get_mut() + && !unsafe_blocks.contains(&unsafe_block) + { + unsafe_blocks.push(unsafe_block); + } + }, + } + + // NB: No need to visit descendant nodes. They're guaranteed to represent the same + // metavariable + } else { + walk_expr(self, e); + } + } +} + +impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx rustc_hir::Body<'tcx>) { + if is_lint_allowed(cx, MACRO_METAVARS_IN_UNSAFE, body.value.hir_id) { + return; + } + + // This BodyVisitor is separate and not part of the lint pass because there is no + // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span + + let mut vis = BodyVisitor { + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if body.value.span.from_expansion() { 1 } else { 0 }, + macro_unsafe_blocks: Vec::new(), + lint: self, + cx + }; + vis.visit_body(body); + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + // Aggregate all unsafe blocks from all spans: + // ``` + // macro_rules! x { + // ($w:expr, $x:expr, $y:expr) => { $w; unsafe { $w; $x; }; unsafe { $x; $y; }; } + // } + // $w: [] (unsafe#0 is never added because it was referenced in a safe context) + // $x: [unsafe#0, unsafe#1] + // $y: [unsafe#1] + // ``` + // We want to lint unsafe blocks #0 and #1 + let bad_unsafe_blocks = self + .metavar_expns + .iter() + .filter_map(|(_, state)| match state { + MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()), + MetavarState::ReferencedInSafe => None, + }) + .flatten() + .copied() + .map(|id| { + // Remove the syntax context to hide "in this macro invocation" in the diagnostic. + // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything + // related to the callsite. + let span = cx.tcx.hir().span(id); + + (id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None)) + }) + .dedup_by(|&(_, a), &(_, b)| a == b); + + for (id, span) in bad_unsafe_blocks { + span_lint_hir_and_then( + cx, + MACRO_METAVARS_IN_UNSAFE, + id, + span, + "this macro expands metavariables in an unsafe block", + |diag| { + diag.note("this allows the user of the macro to write unsafe code outside of an unsafe block"); + diag.help( + "consider expanding any metavariables outside of this block, e.g. by storing them in a variable", + ); + diag.help( + "... or also expand referenced metavariables in a safe context to require an unsafe block at callsite", + ); + }, + ); + } + } +} diff --git a/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs new file mode 100644 index 00000000000..f5e01b431ad --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs @@ -0,0 +1,260 @@ +//! Tests macro_metavars_in_unsafe with default configuration +#![feature(decl_macro, lint_reasons)] +#![warn(clippy::macro_metavars_in_unsafe)] +#![allow(clippy::no_effect)] + +#[macro_export] +macro_rules! allow_works { + ($v:expr) => { + #[expect(clippy::macro_metavars_in_unsafe)] + unsafe { + $v; + }; + }; +} + +#[macro_export] +macro_rules! simple { + ($v:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + dbg!($v); + } + }; +} + +#[macro_export] +#[rustfmt::skip] // for some reason rustfmt rewrites $r#unsafe to r#u$nsafe, bug? +macro_rules! raw_symbol { + ($r#mod:expr, $r#unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $r#mod; + } + $r#unsafe; + }; +} + +#[macro_export] +macro_rules! multilevel_unsafe { + ($v:expr) => { + unsafe { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + } + }; +} + +#[macro_export] +macro_rules! in_function { + ($v:expr) => { + unsafe { + fn f() { + // function introduces a new body, so don't lint. + $v; + } + } + }; +} + +#[macro_export] +macro_rules! in_function_with_unsafe { + ($v:expr) => { + unsafe { + fn f() { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + } + } + }; +} + +#[macro_export] +macro_rules! const_static { + ($c:expr, $s:expr) => { + unsafe { + // const and static introduces new body, don't lint + const _X: i32 = $c; + static _Y: i32 = $s; + } + }; +} + +#[macro_export] +macro_rules! const_generic_in_struct { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + struct Ty< + const L: i32 = 1, + const M: i32 = { + 1; + unsafe { $inside_unsafe } + //~^ ERROR: this macro expands metavariables in an unsafe block + }, + const N: i32 = { $outside_unsafe }, + >; + } + }; +} + +#[macro_export] +macro_rules! fn_with_const_generic { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + fn f() { + $outside_unsafe; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + } + } + } + }; +} + +#[macro_export] +macro_rules! variables { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + let inside_unsafe = 1; + inside_unsafe; + } + $outside_unsafe; + let outside_unsafe = 1; + outside_unsafe; + }; +} + +#[macro_export] +macro_rules! multiple_matchers { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + } + $outside_unsafe; + }; + ($($v:expr, $x:expr),+) => { + $( + $v; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + } + );+ + }; +} + +#[macro_export] +macro_rules! multiple_unsafe_blocks { + ($w:expr, $x:expr, $y:expr) => { + $w; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + } + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + $y; + } + }; +} + +pub macro macro2_0($v:expr) { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } +} + +// don't lint private macros with the default configuration +macro_rules! private_mac { + ($v:expr) => { + unsafe { + $v; + } + }; +} + +// don't lint exported macros that are doc(hidden) because they also aren't part of the public API +#[macro_export] +#[doc(hidden)] +macro_rules! exported_but_hidden { + ($v:expr) => { + unsafe { + $v; + } + }; +} + +// don't lint if the same metavariable is expanded in an unsafe block and then outside of one: +// unsafe {} is still needed at callsite so not problematic +#[macro_export] +macro_rules! does_require_unsafe { + ($v:expr) => { + unsafe { + $v; + } + $v; + }; +} + +#[macro_export] +macro_rules! unsafe_from_root_ctxt { + ($v:expr) => { + // Expands to unsafe { 1 }, but the unsafe block is from the root ctxt and not this macro, + // so no warning. + $v; + }; +} + +// invoked from another macro, should still generate a warning +#[macro_export] +macro_rules! nested_macro_helper { + ($v:expr) => {{ + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + }}; +} + +#[macro_export] +macro_rules! nested_macros { + ($v:expr, $v2:expr) => {{ + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + nested_macro_helper!($v); + $v; + } + }}; +} + +fn main() { + allow_works!(1); + simple!(1); + raw_symbol!(1, 1); + multilevel_unsafe!(1); + in_function!(1); + in_function_with_unsafe!(1); + const_static!(1, 1); + const_generic_in_struct!(1, 1); + fn_with_const_generic!(1, 1); + variables!(1, 1); + multiple_matchers!(1, 1); + multiple_matchers!(1, 1, 1, 1); + macro2_0!(1); + private_mac!(1); + exported_but_hidden!(1); + does_require_unsafe!(1); + multiple_unsafe_blocks!(1, 1, 1); + unsafe_from_root_ctxt!(unsafe { 1 }); + nested_macros!(1, 1); +} diff --git a/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr b/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr new file mode 100644 index 00000000000..d6b97f6fde1 --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr @@ -0,0 +1,187 @@ +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:19:9 + | +LL | / unsafe { +LL | | +LL | | dbg!($v); +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:30:9 + | +LL | / unsafe { +LL | | +LL | | $r#mod; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:42:13 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_____________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:67:17 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_________________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:95:21 + | +LL | unsafe { $inside_unsafe } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:110:17 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | } + | |_________________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:122:9 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | let inside_unsafe = 1; +LL | | inside_unsafe; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:137:9 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:146:13 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | } + | |_____________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:171:5 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_____^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:158:9 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:162:9 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | $y; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:222:9 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:232:9 + | +LL | / unsafe { +LL | | +LL | | nested_macro_helper!($v); +LL | | $v; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: aborting due to 14 previous errors + diff --git a/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml b/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml new file mode 100644 index 00000000000..d4bbc2a1be8 --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml @@ -0,0 +1 @@ +warn-unsafe-macro-metavars-in-private-macros = true diff --git a/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs b/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs new file mode 100644 index 00000000000..2bbe1fa7b7f --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs @@ -0,0 +1,15 @@ +//! Tests macro_metavars_in_unsafe with private (non-exported) macros +#![warn(clippy::macro_metavars_in_unsafe)] + +macro_rules! mac { + ($v:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + dbg!($v); + } + }; +} + +fn main() { + mac!(1); +} diff --git a/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr b/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr new file mode 100644 index 00000000000..f9c418b2218 --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr @@ -0,0 +1,17 @@ +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/private/test.rs:6:9 + | +LL | / unsafe { +LL | | +LL | | dbg!($v); +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index e1ba60911fe..e42046ddef9 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -75,6 +75,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1 | LL | foobar = 42 @@ -157,6 +158,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1 | LL | barfoo = 53 @@ -239,6 +241,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1 | LL | allow_mixed_uninlined_format_args = true