diff options
| author | Samuel E. Moelius III <sam@moeli.us> | 2022-03-23 21:08:52 -0400 |
|---|---|---|
| committer | Samuel E. Moelius III <sam@moeli.us> | 2022-03-24 05:08:10 -0400 |
| commit | 65a26692fd361b794f528ead645d880527ce3de0 (patch) | |
| tree | 25a15004c43c20d0e63695a594966025cb339ea3 /clippy_lints | |
| parent | f07ee8a998eff61977d594eeac44dc1d0ead7b02 (diff) | |
| download | rust-65a26692fd361b794f528ead645d880527ce3de0.tar.gz rust-65a26692fd361b794f528ead645d880527ce3de0.zip | |
Add `crate_in_macro_def` lint
Diffstat (limited to 'clippy_lints')
| -rw-r--r-- | clippy_lints/src/crate_in_macro_def.rs | 100 | ||||
| -rw-r--r-- | clippy_lints/src/lib.register_all.rs | 1 | ||||
| -rw-r--r-- | clippy_lints/src/lib.register_correctness.rs | 1 | ||||
| -rw-r--r-- | clippy_lints/src/lib.register_lints.rs | 1 | ||||
| -rw-r--r-- | clippy_lints/src/lib.rs | 2 | ||||
| -rw-r--r-- | clippy_lints/src/utils/conf.rs | 2 |
6 files changed, 106 insertions, 1 deletions
diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs new file mode 100644 index 00000000000..9ac6d6d520c --- /dev/null +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -0,0 +1,100 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::MacroDef; +use rustc_ast::node_id::NodeId; +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; + +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 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_rules! print_message { + /// () => { + /// println!("{}", crate::MESSAGE); + /// }; + /// } + /// pub const MESSAGE: &str = "Hello!"; + /// ``` + /// Use instead: + /// ```rust + /// macro_rules! print_message { + /// () => { + /// println!("{}", $crate::MESSAGE); + /// }; + /// } + /// pub const MESSAGE: &str = "Hello!"; + /// ``` + #[clippy::version = "1.61.0"] + pub CRATE_IN_MACRO_DEF, + correctness, + "using `crate` in a macro definition" +} +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 contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> { + let mut prev_is_dollar = false; + let mut cursor = tts.trees(); + while let Some(curr) = cursor.next() { + if_chain! { + if !prev_is_dollar; + if let Some(span) = is_crate_keyword(&curr); + if let Some(next) = cursor.look_ahead(0); + if is_token(next, &TokenKind::ModSep); + then { + return Some(span); + } + } + if let TokenTree::Delimited(_, _, tts) = &curr { + let span = contains_unhygienic_crate_reference(tts); + if span.is_some() { + return span; + } + } + prev_is_dollar = is_token(&curr, &TokenKind::Dollar); + } + None +} + +fn is_crate_keyword(tt: &TokenTree) -> Option<Span> { + if_chain! { + if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt; + if symbol.as_str() == "crate"; + then { Some(*span) } else { None } + } +} + +fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool { + if let TokenTree::Token(Token { kind: other, .. }) = tt { + kind == other + } else { + false + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 132a4662676..1fb3ca1fd9b 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -37,6 +37,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), LintId::of(derivable_impls::DERIVABLE_IMPLS), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index df63f84463d..d5cade8fe99 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(drop_forget_ref::DROP_COPY), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 65ad64f1901..984939a74b8 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -97,6 +97,7 @@ store.register_lints(&[ copies::IF_SAME_THEN_ELSE, copies::SAME_FUNCTIONS_IN_IF_CONDITION, copy_iterator::COPY_ITERATOR, + crate_in_macro_def::CRATE_IN_MACRO_DEF, create_dir::CREATE_DIR, dbg_macro::DBG_MACRO, default::DEFAULT_TRAIT_ACCESS, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f2a07999144..c8b57956b1b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -190,6 +190,7 @@ mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; +mod crate_in_macro_def; mod create_dir; mod dbg_macro; mod default; @@ -867,6 +868,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ignore_publish: cargo_ignore_publish, }) }); + store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 680b2eb1da7..b1e474f8056 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); |
