[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master)
-## Rust 1.59 (beta)
+## Rust 1.60 (beta)
Current beta, release 2022-04-07
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
+[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
if_chain = "1.0"
itertools = "0.10.1"
quote = "1.0"
-serde = { version = "1.0", features = ["derive"] }
+serde = { version = "1.0.125", features = ["derive"] }
syn = { version = "1.0", features = ["full"] }
futures = "0.3"
parking_lot = "0.11.2"
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+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::{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:
+ /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
+ ///
+ /// ### Example
+ /// ```rust
+ /// #[macro_export]
+ /// macro_rules! print_message {
+ /// () => {
+ /// println!("{}", crate::MESSAGE);
+ /// };
+ /// }
+ /// pub const MESSAGE: &str = "Hello!";
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// #[macro_export]
+ /// macro_rules! print_message {
+ /// () => {
+ /// println!("{}", $crate::MESSAGE);
+ /// };
+ /// }
+ /// 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,
+ suspicious,
+ "using `crate` in a macro definition"
+}
+declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
+
+impl EarlyLintPass for CrateInMacroDef {
+ 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,
+ "`crate` references the macro call's crate",
+ "to reference the macro definition's crate, 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();
+ then {
+ segment.ident.name == sym::macro_export
+ } else {
+ false
+ }
+ }
+}
+
+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
+ }
+}
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),
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,
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
+ LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
mod comparison_chain;
mod copies;
mod copy_iterator;
+mod crate_in_macro_def;
mod create_dir;
mod dbg_macro;
mod default;
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`
}
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet;
+use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{iter_input_pats, method_chain_args};
use if_chain::if_chain;
let fn_arg = &map_args[1];
if is_unit_function(cx, fn_arg) {
+ let mut applicability = Applicability::MachineApplicable;
let msg = suggestion_msg("function", map_type);
let suggestion = format!(
"if let {0}({binding}) = {1} {{ {2}({binding}) }}",
variant,
- snippet(cx, var_arg.span, "_"),
- snippet(cx, fn_arg.span, "_"),
+ snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
+ snippet_with_applicability(cx, fn_arg.span, "_", &mut applicability),
binding = let_binding_name(cx, var_arg)
);
span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
- diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable);
+ diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
});
} else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
let msg = suggestion_msg("closure", map_type);
span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
+ let mut applicability = Applicability::MachineApplicable;
let suggestion = format!(
"if let {0}({1}) = {2} {{ {3} }}",
variant,
- snippet(cx, binding.pat.span, "_"),
- snippet(cx, var_arg.span, "_"),
- snippet(cx, reduced_expr_span, "_")
- );
- diag.span_suggestion(
- stmt.span,
- "try this",
- suggestion,
- Applicability::MachineApplicable, // snippet
+ snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability),
+ snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
+ snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0,
);
+ diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
} else {
let suggestion = format!(
"if let {0}({1}) = {2} {{ ... }}",
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
if is_panic(cx, macro_call.def_id) {
+ if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+ return;
+ }
+
span_lint(
cx,
PANIC,
--- /dev/null
+// run-rustfix
+#![warn(clippy::crate_in_macro_def)]
+
+mod hygienic {
+ #[macro_export]
+ macro_rules! print_message_hygienic {
+ () => {
+ println!("{}", $crate::hygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic {
+ #[macro_export]
+ macro_rules! print_message_unhygienic {
+ () => {
+ println!("{}", $crate::unhygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic_intentionally {
+ // For cases where the use of `crate` is intentional, applying `allow` to the macro definition
+ // should suppress the lint.
+ #[allow(clippy::crate_in_macro_def)]
+ #[macro_export]
+ macro_rules! print_message_unhygienic_intentionally {
+ () => {
+ println!("{}", crate::CALLER_PROVIDED_MESSAGE);
+ };
+ }
+}
+
+#[macro_use]
+mod not_exported {
+ macro_rules! print_message_not_exported {
+ () => {
+ println!("{}", crate::not_exported::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+fn main() {
+ print_message_hygienic!();
+ print_message_unhygienic!();
+ print_message_unhygienic_intentionally!();
+ print_message_not_exported!();
+}
+
+pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!";
--- /dev/null
+// run-rustfix
+#![warn(clippy::crate_in_macro_def)]
+
+mod hygienic {
+ #[macro_export]
+ macro_rules! print_message_hygienic {
+ () => {
+ println!("{}", $crate::hygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic {
+ #[macro_export]
+ macro_rules! print_message_unhygienic {
+ () => {
+ println!("{}", crate::unhygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic_intentionally {
+ // For cases where the use of `crate` is intentional, applying `allow` to the macro definition
+ // should suppress the lint.
+ #[allow(clippy::crate_in_macro_def)]
+ #[macro_export]
+ macro_rules! print_message_unhygienic_intentionally {
+ () => {
+ println!("{}", crate::CALLER_PROVIDED_MESSAGE);
+ };
+ }
+}
+
+#[macro_use]
+mod not_exported {
+ macro_rules! print_message_not_exported {
+ () => {
+ println!("{}", crate::not_exported::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+fn main() {
+ print_message_hygienic!();
+ print_message_unhygienic!();
+ print_message_unhygienic_intentionally!();
+ print_message_not_exported!();
+}
+
+pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!";
--- /dev/null
+error: `crate` references the macro call's crate
+ --> $DIR/crate_in_macro_def.rs:19:28
+ |
+LL | println!("{}", crate::unhygienic::MESSAGE);
+ | ^^^^^ help: to reference the macro definition's crate, use: `$crate`
+ |
+ = note: `-D clippy::crate-in-macro-def` implied by `-D warnings`
+
+error: aborting due to previous error
+
if let Some(ref value) = x.field { do_nothing(value + captured) }
- if let Some(a) = option() { do_nothing(a) }}
+ if let Some(a) = option() { do_nothing(a) }
+
+ if let Some(value) = option() { println!("{:?}", value) }
+}
fn main() {}
x.field.map(|ref value| { do_nothing(value + captured) });
- option().map(do_nothing);}
+ option().map(do_nothing);
+
+ option().map(|value| println!("{:?}", value));
+}
fn main() {}
error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:83:5
|
-LL | option().map(do_nothing);}
+LL | option().map(do_nothing);
| ^^^^^^^^^^^^^^^^^^^^^^^^-
| |
| help: try this: `if let Some(a) = option() { do_nothing(a) }`
-error: aborting due to 18 previous errors
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:85:5
+ |
+LL | option().map(|value| println!("{:?}", value));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = option() { println!("{:?}", value) }`
+
+error: aborting due to 19 previous errors
-#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
#![allow(clippy::assertions_on_constants, clippy::eq_op)]
+#![feature(inline_const)]
+#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
extern crate core;
+const _: () = {
+ if 1 == 0 {
+ panic!("A balanced diet means a cupcake in each hand");
+ }
+};
+
+fn inline_const() {
+ let _ = const {
+ if 1 == 0 {
+ panic!("When nothing goes right, go left")
+ }
+ };
+}
+
fn panic() {
let a = 2;
panic!();
error: `panic` should not be present in production code
- --> $DIR/panicking_macros.rs:8:5
+ --> $DIR/panicking_macros.rs:23:5
|
LL | panic!();
| ^^^^^^^^
= note: `-D clippy::panic` implied by `-D warnings`
error: `panic` should not be present in production code
- --> $DIR/panicking_macros.rs:9:5
+ --> $DIR/panicking_macros.rs:24:5
|
LL | panic!("message");
| ^^^^^^^^^^^^^^^^^
error: `panic` should not be present in production code
- --> $DIR/panicking_macros.rs:10:5
+ --> $DIR/panicking_macros.rs:25:5
|
LL | panic!("{} {}", "panic with", "multiple arguments");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `todo` should not be present in production code
- --> $DIR/panicking_macros.rs:16:5
+ --> $DIR/panicking_macros.rs:31:5
|
LL | todo!();
| ^^^^^^^
= note: `-D clippy::todo` implied by `-D warnings`
error: `todo` should not be present in production code
- --> $DIR/panicking_macros.rs:17:5
+ --> $DIR/panicking_macros.rs:32:5
|
LL | todo!("message");
| ^^^^^^^^^^^^^^^^
error: `todo` should not be present in production code
- --> $DIR/panicking_macros.rs:18:5
+ --> $DIR/panicking_macros.rs:33:5
|
LL | todo!("{} {}", "panic with", "multiple arguments");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `unimplemented` should not be present in production code
- --> $DIR/panicking_macros.rs:24:5
+ --> $DIR/panicking_macros.rs:39:5
|
LL | unimplemented!();
| ^^^^^^^^^^^^^^^^
= note: `-D clippy::unimplemented` implied by `-D warnings`
error: `unimplemented` should not be present in production code
- --> $DIR/panicking_macros.rs:25:5
+ --> $DIR/panicking_macros.rs:40:5
|
LL | unimplemented!("message");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: `unimplemented` should not be present in production code
- --> $DIR/panicking_macros.rs:26:5
+ --> $DIR/panicking_macros.rs:41:5
|
LL | unimplemented!("{} {}", "panic with", "multiple arguments");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: usage of the `unreachable!` macro
- --> $DIR/panicking_macros.rs:32:5
+ --> $DIR/panicking_macros.rs:47:5
|
LL | unreachable!();
| ^^^^^^^^^^^^^^
= note: `-D clippy::unreachable` implied by `-D warnings`
error: usage of the `unreachable!` macro
- --> $DIR/panicking_macros.rs:33:5
+ --> $DIR/panicking_macros.rs:48:5
|
LL | unreachable!("message");
| ^^^^^^^^^^^^^^^^^^^^^^^
error: usage of the `unreachable!` macro
- --> $DIR/panicking_macros.rs:34:5
+ --> $DIR/panicking_macros.rs:49:5
|
LL | unreachable!("{} {}", "panic with", "multiple arguments");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `panic` should not be present in production code
- --> $DIR/panicking_macros.rs:40:5
+ --> $DIR/panicking_macros.rs:55:5
|
LL | panic!();
| ^^^^^^^^
error: `todo` should not be present in production code
- --> $DIR/panicking_macros.rs:41:5
+ --> $DIR/panicking_macros.rs:56:5
|
LL | todo!();
| ^^^^^^^
error: `unimplemented` should not be present in production code
- --> $DIR/panicking_macros.rs:42:5
+ --> $DIR/panicking_macros.rs:57:5
|
LL | unimplemented!();
| ^^^^^^^^^^^^^^^^
error: usage of the `unreachable!` macro
- --> $DIR/panicking_macros.rs:43:5
+ --> $DIR/panicking_macros.rs:58:5
|
LL | unreachable!();
| ^^^^^^^^^^^^^^
if let Ok(ref value) = x.field { do_nothing(value + captured) }
+
+ if let Ok(value) = x.field { println!("{:?}", value) }
}
fn main() {}
x.field.map(|ref value| { do_nothing(value + captured) });
+
+ x.field.map(|value| println!("{:?}", value));
}
fn main() {}
| |
| help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }`
-error: aborting due to 17 previous errors
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:79:5
+ |
+LL | x.field.map(|value| println!("{:?}", value));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { println!("{:?}", value) }`
+
+error: aborting due to 18 previous errors