11use clippy_utils:: diagnostics:: span_lint_and_sugg;
2- use rustc_ast:: ast:: MacroDef ;
3- use rustc_ast:: node_id:: NodeId ;
2+ use rustc_ast:: ast:: { AttrKind , Attribute , Item , ItemKind } ;
43use rustc_ast:: token:: { Token , TokenKind } ;
54use rustc_ast:: tokenstream:: { TokenStream , TokenTree } ;
65use rustc_errors:: Applicability ;
76use rustc_lint:: { EarlyContext , EarlyLintPass } ;
87use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
9- use rustc_span:: Span ;
8+ use rustc_span:: { symbol :: sym , Span } ;
109
1110declare_clippy_lint ! {
1211 /// ### What it does
1312 /// Checks for use of `crate` as opposed to `$crate` in a macro definition.
1413 ///
1514 /// ### Why is this bad?
16- /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro
17- /// definition's crate. Rarely is the former intended. See:
15+ /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's
16+ /// crate. Rarely is the former intended. See:
1817 /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
1918 ///
2019 /// ### Example
2120 /// ```rust
21+ /// #[macro_export]
2222 /// macro_rules! print_message {
2323 /// () => {
2424 /// println!("{}", crate::MESSAGE);
@@ -28,13 +28,21 @@ declare_clippy_lint! {
2828 /// ```
2929 /// Use instead:
3030 /// ```rust
31+ /// #[macro_export]
3132 /// macro_rules! print_message {
3233 /// () => {
3334 /// println!("{}", $crate::MESSAGE);
3435 /// };
3536 /// }
3637 /// pub const MESSAGE: &str = "Hello!";
3738 /// ```
39+ ///
40+ /// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the
41+ /// macro definition, e.g.:
42+ /// ```rust,ignore
43+ /// #[allow(clippy::crate_in_macro_def)]
44+ /// macro_rules! ok { ... crate::foo ... }
45+ /// ```
3846 #[ clippy:: version = "1.61.0" ]
3947 pub CRATE_IN_MACRO_DEF ,
4048 correctness,
@@ -43,18 +51,36 @@ declare_clippy_lint! {
4351declare_lint_pass ! ( CrateInMacroDef => [ CRATE_IN_MACRO_DEF ] ) ;
4452
4553impl EarlyLintPass for CrateInMacroDef {
46- fn check_mac_def ( & mut self , cx : & EarlyContext < ' _ > , macro_def : & MacroDef , _: NodeId ) {
47- let tts = macro_def. body . inner_tokens ( ) ;
48- if let Some ( span) = contains_unhygienic_crate_reference ( & tts) {
49- span_lint_and_sugg (
50- cx,
51- CRATE_IN_MACRO_DEF ,
52- span,
53- "reference to the macro call's crate, which is rarely intended" ,
54- "if reference to the macro definition's crate is intended, use" ,
55- String :: from ( "$crate" ) ,
56- Applicability :: MachineApplicable ,
57- ) ;
54+ fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
55+ if_chain ! {
56+ if item. attrs. iter( ) . any( is_macro_export) ;
57+ if let ItemKind :: MacroDef ( macro_def) = & item. kind;
58+ let tts = macro_def. body. inner_tokens( ) ;
59+ if let Some ( span) = contains_unhygienic_crate_reference( & tts) ;
60+ then {
61+ span_lint_and_sugg(
62+ cx,
63+ CRATE_IN_MACRO_DEF ,
64+ span,
65+ "reference to the macro call's crate, which is rarely intended" ,
66+ "if reference to the macro definition's crate is intended, use" ,
67+ String :: from( "$crate" ) ,
68+ Applicability :: MachineApplicable ,
69+ ) ;
70+ }
71+ }
72+ }
73+ }
74+
75+ fn is_macro_export ( attr : & Attribute ) -> bool {
76+ if_chain ! {
77+ if let AttrKind :: Normal ( attr_item, _) = & attr. kind;
78+ if let [ segment] = attr_item. path. segments. as_slice( ) ;
79+ if segment. ident. name == sym:: macro_export;
80+ then {
81+ true
82+ } else {
83+ false
5884 }
5985 }
6086}
0 commit comments