1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use rustc_ast::ast::MacroDef;
3 use rustc_ast::node_id::NodeId;
4 use rustc_ast::token::{Token, TokenKind};
5 use rustc_ast::tokenstream::{TokenStream, TokenTree};
6 use rustc_errors::Applicability;
7 use rustc_lint::{EarlyContext, EarlyLintPass};
8 use rustc_session::{declare_lint_pass, declare_tool_lint};
11 declare_clippy_lint! {
13 /// Checks for use of `crate` as opposed to `$crate` in a macro definition.
15 /// ### Why is this bad?
16 /// `crate` refers to macro call's crate, whereas `$crate` refers to the macro
17 /// definition's crate. Rarely is the former intended. See:
18 /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
22 /// macro_rules! print_message {
24 /// println!("{}", crate::MESSAGE);
27 /// pub const MESSAGE: &str = "Hello!";
31 /// macro_rules! print_message {
33 /// println!("{}", $crate::MESSAGE);
36 /// pub const MESSAGE: &str = "Hello!";
38 #[clippy::version = "1.61.0"]
39 pub CRATE_IN_MACRO_DEF,
41 "using `crate` in a macro definition"
43 declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
45 impl 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) {
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,
62 fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
63 let mut prev_is_dollar = false;
64 let mut cursor = tts.trees();
65 while let Some(curr) = cursor.next() {
68 if let Some(span) = is_crate_keyword(&curr);
69 if let Some(next) = cursor.look_ahead(0);
70 if is_token(next, &TokenKind::ModSep);
75 if let TokenTree::Delimited(_, _, tts) = &curr {
76 let span = contains_unhygienic_crate_reference(tts);
81 prev_is_dollar = is_token(&curr, &TokenKind::Dollar);
86 fn is_crate_keyword(tt: &TokenTree) -> Option<Span> {
88 if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt;
89 if symbol.as_str() == "crate";
90 then { Some(*span) } else { None }
94 fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool {
95 if let TokenTree::Token(Token { kind: other, .. }) = tt {