From ae4900c9b83b111df8eb55ac1e914e498d6a2746 Mon Sep 17 00:00:00 2001
From: xFrednet <xFrednet@gmail.com>
Date: Sat, 25 Jun 2022 14:16:32 +0200
Subject: [PATCH] Fix `#[expect]` for `clippy::macro_use_imports`

---
 clippy_lints/src/macro_use.rs        | 42 +++++++++++++----------
 tests/ui/macro_use_imports.fixed     |  1 +
 tests/ui/macro_use_imports.rs        |  1 +
 tests/ui/macro_use_imports.stderr    | 20 +++++------
 tests/ui/macro_use_imports_expect.rs | 51 ++++++++++++++++++++++++++++
 5 files changed, 88 insertions(+), 27 deletions(-)
 create mode 100644 tests/ui/macro_use_imports_expect.rs

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<Span>,
     mac_refs: Vec<MacroRefData>,
@@ -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::<Vec<_>>();
-                            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() {}