about summary refs log tree commit diff
path: root/clippy_lints
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints')
-rw-r--r--clippy_lints/src/crate_in_macro_def.rs60
-rw-r--r--clippy_lints/src/utils/conf.rs2
2 files changed, 44 insertions, 18 deletions
diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs
index de9c7ee5f20..21c65f9d6f0 100644
--- a/clippy_lints/src/crate_in_macro_def.rs
+++ b/clippy_lints/src/crate_in_macro_def.rs
@@ -1,24 +1,24 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use rustc_ast::ast::MacroDef;
-use rustc_ast::node_id::NodeId;
+use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind};
 use rustc_ast::token::{Token, TokenKind};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
+use rustc_span::{symbol::sym, Span};
 
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for use of `crate` as opposed to `$crate` in a macro definition.
     ///
     /// ### Why is this bad?
-    /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro
-    /// definition's crate. Rarely is the former intended. See:
+    /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's
+    /// crate. Rarely is the former intended. See:
     /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
     ///
     /// ### Example
     /// ```rust
+    /// #[macro_export]
     /// macro_rules! print_message {
     ///     () => {
     ///         println!("{}", crate::MESSAGE);
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// ```
     /// Use instead:
     /// ```rust
+    /// #[macro_export]
     /// macro_rules! print_message {
     ///     () => {
     ///         println!("{}", $crate::MESSAGE);
@@ -35,6 +36,13 @@ declare_clippy_lint! {
     /// }
     /// pub const MESSAGE: &str = "Hello!";
     /// ```
+    ///
+    /// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the
+    /// macro definition, e.g.:
+    /// ```rust,ignore
+    /// #[allow(clippy::crate_in_macro_def)]
+    /// macro_rules! ok { ... crate::foo ... }
+    /// ```
     #[clippy::version = "1.61.0"]
     pub CRATE_IN_MACRO_DEF,
     correctness,
@@ -43,18 +51,36 @@ declare_clippy_lint! {
 declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
 
 impl EarlyLintPass for CrateInMacroDef {
-    fn check_mac_def(&mut self, cx: &EarlyContext<'_>, macro_def: &MacroDef, _: NodeId) {
-        let tts = macro_def.body.inner_tokens();
-        if let Some(span) = contains_unhygienic_crate_reference(&tts) {
-            span_lint_and_sugg(
-                cx,
-                CRATE_IN_MACRO_DEF,
-                span,
-                "reference to the macro call's crate, which is rarely intended",
-                "if reference to the macro definition's crate is intended, use",
-                String::from("$crate"),
-                Applicability::MachineApplicable,
-            );
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        if_chain! {
+            if item.attrs.iter().any(is_macro_export);
+            if let ItemKind::MacroDef(macro_def) = &item.kind;
+            let tts = macro_def.body.inner_tokens();
+            if let Some(span) = contains_unhygienic_crate_reference(&tts);
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    CRATE_IN_MACRO_DEF,
+                    span,
+                    "reference to the macro call's crate, which is rarely intended",
+                    "if reference to the macro definition's crate is intended, use",
+                    String::from("$crate"),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
+
+fn is_macro_export(attr: &Attribute) -> bool {
+    if_chain! {
+        if let AttrKind::Normal(attr_item, _) = &attr.kind;
+        if let [segment] = attr_item.path.segments.as_slice();
+        if segment.ident.name == sym::macro_export;
+        then {
+            true
+        } else {
+            false
         }
     }
 }
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index b1e474f8056..680b2eb1da7 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -123,7 +123,7 @@ macro_rules! define_Conf {
 
         #[cfg(feature = "internal")]
         pub mod metadata {
-            use $crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
+            use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
 
             macro_rules! wrap_option {
                 () => (None);