diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index da806918be0..6b78c162065 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use if_chain::if_chain; @@ -51,8 +51,9 @@ impl MacroRefData { #[derive(Default)] #[expect(clippy::module_name_repetitions)] pub struct MacroUseImports { - /// the actual import path used and the span of the attribute above it. - imports: Vec<(String, Span)>, + /// the actual import path used and the span of the attribute above it. The value is + /// the location, where the lint should be emitted. + imports: Vec<(String, Span, hir::HirId)>, /// the span of the macro reference, kept to ensure only one reference is used per macro call. collected: FxHashSet, mac_refs: Vec, @@ -91,7 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { if_chain! { if cx.sess().opts.edition >= Edition::Edition2018; if let hir::ItemKind::Use(path, _kind) = &item.kind; - let attrs = cx.tcx.hir().attrs(item.hir_id()); + let hir_id = item.hir_id(); + let attrs = cx.tcx.hir().attrs(hir_id); if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); if let Res::Def(DefKind::Mod, id) = path.res; if !id.is_local(); @@ -100,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { let span = mac_attr.span; let def_path = cx.tcx.def_path_str(mac_id); - self.imports.push((def_path, span)); + self.imports.push((def_path, span, hir_id)); } } } else { @@ -138,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { fn check_crate_post(&mut self, cx: &LateContext<'_>) { let mut used = FxHashMap::default(); let mut check_dup = vec![]; - for (import, span) in &self.imports { + for (import, span, hir_id) in &self.imports { let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name)); if let Some(idx) = found_idx { @@ -151,7 +153,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { [] | [_] => return, [root, item] => { if !check_dup.contains(&(*item).to_string()) { - used.entry(((*root).to_string(), span)) + used.entry(((*root).to_string(), span, hir_id)) .or_insert_with(Vec::new) .push((*item).to_string()); check_dup.push((*item).to_string()); @@ -169,13 +171,13 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { } }) .collect::>(); - used.entry(((*root).to_string(), span)) + used.entry(((*root).to_string(), span, hir_id)) .or_insert_with(Vec::new) .push(filtered.join("::")); check_dup.extend(filtered); } else { let rest = rest.to_vec(); - used.entry(((*root).to_string(), span)) + used.entry(((*root).to_string(), span, hir_id)) .or_insert_with(Vec::new) .push(rest.join("::")); check_dup.extend(rest.iter().map(ToString::to_string)); @@ -186,27 +188,33 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { } let mut suggestions = vec![]; - for ((root, span), path) in used { + for ((root, span, hir_id), path) in used { if path.len() == 1 { - suggestions.push((span, format!("{}::{}", root, path[0]))); + suggestions.push((span, format!("{}::{}", root, path[0]), hir_id)); } else { - suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")))); + suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")), hir_id)); } } // If mac_refs is not empty we have encountered an import we could not handle // such as `std::prelude::v1::foo` or some other macro that expands to an import. if self.mac_refs.is_empty() { - for (span, import) in suggestions { + for (span, import, hir_id) in suggestions { let help = format!("use {};", import); - span_lint_and_sugg( + span_lint_hir_and_then( cx, MACRO_USE_IMPORTS, + *hir_id, *span, "`macro_use` attributes are no longer needed in the Rust 2018 edition", - "remove the attribute and import the macro directly, try", - help, - Applicability::MaybeIncorrect, + |diag| { + diag.span_suggestion( + *span, + "remove the attribute and import the macro directly, try", + help, + Applicability::MaybeIncorrect, + ); + }, ); } } diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index a83c8ba0b64..e612480d264 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -4,6 +4,7 @@ // run-rustfix // ignore-32bit +#![feature(lint_reasons)] #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index e26a7545ea6..b34817cc3b2 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -4,6 +4,7 @@ // run-rustfix // ignore-32bit +#![feature(lint_reasons)] #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index 9028a636e7f..bf7b6edd0e3 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -1,28 +1,28 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:18:5 + --> $DIR/macro_use_imports.rs:23:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` | = note: `-D clippy::macro-use-imports` implied by `-D warnings` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:20:5 + --> $DIR/macro_use_imports.rs:21:5 | LL | #[macro_use] | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:22:5 - | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` - -error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:24:5 + --> $DIR/macro_use_imports.rs:25:5 | LL | #[macro_use] | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:19:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};` + error: aborting due to 4 previous errors diff --git a/tests/ui/macro_use_imports_expect.rs b/tests/ui/macro_use_imports_expect.rs new file mode 100644 index 00000000000..8a1b05da9ef --- /dev/null +++ b/tests/ui/macro_use_imports_expect.rs @@ -0,0 +1,51 @@ +// aux-build:macro_rules.rs +// aux-build:macro_use_helper.rs +// aux-build:proc_macro_derive.rs +// ignore-32bit + +#![feature(lint_reasons)] +#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] +#![allow(clippy::single_component_path_imports)] +#![warn(clippy::macro_use_imports)] + +#[macro_use] +extern crate macro_use_helper as mac; + +#[macro_use] +extern crate proc_macro_derive as mini_mac; + +mod a { + #[expect(clippy::macro_use_imports)] + #[macro_use] + use mac; + #[expect(clippy::macro_use_imports)] + #[macro_use] + use mini_mac; + #[expect(clippy::macro_use_imports)] + #[macro_use] + use mac::inner; + #[expect(clippy::macro_use_imports)] + #[macro_use] + use mac::inner::nested; + + #[derive(ClippyMiniMacroTest)] + struct Test; + + fn test() { + pub_macro!(); + inner_mod_macro!(); + pub_in_private_macro!(_var); + function_macro!(); + let v: ty_macro!() = Vec::default(); + + inner::try_err!(); + inner::foofoo!(); + nested::string_add!(); + } +} + +// issue #7015, ICE due to calling `module_children` with local `DefId` +#[macro_use] +use a as b; + +fn main() {}