From 003a2119e7ffe70ee341ebc2bde3866783db8d9d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 27 Feb 2023 22:01:12 -0500 Subject: [PATCH 01/47] Improve diagnostic of `no_mangle_with_rust_abi` --- clippy_lints/src/no_mangle_with_rust_abi.rs | 28 ++++---- tests/ui/no_mangle_with_rust_abi.fixed | 48 ------------- tests/ui/no_mangle_with_rust_abi.rs | 2 - tests/ui/no_mangle_with_rust_abi.stderr | 74 +++++++++++++++------ 4 files changed, 71 insertions(+), 81 deletions(-) delete mode 100644 tests/ui/no_mangle_with_rust_abi.fixed diff --git a/clippy_lints/src/no_mangle_with_rust_abi.rs b/clippy_lints/src/no_mangle_with_rust_abi.rs index bc64ccb295c..8fd9ae351a0 100644 --- a/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -1,9 +1,10 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Pos}; use rustc_target::spec::abi::Abi; declare_clippy_lint! { @@ -38,25 +39,28 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Fn(fn_sig, _, _) = &item.kind { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut applicability); + let mut app = Applicability::MaybeIncorrect; + let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut app); for attr in attrs { if let Some(ident) = attr.ident() && ident.name == rustc_span::sym::no_mangle && fn_sig.header.abi == Abi::Rust - && !snippet.contains("extern") { + && let Some((fn_attrs, _)) = snippet.split_once("fn") + && !fn_attrs.contains("extern") + { + let sugg_span = fn_sig.span + .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len())) + .shrink_to_lo(); - let suggestion = snippet.split_once("fn") - .map_or(String::new(), |(first, second)| format!(r#"{first}extern "C" fn{second}"#)); - - span_lint_and_sugg( + span_lint_and_then( cx, NO_MANGLE_WITH_RUST_ABI, fn_sig.span, - "attribute #[no_mangle] set on a Rust ABI function", - "try", - suggestion, - applicability + "`#[no_mangle]` set on a function with the default (`Rust`) ABI", + |diag| { + diag.span_suggestion(sugg_span, "set an ABI", "extern \"C\" ", app) + .span_suggestion(sugg_span, "or explicitly set the default", "extern \"Rust\" ", app); + }, ); } } diff --git a/tests/ui/no_mangle_with_rust_abi.fixed b/tests/ui/no_mangle_with_rust_abi.fixed deleted file mode 100644 index d18dec22a8b..00000000000 --- a/tests/ui/no_mangle_with_rust_abi.fixed +++ /dev/null @@ -1,48 +0,0 @@ -// run-rustfix - -#![allow(unused)] -#![warn(clippy::no_mangle_with_rust_abi)] - -#[no_mangle] -extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} - -#[no_mangle] -pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} - -/// # Safety -/// This function shouldn't be called unless the horsemen are ready -#[no_mangle] -pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} - -/// # Safety -/// This function shouldn't be called unless the horsemen are ready -#[no_mangle] -unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} - -#[no_mangle] -extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( - arg_one: u32, - arg_two: usize, -) -> u32 { - 0 -} - -// Must not run on functions that explicitly opt in to Rust ABI with `extern "Rust"` -#[no_mangle] -#[rustfmt::skip] -extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {} - -fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {} - -#[no_mangle] -extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {} - -extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} - -extern "C" { - fn c_abi_in_block(arg_one: u32, arg_two: usize); -} - -fn main() { - // test code goes here -} diff --git a/tests/ui/no_mangle_with_rust_abi.rs b/tests/ui/no_mangle_with_rust_abi.rs index 481e1b6d961..b32e721110e 100644 --- a/tests/ui/no_mangle_with_rust_abi.rs +++ b/tests/ui/no_mangle_with_rust_abi.rs @@ -1,5 +1,3 @@ -// run-rustfix - #![allow(unused)] #![warn(clippy::no_mangle_with_rust_abi)] diff --git a/tests/ui/no_mangle_with_rust_abi.stderr b/tests/ui/no_mangle_with_rust_abi.stderr index 71517d31809..da5d31d8f2d 100644 --- a/tests/ui/no_mangle_with_rust_abi.stderr +++ b/tests/ui/no_mangle_with_rust_abi.stderr @@ -1,31 +1,66 @@ -error: attribute #[no_mangle] set on a Rust ABI function - --> $DIR/no_mangle_with_rust_abi.rs:7:1 +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> $DIR/no_mangle_with_rust_abi.rs:5:1 | LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` +help: set an ABI + | +LL | extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + | +++++++++++++ -error: attribute #[no_mangle] set on a Rust ABI function - --> $DIR/no_mangle_with_rust_abi.rs:10:1 +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> $DIR/no_mangle_with_rust_abi.rs:8:1 | LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + | +++++++++++++ -error: attribute #[no_mangle] set on a Rust ABI function - --> $DIR/no_mangle_with_rust_abi.rs:15:1 +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> $DIR/no_mangle_with_rust_abi.rs:13:1 | LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + | +++++++++++++ -error: attribute #[no_mangle] set on a Rust ABI function - --> $DIR/no_mangle_with_rust_abi.rs:20:1 +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> $DIR/no_mangle_with_rust_abi.rs:18:1 | LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + | +++++++++++++ -error: attribute #[no_mangle] set on a Rust ABI function - --> $DIR/no_mangle_with_rust_abi.rs:23:1 +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> $DIR/no_mangle_with_rust_abi.rs:21:1 | LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( LL | | arg_one: u32, @@ -33,13 +68,14 @@ LL | | arg_two: usize, LL | | ) -> u32 { | |________^ | -help: try +help: set an ABI | -LL + extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( -LL + arg_one: u32, -LL + arg_two: usize, -LL ~ ) -> u32 { +LL | extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + | ++++++++++ +help: or explicitly set the default | +LL | extern "Rust" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + | +++++++++++++ error: aborting due to 5 previous errors From 1a81a3e5cfc47908154b13bd921f9c03c5592010 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 2 Mar 2023 00:51:04 -0500 Subject: [PATCH 02/47] Remove `snippet_with_macro_callsite` --- clippy_lints/src/default.rs | 9 ++-- clippy_lints/src/if_then_some_else_none.rs | 17 ++++---- clippy_lints/src/loops/same_item_push.rs | 32 ++++++++------ clippy_lints/src/matches/manual_unwrap_or.rs | 12 ++---- clippy_lints/src/matches/match_bool.rs | 20 +++++---- clippy_lints/src/matches/match_ref_pats.rs | 28 ++++++++++--- .../src/matches/match_single_binding.rs | 41 +++++++++--------- .../src/matches/redundant_pattern_match.rs | 17 ++++---- clippy_lints/src/matches/single_match.rs | 18 +++----- .../src/methods/bind_instead_of_map.rs | 11 ++--- clippy_lints/src/methods/clone_on_ref_ptr.rs | 8 ++-- clippy_lints/src/methods/or_fun_call.rs | 28 ++++--------- clippy_lints/src/misc.rs | 17 ++++---- clippy_lints/src/needless_bool.rs | 13 ++---- clippy_lints/src/option_if_let_else.rs | 28 +++++++++---- .../src/semicolon_if_nothing_returned.rs | 13 +++--- clippy_lints/src/unit_types/let_unit_value.rs | 7 ++-- clippy_lints/src/useless_conversion.rs | 14 ++++--- clippy_utils/src/source.rs | 42 ++++++++++--------- clippy_utils/src/sugg.rs | 10 +---- .../redundant_pattern_matching_result.fixed | 4 +- .../redundant_pattern_matching_result.stderr | 4 +- 22 files changed, 200 insertions(+), 193 deletions(-) diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 080d44e6398..80c22742ba4 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; -use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_drop, is_copy}; use clippy_utils::{ any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro, match_def_path, paths, @@ -160,6 +160,8 @@ impl<'tcx> LateLintPass<'tcx> for Default { } }; + let init_ctxt = local.span.ctxt(); + // find all "later statement"'s where the fields of the binding set as // Default::default() get reassigned, unless the reassignment refers to the original binding let mut first_assign = None; @@ -169,7 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { // find out if and which field was set by this `consecutive_statement` if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) { // interrupt and cancel lint if assign_rhs references the original binding - if contains_name(binding_name, assign_rhs, cx) { + if contains_name(binding_name, assign_rhs, cx) || init_ctxt != consecutive_statement.span.ctxt() { cancel_lint = true; break; } @@ -204,11 +206,12 @@ impl<'tcx> LateLintPass<'tcx> for Default { .iter() .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); + let mut app = Applicability::Unspecified; let field_list = assigned_fields .into_iter() .map(|(field, rhs)| { // extract and store the assigned value for help message - let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); + let value_snippet = snippet_with_context(cx, rhs.span, init_ctxt, "..", &mut app).0; format!("{field}: {value_snippet}") }) .collect::>() diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 9cadaaa493e..725bd3d54bc 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::source::snippet_with_context; +use clippy_utils::sugg::Sugg; use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; +use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -72,21 +74,20 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { return; } + let ctxt = expr.span.ctxt(); + if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr) && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind + && then_expr.span.ctxt() == ctxt && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !stmts_contains_early_return(then_block.stmts) { - let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); - let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { - format!("({cond_snip})") - } else { - cond_snip.into_owned() - }; - let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, ""); + let mut app = Applicability::Unspecified; + let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string(); + let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; let mut method_body = if then_block.stmts.is_empty() { arg_snip.into_owned() } else { diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 540656a2cd9..9d9341559ac 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,15 +1,17 @@ use super::SAME_ITEM_PUSH; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::path_to_local; -use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use rustc_span::SyntaxContext; use std::iter::Iterator; /// Detects for loop pushing the same item into a Vec @@ -20,9 +22,10 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, ) { - fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) { + let mut app = Applicability::Unspecified; + let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0; + let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0; span_lint_and_help( cx, @@ -43,7 +46,7 @@ pub(super) fn check<'tcx>( walk_expr(&mut same_item_push_visitor, body); if_chain! { if same_item_push_visitor.should_lint(); - if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push; + if let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push; let vec_ty = cx.typeck_results().expr_ty(vec); let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); if cx @@ -69,11 +72,11 @@ pub(super) fn check<'tcx>( then { match init.kind { // immutable bindings that are initialized with literal - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), // immutable bindings that are initialized with constant ExprKind::Path(ref path) => { if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { - emit_lint(cx, vec, pushed_item); + emit_lint(cx, vec, pushed_item, ctxt); } } _ => {}, @@ -82,11 +85,11 @@ pub(super) fn check<'tcx>( } }, // constant - Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt), _ => {}, } }, - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), _ => {}, } } @@ -98,7 +101,7 @@ struct SameItemPushVisitor<'a, 'tcx> { non_deterministic_expr: bool, multiple_pushes: bool, // this field holds the last vec push operation visited, which should be the only push seen - vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>, + vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)>, cx: &'a LateContext<'tcx>, used_locals: FxHashSet, } @@ -118,7 +121,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { if_chain! { if !self.non_deterministic_expr; if !self.multiple_pushes; - if let Some((vec, _)) = self.vec_push; + if let Some((vec, _, _)) = self.vec_push; if let Some(hir_id) = path_to_local(vec); then { !self.used_locals.contains(&hir_id) @@ -173,7 +176,10 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { // Given some statement, determine if that statement is a push on a Vec. If it is, return // the Vec being pushed into and the item being pushed -fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { +fn get_vec_push<'tcx>( + cx: &LateContext<'tcx>, + stmt: &'tcx Stmt<'_>, +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> { if_chain! { // Extract method being called if let StmtKind::Semi(semi_stmt) = &stmt.kind; @@ -184,7 +190,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(& if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec); if path.ident.name.as_str() == "push"; then { - return Some((self_expr, pushed_item)) + return Some((self_expr, pushed_item, semi_stmt.span.ctxt())) } } None diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 587c926dc01..84ff3c710c2 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -33,14 +33,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent)); - let suggestion = if scrutinee.span.from_expansion() { - // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)` - sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") - } - else { - sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() - }; - + let mut app = Applicability::MachineApplicable; + let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par(); span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, @@ -49,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: format!( "{suggestion}.unwrap_or({reindented_or_body})", ), - Applicability::MachineApplicable, + app, ); } } diff --git a/clippy_lints/src/matches/match_bool.rs b/clippy_lints/src/matches/match_bool.rs index 1c216e13570..df1e585f10b 100644 --- a/clippy_lints/src/matches/match_bool.rs +++ b/clippy_lints/src/matches/match_bool.rs @@ -10,9 +10,9 @@ use rustc_middle::ty; use super::MATCH_BOOL; -pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { +pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { // Type of expression is `bool`. - if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool { + if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool { span_lint_and_then( cx, MATCH_BOOL, @@ -36,24 +36,26 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: }; if let Some((true_expr, false_expr)) = exprs { + let mut app = Applicability::HasPlaceholders; + let ctxt = expr.span.ctxt(); let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) { (false, false) => Some(format!( "if {} {} else {}", - snippet(cx, ex.span, "b"), - expr_block(cx, true_expr, None, "..", Some(expr.span)), - expr_block(cx, false_expr, None, "..", Some(expr.span)) + snippet(cx, scrutinee.span, "b"), + expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app), + expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) )), (false, true) => Some(format!( "if {} {}", - snippet(cx, ex.span, "b"), - expr_block(cx, true_expr, None, "..", Some(expr.span)) + snippet(cx, scrutinee.span, "b"), + expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app) )), (true, false) => { - let test = Sugg::hir(cx, ex, ".."); + let test = Sugg::hir(cx, scrutinee, ".."); Some(format!( "if {} {}", !test, - expr_block(cx, false_expr, None, "..", Some(expr.span)) + expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) )) }, (true, true) => None, diff --git a/clippy_lints/src/matches/match_ref_pats.rs b/clippy_lints/src/matches/match_ref_pats.rs index 80f964ba1b7..aba4c85c59e 100644 --- a/clippy_lints/src/matches/match_ref_pats.rs +++ b/clippy_lints/src/matches/match_ref_pats.rs @@ -1,13 +1,14 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::sugg::Sugg; use core::iter::once; +use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::LateContext; use super::MATCH_REF_PATS; -pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>) +pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, scrutinee: &Expr<'_>, pats: I, expr: &Expr<'_>) where 'b: 'a, I: Clone + Iterator>, @@ -17,13 +18,28 @@ where } let (first_sugg, msg, title); - let span = ex.span.source_callsite(); - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind { - first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); + let ctxt = expr.span.ctxt(); + let mut app = Applicability::Unspecified; + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = scrutinee.kind { + if scrutinee.span.ctxt() != ctxt { + return; + } + first_sugg = once(( + scrutinee.span, + Sugg::hir_with_context(cx, inner, ctxt, "..", &mut app).to_string(), + )); msg = "try"; title = "you don't need to add `&` to both the expression and the patterns"; } else { - first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string())); + let Some(span) = walk_span_to_context(scrutinee.span, ctxt) else { + return; + }; + first_sugg = once(( + span, + Sugg::hir_with_context(cx, scrutinee, ctxt, "..", &mut app) + .deref() + .to_string(), + )); msg = "instead of prefixing all patterns with `&`, you can dereference the expression"; title = "you don't need to add `&` to all patterns"; } diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 065a5c72621..eec5c1143d4 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::HirNode; -use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability}; -use clippy_utils::sugg::Sugg; +use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_applicability}; use clippy_utils::{get_parent_expr, is_refutable, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind}; @@ -24,21 +23,25 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let matched_vars = ex.span; let bind_names = arms[0].pat.span; let match_body = peel_blocks(arms[0].body); - let mut snippet_body = if match_body.span.from_expansion() { - Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string() - } else { - snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string() - }; + let mut app = Applicability::MaybeIncorrect; + let (snippet_body, from_macro) = snippet_block_with_context( + cx, + match_body.span, + arms[0].span.ctxt(), + "..", + Some(expr.span), + &mut app, + ); + let mut snippet_body = snippet_body.to_string(); // Do we need to add ';' to suggestion ? - if let ExprKind::Block(block, _) = match_body.kind { + if matches!(match_body.kind, ExprKind::Block(..)) { // macro + expr_ty(body) == () - if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { + if from_macro && cx.typeck_results().expr_ty(match_body).is_unit() { snippet_body.push(';'); } } - let mut applicability = Applicability::MaybeIncorrect; match arms[0].pat.kind { PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => { let (target_span, sugg) = match opt_parent_assign_span(cx, ex) { @@ -48,7 +51,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e (ex, expr), (bind_names, matched_vars), &snippet_body, - &mut applicability, + &mut app, Some(span), true, ); @@ -60,7 +63,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e "this assignment could be simplified", "consider removing the `match` expression", sugg, - applicability, + app, ); return; @@ -69,10 +72,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e span, format!( "let {} = {};\n{}let {} = {snippet_body};", - snippet_with_applicability(cx, bind_names, "..", &mut applicability), - snippet_with_applicability(cx, matched_vars, "..", &mut applicability), + snippet_with_applicability(cx, bind_names, "..", &mut app), + snippet_with_applicability(cx, matched_vars, "..", &mut app), " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, pat_span, "..", &mut applicability) + snippet_with_applicability(cx, pat_span, "..", &mut app) ), ), None => { @@ -81,7 +84,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e (ex, expr), (bind_names, matched_vars), &snippet_body, - &mut applicability, + &mut app, None, true, ); @@ -96,7 +99,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e "this match could be written as a `let` statement", "consider using a `let` statement", sugg, - applicability, + app, ); }, PatKind::Wild => { @@ -106,7 +109,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e (ex, expr), (bind_names, matched_vars), &snippet_body, - &mut applicability, + &mut app, None, false, ); @@ -118,7 +121,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e "this match could be replaced by its scrutinee and body", "consider using the scrutinee and body instead", sugg, - applicability, + app, ); } else { span_lint_and_sugg( diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 81bebff34c8..e8610670054 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,6 +1,6 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::any_temporaries_need_ordered_drop; @@ -150,22 +150,25 @@ fn find_sugg_for_if_let<'tcx>( // if/while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ let expr_span = expr.span; + let ctxt = expr.span.ctxt(); // if/while let ... = ... { ... } - // ^^^ - let op_span = result_expr.span.source_callsite(); + // ^^^ + let Some(res_span) = walk_span_to_context(result_expr.span.source_callsite(), ctxt) else { + return; + }; // if/while let ... = ... { ... } - // ^^^^^^^^^^^^^^^^^^^ - let span = expr_span.until(op_span.shrink_to_hi()); + // ^^^^^^^^^^^^^^^^^^^^^^ + let span = expr_span.until(res_span.shrink_to_hi()); - let app = if needs_drop { + let mut app = if needs_drop { Applicability::MaybeIncorrect } else { Applicability::MachineApplicable }; - let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_") + let sugg = Sugg::hir_with_context(cx, result_expr, ctxt, "_", &mut app) .maybe_par() .to_string(); diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 19b49c44d57..ad47c13896c 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -67,8 +67,10 @@ fn report_single_pattern( els: Option<&Expr<'_>>, ) { let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; + let ctxt = expr.span.ctxt(); + let mut app = Applicability::HasPlaceholders; let els_str = els.map_or(String::new(), |els| { - format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span))) + format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); @@ -103,7 +105,7 @@ fn report_single_pattern( // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), snippet(cx, arms[0].pat.span, ".."), - expr_block(cx, arms[0].body, None, "..", Some(expr.span)), + expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) } else { @@ -112,21 +114,13 @@ fn report_single_pattern( "if let {} = {} {}{els_str}", snippet(cx, arms[0].pat.span, ".."), snippet(cx, ex.span, ".."), - expr_block(cx, arms[0].body, None, "..", Some(expr.span)), + expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) } }; - span_lint_and_sugg( - cx, - lint, - expr.span, - msg, - "try this", - sugg, - Applicability::HasPlaceholders, - ); + span_lint_and_sugg(cx, lint, expr.span, msg, "try this", sugg, app); } fn check_opt_like<'a>( diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 4720a6e6888..8ddb912f90b 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,6 +1,6 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_with_macro_callsite}; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -77,11 +77,8 @@ pub(crate) trait BindInsteadOfMap { if !contains_return(inner_expr); if let Some(msg) = Self::lint_msg(cx); then { - let some_inner_snip = if inner_expr.span.from_expansion() { - snippet_with_macro_callsite(cx, inner_expr.span, "_") - } else { - snippet(cx, inner_expr.span, "_") - }; + let mut app = Applicability::MachineApplicable; + let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0; let closure_args_snip = snippet(cx, closure_args_span, ".."); let option_snip = snippet(cx, recv.span, ".."); @@ -93,7 +90,7 @@ pub(crate) trait BindInsteadOfMap { &msg, "try this", note, - Applicability::MachineApplicable, + app, ); true } else { diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 355f53532e2..5e8ad0861f3 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths; -use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -33,7 +33,9 @@ pub(super) fn check( return; }; - let snippet = snippet_with_macro_callsite(cx, receiver.span, ".."); + // Sometimes unnecessary ::<_> after Rc/Arc/Weak + let mut app = Applicability::Unspecified; + let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; span_lint_and_sugg( cx, @@ -42,7 +44,7 @@ pub(super) fn check( "using `.clone()` on a ref-counted pointer", "try this", format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), - Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak + app, ); } } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 4460f38fcc1..7ce28ea93e0 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; -use clippy_utils::source::{snippet, snippet_with_macro_callsite}; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_trait_item, last_path_segment}; use if_chain::if_chain; @@ -9,7 +9,6 @@ use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Symbol}; -use std::borrow::Cow; use super::OR_FUN_CALL; @@ -111,37 +110,24 @@ pub(super) fn check<'tcx>( if poss.contains(&name); then { + let ctxt = span.ctxt(); + let mut app = Applicability::HasPlaceholders; let sugg = { let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { (false, Some(fun_span)) => (fun_span, false), _ => (arg.span, true), }; - let format_span = |span: Span| { - let not_macro_argument_snippet = snippet_with_macro_callsite(cx, span, ".."); - let snip = if not_macro_argument_snippet == "vec![]" { - let macro_expanded_snipped = snippet(cx, snippet_span, ".."); - match macro_expanded_snipped.strip_prefix("$crate::vec::") { - Some(stripped) => Cow::Owned(stripped.to_owned()), - None => macro_expanded_snipped, - } - } else { - not_macro_argument_snippet - }; - - snip.to_string() - }; - - let snip = format_span(snippet_span); + let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; let snip = if use_lambda { let l_arg = if fn_has_arguments { "_" } else { "" }; format!("|{l_arg}| {snip}") } else { - snip + snip.into_owned() }; if let Some(f) = second_arg { - let f = format_span(f.span); + let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; format!("{snip}, {f}") } else { snip @@ -155,7 +141,7 @@ pub(super) fn check<'tcx>( &format!("use of `{name}` followed by a function call"), "try this", format!("{name}_{suffix}({sugg})"), - Applicability::HasPlaceholders, + app, ); } } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 0705029a613..3752b9a946f 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::{snippet, snippet_opt, snippet_with_context}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -181,20 +181,17 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind; if let Some(init) = local.init; then { - // use the macro callsite when the init span (but not the whole local span) - // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];` - let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() { - Sugg::hir_with_macro_callsite(cx, init, "..") - } else { - Sugg::hir(cx, init, "..") - }; + let ctxt = local.span.ctxt(); + let mut app = Applicability::MachineApplicable; + let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); let (mutopt, initref) = if mutabl == Mutability::Mut { ("mut ", sugg_init.mut_addr()) } else { ("", sugg_init.addr()) }; let tyopt = if let Some(ty) = local.ty { - format!(": &{mutopt}{ty}", ty=snippet(cx, ty.span, "..")) + let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; + format!(": &{mutopt}{ty_snip}") } else { String::new() }; @@ -212,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { "let {name}{tyopt} = {initref};", name=snippet(cx, name.span, ".."), ), - Applicability::MachineApplicable, + app, ); } ); diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index a4eec95b371..c87059bf61d 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -340,18 +340,11 @@ fn suggest_bool_comparison<'a, 'tcx>( cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, expr: &Expr<'_>, - mut applicability: Applicability, + mut app: Applicability, message: &str, conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, ) { - let hint = if expr.span.from_expansion() { - if applicability != Applicability::Unspecified { - applicability = Applicability::MaybeIncorrect; - } - Sugg::hir_with_macro_callsite(cx, expr, "..") - } else { - Sugg::hir_with_applicability(cx, expr, "..", &mut applicability) - }; + let hint = Sugg::hir_with_context(cx, expr, e.span.ctxt(), "..", &mut app); span_lint_and_sugg( cx, BOOL_COMPARISON, @@ -359,7 +352,7 @@ fn suggest_bool_comparison<'a, 'tcx>( message, "try simplifying it as shown", conv_hint(hint).to_string(), - applicability, + app, ); } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index c5ea09590d3..bbbcda069c5 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -12,6 +12,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -95,10 +96,10 @@ struct OptionOccurrence { none_expr: String, } -fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { +fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> String { format!( "{}{}", - Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(), + cond_sugg.maybe_par(), if as_mut { ".as_mut()" } else if as_ref { @@ -111,6 +112,7 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo fn try_get_option_occurrence<'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, pat: &Pat<'tcx>, expr: &Expr<'_>, if_then: &'tcx Expr<'_>, @@ -160,11 +162,23 @@ fn try_get_option_occurrence<'tcx>( } } + let mut app = Applicability::Unspecified; return Some(OptionOccurrence { - option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), + option: format_option_in_sugg( + Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app), + as_ref, + as_mut, + ), method_sugg: method_sugg.to_string(), - some_expr: format!("|{capture_mut}{capture_name}| {}", Sugg::hir_with_macro_callsite(cx, some_body, "..")), - none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")), + some_expr: format!( + "|{capture_mut}{capture_name}| {}", + Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app), + ), + none_expr: format!( + "{}{}", + if method_sugg == "map_or" { "" } else { "|| " }, + Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app), + ), }); } } @@ -194,7 +208,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> }) = higher::IfLet::hir(cx, expr) { if !is_else_clause(cx.tcx, expr) { - return try_get_option_occurrence(cx, let_pat, let_expr, if_then, if_else); + return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, let_expr, if_then, if_else); } } None @@ -203,7 +217,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind { if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) { - return try_get_option_occurrence(cx, let_pat, ex, if_then, if_else); + return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, ex, if_then, if_else); } } None diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 66638eed998..355f907e257 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,7 +1,6 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::sugg; +use clippy_utils::source::snippet_with_context; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; @@ -44,7 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { if let Some(expr) = block.expr; let t_expr = cx.typeck_results().expr_ty(expr); if t_expr.is_unit(); - if let snippet = snippet_with_macro_callsite(cx, expr.span, "}"); + let mut app = Applicability::MaybeIncorrect; + if let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0; if !snippet.ends_with('}') && !snippet.ends_with(';'); if cx.sess().source_map().is_multiline(block.span); then { @@ -52,17 +52,14 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { if let ExprKind::DropTemps(..) = &expr.kind { return; } - - let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, ".."); - let suggestion = format!("{sugg};"); span_lint_and_sugg( cx, SEMICOLON_IF_NOTHING_RETURNED, expr.span.source_callsite(), "consider adding a `;` to the last statement for consistent formatting", "add a `;` here", - suggestion, - Applicability::MaybeIncorrect, + format!("{snippet};"), + app, ); } } diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index d6167a62169..a0164fdfc1e 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_node; -use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::source::snippet_with_context; use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -48,12 +48,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { "this let-binding has unit value", |diag| { if let Some(expr) = &local.init { - let snip = snippet_with_macro_callsite(cx, expr.span, "()"); + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0; diag.span_suggestion( local.span, "omit the `let` binding", format!("{snip};"), - Applicability::MachineApplicable, // snippet + app, ); } }, diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index a95e7b61374..47f19557e58 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::source::{snippet, snippet_with_macro_callsite}; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths}; @@ -68,15 +68,16 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_and_consts(a, b) { - let sugg = snippet_with_macro_callsite(cx, recv.span, "").to_string(); + let mut app = Applicability::MachineApplicable; + let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "", &mut app).0; span_lint_and_sugg( cx, USELESS_CONVERSION, e.span, &format!("useless conversion to the same type: `{b}`"), "consider removing `.into()`", - sugg, - Applicability::MachineApplicable, // snippet + sugg.into_owned(), + app, ); } } @@ -165,7 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if same_type_and_consts(a, b); then { - let sugg = Sugg::hir_with_macro_callsite(cx, arg, "").maybe_par(); + let mut app = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_par(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( @@ -175,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { &format!("useless conversion to the same type: `{b}`"), &sugg_msg, sugg.to_string(), - Applicability::MachineApplicable, // snippet + app, ); } } diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index cd5dcfdaca3..62fa37660fa 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -12,24 +12,21 @@ use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; use std::borrow::Cow; /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. -/// Also takes an `Option` which can be put inside the braces. -pub fn expr_block<'a, T: LintContext>( +pub fn expr_block( cx: &T, expr: &Expr<'_>, - option: Option, - default: &'a str, + outer: SyntaxContext, + default: &str, indent_relative_to: Option, -) -> Cow<'a, str> { - let code = snippet_block(cx, expr.span, default, indent_relative_to); - let string = option.unwrap_or_default(); - if expr.span.from_expansion() { - Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) + app: &mut Applicability, +) -> String { + let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app); + if from_macro { + format!("{{ {code} }}") } else if let ExprKind::Block(_, _) = expr.kind { - Cow::Owned(format!("{code}{string}")) - } else if string.is_empty() { - Cow::Owned(format!("{{ {code} }}")) + format!("{code}") } else { - Cow::Owned(format!("{{\n{code};\n{string}\n}}")) + format!("{{ {code} }}") } } @@ -229,12 +226,6 @@ fn snippet_with_applicability_sess<'a>( ) } -/// Same as `snippet`, but should only be used when it's clear that the input span is -/// not a macro argument. -pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { - snippet(cx, span.source_callsite(), default) -} - /// Converts a span to a code snippet. Returns `None` if not available. pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option { snippet_opt_sess(cx.sess(), span) @@ -303,6 +294,19 @@ pub fn snippet_block_with_applicability<'a>( reindent_multiline(snip, true, indent) } +pub fn snippet_block_with_context<'a>( + cx: &impl LintContext, + span: Span, + outer: SyntaxContext, + default: &'a str, + indent_relative_to: Option, + app: &mut Applicability, +) -> (Cow<'a, str>, bool) { + let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app); + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + (reindent_multiline(snip, true, indent), from_macro) +} + /// Same as `snippet_with_applicability`, but first walks the span up to the given context. This /// will result in the macro call, rather then the expansion, if the span is from a child context. /// If the span is not from a child context, it will be used directly instead. diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 51e270d330c..8e3ee22a765 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,9 +1,7 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::source::{ - snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite, -}; +use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; use rustc_ast::util::parser::AssocOp; @@ -89,12 +87,6 @@ impl<'a> Sugg<'a> { }) } - /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro. - pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self { - let get_snippet = |span| snippet_with_macro_callsite(cx, span, default); - Self::hir_from_snippet(expr, get_snippet) - } - /// Same as `hir`, but first walks the span up to the given context. This will result in the /// macro call, rather then the expansion, if the span is from a child context. If the span is /// not from a child context, it will be used directly instead. diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index b88c5d0bec8..42348df4480 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -69,8 +69,8 @@ fn issue5504() { } fn try_result_opt() -> Result { - while (r#try!(result_opt())).is_some() {} - if (r#try!(result_opt())).is_some() {} + while r#try!(result_opt()).is_some() {} + if r#try!(result_opt()).is_some() {} Ok(42) } diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index e6afe9eb78e..d6a46babb77 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -88,13 +88,13 @@ error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_result.rs:84:19 | LL | while let Some(_) = r#try!(result_opt()) {} - | ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()` + | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_result.rs:85:16 | LL | if let Some(_) = r#try!(result_opt()) {} - | -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()` + | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_result.rs:91:12 From 90afb207eb14e22cc60cea3591509ae568d41c62 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Thu, 24 Nov 2022 17:58:32 +0100 Subject: [PATCH 03/47] Remove `identity_future` indirection This was previously needed because the indirection used to hide some unexplained lifetime errors, which it turned out were related to the `min_choice` algorithm. Removing the indirection also solves a couple of cycle errors, large moves and makes async blocks support the `#[track_caller]` annotation. --- clippy_lints/src/manual_async_fn.rs | 9 +-------- clippy_utils/src/lib.rs | 11 +---------- tests/ui/author/blocks.stdout | 6 +----- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 3778eb4c732..f97c6bcb5d1 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_function_call_with_def_id; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -175,16 +174,10 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; - if let Some(args) = cx - .tcx - .lang_items() - .identity_future_fn() - .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id)); - if args.len() == 1; if let Expr { kind: ExprKind::Closure(&Closure { body, .. }), .. - } = args[0]; + } = block_expr; let closure_body = cx.tcx.hir().body(body); if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block)); then { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index f02f8ecb43d..8edfd2eff72 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1904,16 +1904,7 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool { /// Peels away all the compiler generated code surrounding the body of an async function, pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Call( - _, - &[ - Expr { - kind: ExprKind::Closure(&Closure { body, .. }), - .. - }, - ], - ) = body.value.kind - { + if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind { if let ExprKind::Block( Block { stmts: [], diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index c6acf24c21e..eb3e5189c82 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -43,11 +43,7 @@ if let ExprKind::Block(block, None) = expr.kind if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind && let FnRetTy::DefaultReturn(_) = fn_decl.output && expr1 = &cx.tcx.hir().body(body_id).value - && let ExprKind::Call(func, args) = expr1.kind - && let ExprKind::Path(ref qpath) = func.kind - && matches!(qpath, QPath::LangItem(LangItem::IdentityFuture, _)) - && args.len() == 1 - && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind + && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = expr1.kind && let FnRetTy::DefaultReturn(_) = fn_decl1.output && expr2 = &cx.tcx.hir().body(body_id1).value && let ExprKind::Block(block, None) = expr2.kind From e66e918bd316c407c605d541343255f0d66e7a7b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 7 Mar 2023 17:51:48 +0000 Subject: [PATCH 04/47] Fortify clippy tests. --- tests/ui/erasing_op.rs | 8 +++++--- tests/ui/erasing_op.stderr | 10 +++++----- tests/ui/integer_arithmetic.rs | 2 +- tests/ui/overflow_check_conditional.rs | 9 +++++---- tests/ui/overflow_check_conditional.stderr | 16 ++++++++-------- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/tests/ui/erasing_op.rs b/tests/ui/erasing_op.rs index ae2fad0086d..74985029e00 100644 --- a/tests/ui/erasing_op.rs +++ b/tests/ui/erasing_op.rs @@ -31,9 +31,7 @@ impl core::ops::Mul for Vec1 { #[allow(clippy::no_effect)] #[warn(clippy::erasing_op)] -fn main() { - let x: u8 = 0; - +fn test(x: u8) { x * 0; 0 & x; 0 / x; @@ -41,3 +39,7 @@ fn main() { 0 * Vec1 { x: 5 }; Vec1 { x: 5 } * 0; } + +fn main() { + test(0) +} diff --git a/tests/ui/erasing_op.stderr b/tests/ui/erasing_op.stderr index 165ed9bfe58..97941252355 100644 --- a/tests/ui/erasing_op.stderr +++ b/tests/ui/erasing_op.stderr @@ -1,5 +1,5 @@ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:37:5 + --> $DIR/erasing_op.rs:35:5 | LL | x * 0; | ^^^^^ @@ -7,25 +7,25 @@ LL | x * 0; = note: `-D clippy::erasing-op` implied by `-D warnings` error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:38:5 + --> $DIR/erasing_op.rs:36:5 | LL | 0 & x; | ^^^^^ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:39:5 + --> $DIR/erasing_op.rs:37:5 | LL | 0 / x; | ^^^^^ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:41:5 + --> $DIR/erasing_op.rs:39:5 | LL | 0 * Vec1 { x: 5 }; | ^^^^^^^^^^^^^^^^^ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:42:5 + --> $DIR/erasing_op.rs:40:5 | LL | Vec1 { x: 5 } * 0; | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/integer_arithmetic.rs b/tests/ui/integer_arithmetic.rs index 67f24b4548a..8dfdee662b9 100644 --- a/tests/ui/integer_arithmetic.rs +++ b/tests/ui/integer_arithmetic.rs @@ -4,7 +4,7 @@ #[rustfmt::skip] fn main() { let mut i = 1i32; - let mut var1 = 0i32; + let mut var1 = 13i32; let mut var2 = -1i32; 1 + i; i * 2; diff --git a/tests/ui/overflow_check_conditional.rs b/tests/ui/overflow_check_conditional.rs index 5db75f5291b..e1e30114081 100644 --- a/tests/ui/overflow_check_conditional.rs +++ b/tests/ui/overflow_check_conditional.rs @@ -1,9 +1,6 @@ #![warn(clippy::overflow_check_conditional)] -fn main() { - let a: u32 = 1; - let b: u32 = 2; - let c: u32 = 3; +fn test(a: u32, b: u32, c: u32) { if a + b < a {} if a > a + b {} if a + b < b {} @@ -23,3 +20,7 @@ fn main() { if i > i + j {} if i - j < i {} } + +fn main() { + test(1, 2, 3) +} diff --git a/tests/ui/overflow_check_conditional.stderr b/tests/ui/overflow_check_conditional.stderr index 1b8b146b60a..92d1d8ef911 100644 --- a/tests/ui/overflow_check_conditional.stderr +++ b/tests/ui/overflow_check_conditional.stderr @@ -1,5 +1,5 @@ error: you are trying to use classic C overflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:7:8 + --> $DIR/overflow_check_conditional.rs:4:8 | LL | if a + b < a {} | ^^^^^^^^^ @@ -7,43 +7,43 @@ LL | if a + b < a {} = note: `-D clippy::overflow-check-conditional` implied by `-D warnings` error: you are trying to use classic C overflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:8:8 + --> $DIR/overflow_check_conditional.rs:5:8 | LL | if a > a + b {} | ^^^^^^^^^ error: you are trying to use classic C overflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:9:8 + --> $DIR/overflow_check_conditional.rs:6:8 | LL | if a + b < b {} | ^^^^^^^^^ error: you are trying to use classic C overflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:10:8 + --> $DIR/overflow_check_conditional.rs:7:8 | LL | if b > a + b {} | ^^^^^^^^^ error: you are trying to use classic C underflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:11:8 + --> $DIR/overflow_check_conditional.rs:8:8 | LL | if a - b > b {} | ^^^^^^^^^ error: you are trying to use classic C underflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:12:8 + --> $DIR/overflow_check_conditional.rs:9:8 | LL | if b < a - b {} | ^^^^^^^^^ error: you are trying to use classic C underflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:13:8 + --> $DIR/overflow_check_conditional.rs:10:8 | LL | if a - b > a {} | ^^^^^^^^^ error: you are trying to use classic C underflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:14:8 + --> $DIR/overflow_check_conditional.rs:11:8 | LL | if a < a - b {} | ^^^^^^^^^ From 3d229d535c416787a9541bb26cb5f928d4a38a0d Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Tue, 15 Nov 2022 12:09:31 +0100 Subject: [PATCH 05/47] avoid reuse tripping over copyright notices --- COPYRIGHT | 4 ++++ README.md | 4 ++++ rustc_tools_util/README.md | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/COPYRIGHT b/COPYRIGHT index a6be75b5e31..82703b18fd7 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,3 +1,5 @@ +// REUSE-IgnoreStart + Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license , at your option. All files in the project carrying such notice may not be copied, modified, or distributed except according to those terms. + +// REUSE-IgnoreEnd diff --git a/README.md b/README.md index 3e7379ace7e..b69ed8900a4 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,8 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT ## License + + Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 , at your option. Files in the project may not be copied, modified, or distributed except according to those terms. + + diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index eefc661f963..e197ea048a0 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -49,6 +49,8 @@ The changelog for `rustc_tools_util` is available under: ## License + + Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license , at your option. All files in the project carrying such notice may not be copied, modified, or distributed except according to those terms. + + From 27910cbcbdf76afb93e769ac66efa76271f964b0 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 6 Sep 2022 18:41:01 +0200 Subject: [PATCH 06/47] Introduce a no-op PlaceMention statement for `let _ =`. --- clippy_utils/src/qualify_min_const_fn.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index c00800291db..24403e8b6f3 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -241,6 +241,7 @@ fn check_statement<'tcx>( | StatementKind::StorageDead(_) | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) + | StatementKind::PlaceMention(..) | StatementKind::Coverage(..) | StatementKind::ConstEvalCounter | StatementKind::Nop => Ok(()), From cf8a67d9ad81547895ec986f8bcb17e912037c38 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 10 Mar 2023 10:53:50 +0100 Subject: [PATCH 07/47] Merge commit '3c06e0b1ce003912f8fe0536d3a7fe22558e38cf' into clippyup --- CHANGELOG.md | 153 +++++++++- Cargo.toml | 2 +- .../proposals/syntax-tree-patterns.md | 14 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/almost_complete_range.rs | 2 +- .../src/casts/cast_slice_from_raw_parts.rs | 8 +- clippy_lints/src/collection_is_never_read.rs | 122 ++++++++ clippy_lints/src/declared_lints.rs | 4 + .../src/default_instead_of_iter_empty.rs | 16 +- clippy_lints/src/dereference.rs | 8 +- clippy_lints/src/derivable_impls.rs | 21 +- clippy_lints/src/exit.rs | 2 +- clippy_lints/src/fn_null_check.rs | 2 +- clippy_lints/src/format.rs | 6 +- .../src/functions/impl_trait_in_params.rs | 4 +- .../src/functions/misnamed_getters.rs | 2 +- clippy_lints/src/functions/mod.rs | 2 +- clippy_lints/src/implicit_saturating_add.rs | 16 +- clippy_lints/src/instant_subtraction.rs | 13 +- clippy_lints/src/len_zero.rs | 125 ++++++-- clippy_lints/src/let_underscore.rs | 2 +- clippy_lints/src/let_with_type_underscore.rs | 45 +++ clippy_lints/src/lib.rs | 9 + clippy_lints/src/manual_bits.rs | 11 +- clippy_lints/src/manual_is_ascii_check.rs | 15 +- clippy_lints/src/manual_rem_euclid.rs | 8 +- clippy_lints/src/match_result_ok.rs | 25 +- clippy_lints/src/matches/mod.rs | 2 +- clippy_lints/src/methods/mod.rs | 5 +- clippy_lints/src/missing_assert_message.rs | 82 +++++ clippy_lints/src/missing_doc.rs | 10 +- .../src/multiple_unsafe_ops_per_block.rs | 37 +-- clippy_lints/src/neg_multiply.rs | 6 +- .../src/non_octal_unix_permissions.rs | 2 + .../src/operators/arithmetic_side_effects.rs | 13 +- .../src/permissions_set_readonly_false.rs | 2 +- clippy_lints/src/redundant_async_block.rs | 84 +++++ clippy_lints/src/size_of_ref.rs | 2 +- clippy_lints/src/swap.rs | 111 ++++--- clippy_lints/src/transmute/mod.rs | 2 +- clippy_lints/src/unit_types/let_unit_value.rs | 6 +- clippy_lints/src/use_self.rs | 15 +- clippy_lints/src/utils/author.rs | 2 +- .../src/utils/format_args_collector.rs | 23 ++ .../internal_lints/metadata_collector.rs | 7 +- clippy_lints/src/utils/mod.rs | 1 + clippy_lints/src/write.rs | 148 +++++---- clippy_utils/Cargo.toml | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/macros.rs | 117 ++++++- clippy_utils/src/ty.rs | 6 +- declare_clippy_lint/Cargo.toml | 2 +- lintcheck/Cargo.toml | 14 +- lintcheck/README.md | 9 + lintcheck/src/config.rs | 160 ++++------ lintcheck/src/popular-crates.rs | 65 ++++ rust-toolchain | 2 +- src/driver.rs | 2 +- src/main.rs | 2 +- tests/dogfood.rs | 19 +- tests/ui/arithmetic_side_effects.rs | 28 ++ tests/ui/arithmetic_side_effects.stderr | 288 ++++++++++-------- tests/ui/async_yields_async.fixed | 1 + tests/ui/async_yields_async.rs | 1 + tests/ui/async_yields_async.stderr | 12 +- tests/ui/collection_is_never_read.rs | 165 ++++++++++ tests/ui/collection_is_never_read.stderr | 52 ++++ tests/ui/crashes/ice-10148.rs | 9 + tests/ui/crashes/ice-10148.stderr | 12 + tests/ui/crashes/ice-6179.rs | 2 +- tests/ui/crashes/ice-rust-107877.rs | 17 ++ tests/ui/default_numeric_fallback_f64.fixed | 3 +- tests/ui/default_numeric_fallback_f64.rs | 3 +- tests/ui/default_numeric_fallback_f64.stderr | 48 +-- tests/ui/default_numeric_fallback_i32.fixed | 3 +- tests/ui/default_numeric_fallback_i32.rs | 3 +- tests/ui/default_numeric_fallback_i32.stderr | 52 ++-- tests/ui/derivable_impls.fixed | 37 +++ tests/ui/derivable_impls.rs | 37 +++ tests/ui/format.fixed | 6 - tests/ui/format.rs | 6 - tests/ui/format.stderr | 30 +- tests/ui/impl_trait_in_params.stderr | 4 +- tests/ui/implicit_clone.fixed | 2 +- tests/ui/implicit_clone.rs | 2 +- tests/ui/len_without_is_empty.rs | 92 ++++++ tests/ui/len_without_is_empty.stderr | 20 +- tests/ui/let_unit.fixed | 4 + tests/ui/let_unit.rs | 4 + tests/ui/let_with_type_underscore.rs | 19 ++ tests/ui/let_with_type_underscore.stderr | 39 +++ tests/ui/manual_rem_euclid.fixed | 1 + tests/ui/manual_rem_euclid.rs | 1 + tests/ui/manual_rem_euclid.stderr | 20 +- tests/ui/match_result_ok.fixed | 2 +- tests/ui/match_result_ok.stderr | 2 +- tests/ui/missing_assert_message.rs | 84 +++++ tests/ui/missing_assert_message.stderr | 131 ++++++++ tests/ui/missing_doc.stderr | 70 +---- tests/ui/missing_doc_impl.stderr | 56 +--- tests/ui/multiple_unsafe_ops_per_block.rs | 28 ++ tests/ui/multiple_unsafe_ops_per_block.stderr | 62 +++- tests/ui/new_ret_no_self.rs | 2 +- tests/ui/redundant_async_block.fixed | 64 ++++ tests/ui/redundant_async_block.rs | 64 ++++ tests/ui/redundant_async_block.stderr | 28 ++ tests/ui/redundant_closure_call_fixable.fixed | 1 + tests/ui/redundant_closure_call_fixable.rs | 1 + .../ui/redundant_closure_call_fixable.stderr | 12 +- tests/ui/swap.fixed | 6 +- tests/ui/swap.stderr | 24 +- tests/ui/trailing_empty_array.rs | 2 - tests/ui/use_self.fixed | 10 + tests/ui/use_self.rs | 10 + 114 files changed, 2508 insertions(+), 775 deletions(-) create mode 100644 clippy_lints/src/collection_is_never_read.rs create mode 100644 clippy_lints/src/let_with_type_underscore.rs create mode 100644 clippy_lints/src/missing_assert_message.rs create mode 100644 clippy_lints/src/redundant_async_block.rs create mode 100644 clippy_lints/src/utils/format_args_collector.rs create mode 100644 lintcheck/src/popular-crates.rs create mode 100644 tests/ui/collection_is_never_read.rs create mode 100644 tests/ui/collection_is_never_read.stderr create mode 100644 tests/ui/crashes/ice-10148.rs create mode 100644 tests/ui/crashes/ice-10148.stderr create mode 100644 tests/ui/crashes/ice-rust-107877.rs create mode 100644 tests/ui/let_with_type_underscore.rs create mode 100644 tests/ui/let_with_type_underscore.stderr create mode 100644 tests/ui/missing_assert_message.rs create mode 100644 tests/ui/missing_assert_message.stderr create mode 100644 tests/ui/redundant_async_block.fixed create mode 100644 tests/ui/redundant_async_block.rs create mode 100644 tests/ui/redundant_async_block.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 765826ed867..0abe234fc8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,156 @@ document. ## Unreleased / Beta / In Rust Nightly -[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master) +[7f27e2e7...master](https://github.com/rust-lang/rust-clippy/compare/7f27e2e7...master) + +## Rust 1.68 + +Current stable, released 2023-03-09 + +[d822110d...7f27e2e7](https://github.com/rust-lang/rust-clippy/compare/d822110d...7f27e2e7) + +### New Lints + +* [`permissions_set_readonly_false`] + [#10063](https://github.com/rust-lang/rust-clippy/pull/10063) +* [`almost_complete_range`] + [#10043](https://github.com/rust-lang/rust-clippy/pull/10043) +* [`size_of_ref`] + [#10098](https://github.com/rust-lang/rust-clippy/pull/10098) +* [`semicolon_outside_block`] + [#9826](https://github.com/rust-lang/rust-clippy/pull/9826) +* [`semicolon_inside_block`] + [#9826](https://github.com/rust-lang/rust-clippy/pull/9826) +* [`transmute_null_to_fn`] + [#10099](https://github.com/rust-lang/rust-clippy/pull/10099) +* [`fn_null_check`] + [#10099](https://github.com/rust-lang/rust-clippy/pull/10099) + +### Moves and Deprecations + +* Moved [`manual_clamp`] to `nursery` (Now allow-by-default) + [#10101](https://github.com/rust-lang/rust-clippy/pull/10101) +* Moved [`mutex_atomic`] to `restriction` + [#10115](https://github.com/rust-lang/rust-clippy/pull/10115) +* Renamed `derive_hash_xor_eq` to [`derived_hash_with_manual_eq`] + [#10184](https://github.com/rust-lang/rust-clippy/pull/10184) + +### Enhancements + +* [`collapsible_str_replace`]: Now takes MSRV into consideration. The minimal version is 1.58 + [#10047](https://github.com/rust-lang/rust-clippy/pull/10047) +* [`unused_self`]: No longer lints, if the method body contains a `todo!()` call + [#10166](https://github.com/rust-lang/rust-clippy/pull/10166) +* [`derivable_impls`]: Now suggests deriving `Default` for enums with default unit variants + [#10161](https://github.com/rust-lang/rust-clippy/pull/10161) +* [`arithmetic_side_effects`]: Added two new config values + `arithmetic-side-effects-allowed-binary` and `arithmetic-side-effects-allowed-unary` + to allow operation on user types + [#9840](https://github.com/rust-lang/rust-clippy/pull/9840) +* [`large_const_arrays`], [`large_stack_arrays`]: avoid integer overflow when calculating + total array size + [#10103](https://github.com/rust-lang/rust-clippy/pull/10103) +* [`indexing_slicing`]: add new config `suppress-restriction-lint-in-const` to enable + restriction lints, even if the suggestion might not be applicable + [#9920](https://github.com/rust-lang/rust-clippy/pull/9920) +* [`needless_borrow`], [`redundant_clone`]: Now track references better and detect more cases + [#9701](https://github.com/rust-lang/rust-clippy/pull/9701) +* [`derived_hash_with_manual_eq`]: Now allows `#[derive(PartialEq)]` with custom `Hash` + implementations + [#10184](https://github.com/rust-lang/rust-clippy/pull/10184) +* [`manual_is_ascii_check`]: Now detects ranges with `.contains()` calls + [#10053](https://github.com/rust-lang/rust-clippy/pull/10053) +* [`transmuting_null`]: Now detects `const` pointers to all types + [#10099](https://github.com/rust-lang/rust-clippy/pull/10099) +* [`needless_return`]: Now detects more cases for returns of owned values + [#10110](https://github.com/rust-lang/rust-clippy/pull/10110) + +### False Positive Fixes + +* [`field_reassign_with_default`]: No longer lints cases, where values are initializes from + closures capturing struct values + [#10143](https://github.com/rust-lang/rust-clippy/pull/10143) +* [`seek_to_start_instead_of_rewind`]: No longer lints, if the return of `seek` is used. + [#10096](https://github.com/rust-lang/rust-clippy/pull/10096) +* [`manual_filter`]: Now ignores if expressions where the else branch has side effects or + doesn't return `None` + [#10091](https://github.com/rust-lang/rust-clippy/pull/10091) +* [`implicit_clone`]: No longer lints if the type doesn't implement clone + [#10022](https://github.com/rust-lang/rust-clippy/pull/10022) +* [`match_wildcard_for_single_variants`]: No longer lints on wildcards with a guard + [#10056](https://github.com/rust-lang/rust-clippy/pull/10056) +* [`drop_ref`]: No longer lints idiomatic expression in `match` arms + [#10142](https://github.com/rust-lang/rust-clippy/pull/10142) +* [`arithmetic_side_effects`]: No longer lints on corner cases with negative number literals + [#9867](https://github.com/rust-lang/rust-clippy/pull/9867) +* [`string_lit_as_bytes`]: No longer lints in scrutinies of `match` statements + [#10012](https://github.com/rust-lang/rust-clippy/pull/10012) +* [`manual_assert`]: No longer lints in `else if` statements + [#10013](https://github.com/rust-lang/rust-clippy/pull/10013) +* [`needless_return`]: don't lint when using `do yeet` + [#10109](https://github.com/rust-lang/rust-clippy/pull/10109) +* All lints: No longer lint in enum discriminant values when the suggestion won't work in a + const context + [#10008](https://github.com/rust-lang/rust-clippy/pull/10008) +* [`single_element_loop`]: No longer lints, if the loop contains a `break` or `continue` + [#10162](https://github.com/rust-lang/rust-clippy/pull/10162) +* [`uninlined_format_args`]: No longer suggests inlining arguments in `assert!` and + `debug_assert!` macros before 2021 edition + [#10055](https://github.com/rust-lang/rust-clippy/pull/10055) +* [`explicit_counter_loop`]: No longer ignores counter changes after `continue` expressions + [#10094](https://github.com/rust-lang/rust-clippy/pull/10094) +* [`from_over_into`]: No longer lints on opaque types + [#9982](https://github.com/rust-lang/rust-clippy/pull/9982) +* [`expl_impl_clone_on_copy`]: No longer lints on `#[repr(packed)]` structs with generic + parameters + [#10189](https://github.com/rust-lang/rust-clippy/pull/10189) + +### Suggestion Fixes/Improvements + +* [`zero_ptr`]: Now suggests `core::` paths for `no_std` crates + [#10023](https://github.com/rust-lang/rust-clippy/pull/10023) +* [`useless_conversion`]: Now suggests removing calls to `into_iter()` on an expression + implementing `Iterator` + [#10020](https://github.com/rust-lang/rust-clippy/pull/10020) +* [`box_default`]: The suggestion now uses short paths + [#10153](https://github.com/rust-lang/rust-clippy/pull/10153) +* [`default_trait_access`], [`clone_on_copy`]: The suggestion now uses short paths + [#10160](https://github.com/rust-lang/rust-clippy/pull/10160) +* [`comparison_to_empty`]: The suggestion now removes unused deref operations + [#9962](https://github.com/rust-lang/rust-clippy/pull/9962) +* [`manual_let_else`]: Suggestions for or-patterns now include required brackets. + [#9966](https://github.com/rust-lang/rust-clippy/pull/9966) +* [`match_single_binding`]: suggestion no longer introduces unneeded semicolons + [#10060](https://github.com/rust-lang/rust-clippy/pull/10060) +* [`case_sensitive_file_extension_comparisons`]: Now displays a suggestion with `Path` + [#10107](https://github.com/rust-lang/rust-clippy/pull/10107) +* [`empty_structs_with_brackets`]: The suggestion is no longer machine applicable, to avoid + errors when accessing struct fields + [#10141](https://github.com/rust-lang/rust-clippy/pull/10141) +* [`identity_op`]: Removes borrows in the suggestion when needed + [#10004](https://github.com/rust-lang/rust-clippy/pull/10004) +* [`suboptimal_flops`]: The suggestion now includes parentheses when required + [#10113](https://github.com/rust-lang/rust-clippy/pull/10113) +* [`iter_kv_map`]: Now handles `mut` and reference annotations in the suggestion + [#10159](https://github.com/rust-lang/rust-clippy/pull/10159) +* [`redundant_static_lifetimes`]: The suggestion no longer removes `mut` from references + [#10006](https://github.com/rust-lang/rust-clippy/pull/10006) + +### ICE Fixes + +* [`new_ret_no_self`]: Now avoids a stack overflow for `impl Trait` types + [#10086](https://github.com/rust-lang/rust-clippy/pull/10086) +* [`unnecessary_to_owned`]: Now handles compiler generated notes better + [#10027](https://github.com/rust-lang/rust-clippy/pull/10027) + +### Others + +* `SYSROOT` and `--sysroot` can now be set at the same time + [#10149](https://github.com/rust-lang/rust-clippy/pull/10149) ## Rust 1.67 -Current stable, released 2023-01-26 +Released 2023-01-26 [4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d) @@ -4307,6 +4452,7 @@ Released 2018-09-13 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace +[`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read [`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 [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime @@ -4497,6 +4643,7 @@ Released 2018-09-13 [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value +[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal @@ -4560,6 +4707,7 @@ Released 2018-09-13 [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order [`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op +[`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames @@ -4689,6 +4837,7 @@ Released 2018-09-13 [`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation +[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call diff --git a/Cargo.toml b/Cargo.toml index 70d1268090f..c35dfcbd8c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.69" +version = "0.1.70" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/book/src/development/proposals/syntax-tree-patterns.md b/book/src/development/proposals/syntax-tree-patterns.md index c5587c4bf90..ea4978011b1 100644 --- a/book/src/development/proposals/syntax-tree-patterns.md +++ b/book/src/development/proposals/syntax-tree-patterns.md @@ -68,13 +68,13 @@ The second part of the motivation is clippy's dependence on unstable compiler-internal data structures. Clippy lints are currently written against the compiler's AST / HIR which means that even small changes in these data structures might break a lot of lints. The second goal of this RFC is to **make -lints independant of the compiler's AST / HIR data structures**. +lints independent of the compiler's AST / HIR data structures**. # Approach A lot of complexity in writing lints currently seems to come from having to manually implement the matching logic (see code samples above). It's an -imparative style that describes *how* to match a syntax tree node instead of +imperative style that describes *how* to match a syntax tree node instead of specifying *what* should be matched against declaratively. In other areas, it's common to use declarative patterns to describe desired information and let the implementation do the actual matching. A well-known example of this approach are @@ -270,7 +270,7 @@ pattern!{ // matches if expressions that **may or may not** have an else block // Attn: `If(_, _, _)` matches only ifs that **have** an else block // - // | if with else block | if witout else block + // | if with else block | if without else block // If(_, _, _) | match | no match // If(_, _, _?) | match | match // If(_, _, ()) | no match | match @@ -568,7 +568,7 @@ another example, `Array( Lit(_)* )` is a valid pattern because the parameter of ## The IsMatch Trait -The pattern syntax and the *PatternTree* are independant of specific syntax tree +The pattern syntax and the *PatternTree* are independent of specific syntax tree implementations (rust ast / hir, syn, ...). When looking at the different pattern examples in the previous sections, it can be seen that the patterns don't contain any information specific to a certain syntax tree implementation. @@ -717,7 +717,7 @@ if false { #### Problems Extending Rust syntax (which is quite complex by itself) with additional syntax -needed for specifying patterns (alternations, sequences, repetisions, named +needed for specifying patterns (alternations, sequences, repetitions, named submatches, ...) might become difficult to read and really hard to parse properly. @@ -858,7 +858,7 @@ would be evaluated as soon as the `Block(_)#then` was matched. Another idea in this area would be to introduce a syntax for backreferences. They could be used to require that multiple parts of a pattern should match the same value. For example, the `assign_op_pattern` lint that searches for `a = a -op b` and recommends changing it to `a op= b` requires that both occurrances of +op b` and recommends changing it to `a op= b` requires that both occurrences of `a` are the same. Using `=#...` as syntax for backreferences, the lint could be implemented like this: @@ -882,7 +882,7 @@ least two return statements" could be a practical addition. For patterns like "a literal that is not a boolean literal" one currently needs to list all alternatives except the boolean case. Introducing a negation operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern -would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three +would be equivalent to `Lit( Char(_) | Int(_) )` (given that currently only three literal types are implemented). #### Functional composition diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 796f1ff1695..0b3846c1316 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.69" +version = "0.1.70" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/almost_complete_range.rs b/clippy_lints/src/almost_complete_range.rs index 42e14b5cd94..32d80f42e7e 100644 --- a/clippy_lints/src/almost_complete_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// ```rust /// let _ = 'a'..='z'; /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.68.0"] pub ALMOST_COMPLETE_RANGE, suspicious, "almost complete range" diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 627b795d6ed..1233c632a79 100644 --- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -34,6 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, if let ExprKind::Path(ref qpath) = fun.kind; if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); if let Some(rpk) = raw_parts_kind(cx, fun_def_id); + let ctxt = expr.span.ctxt(); + if cast_expr.span.ctxt() == ctxt; then { let func = match rpk { RawPartsKind::Immutable => "from_raw_parts", @@ -41,8 +43,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, }; let span = expr.span; let mut applicability = Applicability::MachineApplicable; - let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability); - let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability); + let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; + let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; span_lint_and_sugg( cx, CAST_SLICE_FROM_RAW_PARTS, diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs new file mode 100644 index 00000000000..10f2bef268a --- /dev/null +++ b/clippy_lints/src/collection_is_never_read.rs @@ -0,0 +1,122 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr_with_closures; +use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id}; +use core::ops::ControlFlow; +use rustc_hir::{Block, ExprKind, HirId, Local, Node, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; +use rustc_span::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks for collections that are never queried. + /// + /// ### Why is this bad? + /// Putting effort into constructing a collection but then never querying it might indicate that + /// the author forgot to do whatever they intended to do with the collection. Example: Clone + /// a vector, sort it for iteration, but then mistakenly iterate the original vector + /// instead. + /// + /// ### Example + /// ```rust + /// # let samples = vec![3, 1, 2]; + /// let mut sorted_samples = samples.clone(); + /// sorted_samples.sort(); + /// for sample in &samples { // Oops, meant to use `sorted_samples`. + /// println!("{sample}"); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # let samples = vec![3, 1, 2]; + /// let mut sorted_samples = samples.clone(); + /// sorted_samples.sort(); + /// for sample in &sorted_samples { + /// println!("{sample}"); + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub COLLECTION_IS_NEVER_READ, + nursery, + "a collection is never queried" +} +declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]); + +static COLLECTIONS: [Symbol; 10] = [ + sym::BTreeMap, + sym::BTreeSet, + sym::BinaryHeap, + sym::HashMap, + sym::HashSet, + sym::LinkedList, + sym::Option, + sym::String, + sym::Vec, + sym::VecDeque, +]; + +impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + // Look for local variables whose type is a container. Search surrounding bock for read access. + let ty = cx.typeck_results().pat_ty(local.pat); + if COLLECTIONS.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym)) + && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id) + && has_no_read_access(cx, local_id, enclosing_block) + { + span_lint(cx, COLLECTION_IS_NEVER_READ, local.span, "collection is never read"); + } + } +} + +fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool { + let mut has_access = false; + let mut has_read_access = false; + + // Inspect all expressions and sub-expressions in the block. + for_each_expr_with_closures(cx, block, |expr| { + // Ignore expressions that are not simply `id`. + if !path_to_local_id(expr, id) { + return ControlFlow::Continue(()); + } + + // `id` is being accessed. Investigate if it's a read access. + has_access = true; + + // `id` appearing in the left-hand side of an assignment is not a read access: + // + // id = ...; // Not reading `id`. + if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id) + && let ExprKind::Assign(lhs, ..) = parent.kind + && path_to_local_id(lhs, id) + { + return ControlFlow::Continue(()); + } + + // Method call on `id` in a statement ignores any return value, so it's not a read access: + // + // id.foo(...); // Not reading `id`. + // + // Only assuming this for "official" methods defined on the type. For methods defined in extension + // traits (identified as local, based on the orphan rule), pessimistically assume that they might + // have side effects, so consider them a read. + if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id) + && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind + && path_to_local_id(receiver, id) + && let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id) + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && !method_def_id.is_local() + { + return ControlFlow::Continue(()); + } + + // Any other access to `id` is a read access. Stop searching. + has_read_access = true; + ControlFlow::Break(()) + }); + + // Ignore collections that have no access at all. Other lints should catch them. + has_access && !has_read_access +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index cd5dd7a5706..cc6024b87cd 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -92,6 +92,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, crate::collapsible_if::COLLAPSIBLE_IF_INFO, + crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO, crate::comparison_chain::COMPARISON_CHAIN_INFO, crate::copies::BRANCHES_SHARING_CODE_INFO, crate::copies::IFS_SAME_COND_INFO, @@ -226,6 +227,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::let_underscore::LET_UNDERSCORE_LOCK_INFO, crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO, crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO, + crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO, crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, @@ -416,6 +418,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO, crate::misc_early::ZERO_PREFIXED_LITERAL_INFO, crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO, + crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO, crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, @@ -517,6 +520,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::ranges::REVERSED_EMPTY_RANGES_INFO, crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO, crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO, + crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO, crate::redundant_clone::REDUNDANT_CLONE_INFO, crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO, crate::redundant_else::REDUNDANT_ELSE_INFO, diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs index 1ad929864b2..f296b80d283 100644 --- a/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/clippy_lints/src/default_instead_of_iter_empty.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::last_path_segment; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::{match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -38,9 +39,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { && let QPath::Resolved(None, path) = ty_path && let def::Res::Def(_, def_id) = &path.res && match_def_path(cx, *def_id, &paths::ITER_EMPTY) + && let ctxt = expr.span.ctxt() + && ty.span.ctxt() == ctxt { let mut applicability = Applicability::MachineApplicable; - let sugg = make_sugg(cx, ty_path, &mut applicability); + let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability); span_lint_and_sugg( cx, DEFAULT_INSTEAD_OF_ITER_EMPTY, @@ -54,14 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { } } -fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String { +fn make_sugg( + cx: &LateContext<'_>, + ty_path: &rustc_hir::QPath<'_>, + ctxt: SyntaxContext, + applicability: &mut Applicability, +) -> String { if let Some(last) = last_path_segment(ty_path).args && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, }) { - format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability)) + format!("std::iter::empty::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0) } else { "std::iter::empty()".to_owned() } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 47501980e66..7f3f26bed7c 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1357,10 +1357,10 @@ fn replace_types<'tcx>( && let Some(term_ty) = projection_predicate.term.ty() && let ty::Param(term_param_ty) = term_ty.kind() { - let item_def_id = projection_predicate.projection_ty.def_id; - let assoc_item = cx.tcx.associated_item(item_def_id); - let projection = cx.tcx - .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, [])); + let projection = cx.tcx.mk_ty_from_kind(ty::Alias( + ty::Projection, + projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty), + )); if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index c5f4e943f4f..8a5a28c6b3d 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -8,7 +8,7 @@ use rustc_hir::{ Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::AdtDef; +use rustc_middle::ty::{Adt, AdtDef, SubstsRef}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -81,13 +81,18 @@ fn check_struct<'tcx>( self_ty: &Ty<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>, + substs: SubstsRef<'_>, ) { if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { - if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { - for arg in a.args { - if !matches!(arg, GenericArg::Lifetime(_)) { - return; - } + if let Some(PathSegment { args, .. }) = p.segments.last() { + let args = args.map(|a| a.args).unwrap_or(&[]); + + // substs contains the generic parameters of the type declaration, while args contains the arguments + // used at instantiation time. If both len are not equal, it means that some parameters were not + // provided (which means that the default values were used); in this case we will not risk + // suggesting too broad a rewrite. We won't either if any argument is a type or a const. + if substs.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { + return; } } } @@ -184,7 +189,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); if let ImplItemKind::Fn(_, b) = &impl_item.kind; if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); - if let Some(adt_def) = cx.tcx.type_of(item.owner_id).subst_identity().ty_adt_def(); + if let &Adt(adt_def, substs) = cx.tcx.type_of(item.owner_id).subst_identity().kind(); if let attrs = cx.tcx.hir().attrs(item.hir_id()); if !attrs.iter().any(|attr| attr.doc_str().is_some()); if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); @@ -192,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { then { if adt_def.is_struct() { - check_struct(cx, item, self_ty, func_expr, adt_def); + check_struct(cx, item, self_ty, func_expr, adt_def, substs); } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { check_enum(cx, item, func_expr, adt_def); } diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 9c8b0d076df..8ba6a9e4876 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -11,7 +11,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Exit terminates the program at the location it is called. For unrecoverable - /// errors `panics` should be used to provide a stacktrace and potentualy other + /// errors `panics` should be used to provide a stacktrace and potentially other /// information. A normal termination or one with an error code should happen in /// the main function. /// diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs index 91c8c340ce2..d8f4a5fe221 100644 --- a/clippy_lints/src/fn_null_check.rs +++ b/clippy_lints/src/fn_null_check.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// if fn_ptr.is_none() { ... } /// ``` - #[clippy::version = "1.67.0"] + #[clippy::version = "1.68.0"] pub FN_NULL_CHECK, correctness, "`fn()` type assumed to be nullable" diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index d0fab694960..8040938c626 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; @@ -84,9 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { _ => false, }; let sugg = if is_new_string { - snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned() + snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned() } else { - let sugg = Sugg::hir_with_applicability(cx, value, "", &mut applicability); + let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "", &mut applicability); format!("{}.to_string()", sugg.maybe_par()) }; span_useless_format(cx, call_site, sugg, applicability); diff --git a/clippy_lints/src/functions/impl_trait_in_params.rs b/clippy_lints/src/functions/impl_trait_in_params.rs index 2811a73f6c1..d3d0d91c1be 100644 --- a/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/clippy_lints/src/functions/impl_trait_in_params.rs @@ -22,7 +22,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: if let Some(gen_span) = generics.span_for_param_suggestion() { diag.span_suggestion_with_style( gen_span, - "add a type paremeter", + "add a type parameter", format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]), rustc_errors::Applicability::HasPlaceholders, rustc_errors::SuggestionStyle::ShowAlways, @@ -35,7 +35,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: ident.span.ctxt(), ident.span.parent(), ), - "add a type paremeter", + "add a type parameter", format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]), rustc_errors::Applicability::HasPlaceholders, rustc_errors::SuggestionStyle::ShowAlways, diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index 8b53ee68ebd..e5945939e60 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -97,7 +97,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: let Some(correct_field) = correct_field else { // There is no field corresponding to the getter name. - // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is + // FIXME: This can be a false positive if the correct field is reachable through deeper autodereferences than used_field is return; }; diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index d2852b4acad..7c5e44bb7dc 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -185,7 +185,7 @@ declare_clippy_lint! { /// ### Examples /// ```rust /// // this could be annotated with `#[must_use]`. - /// fn id(t: T) -> T { t } + /// pub fn id(t: T) -> T { t } /// ``` #[clippy::version = "1.40.0"] pub MUST_USE_CANDIDATE, diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index 6e19343931e..57e6caa8711 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use if_chain::if_chain; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; @@ -55,6 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { if let ExprKind::AssignOp(op1, target, value) = ex.kind; let ty = cx.typeck_results().expr_ty(target); if Some(c) == get_int_max(ty); + let ctxt = expr.span.ctxt(); + if ex.span.ctxt() == ctxt; + if expr1.span.ctxt() == ctxt; if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target); if BinOpKind::Add == op1.node; if let ExprKind::Lit(ref lit) = value.kind; @@ -62,8 +65,15 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { if block.expr.is_none(); then { let mut app = Applicability::MachineApplicable; - let code = snippet_with_applicability(cx, target.span, "_", &mut app); - let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")}; + let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0; + let sugg = if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind + && else_.hir_id == expr.hir_id + { + format!("{{{code} = {code}.saturating_add(1); }}") + } else { + format!("{code} = {code}.saturating_add(1);") + }; span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app); } } diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 668110c7cc0..34e9991582c 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{self, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::ty; use rustc_errors::Applicability; @@ -161,14 +161,9 @@ fn print_unchecked_duration_subtraction_sugg( ) { let mut applicability = Applicability::MachineApplicable; - let left_expr = - source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability); - let right_expr = source::snippet_with_applicability( - cx, - right_expr.span, - "std::time::Duration::from_secs(1)", - &mut applicability, - ); + let ctxt = expr.span.ctxt(); + let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "", &mut applicability).0; + let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "", &mut applicability).0; diagnostics::span_lint_and_sugg( cx, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e13bc47973b..0805b4b1979 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,13 +1,14 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefIdSet; use rustc_hir::{ - def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item, - ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp, + def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, + GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy, + QPath, TraitItemRef, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -16,7 +17,6 @@ use rustc_span::{ source_map::{Span, Spanned, Symbol}, symbol::sym, }; -use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -251,33 +251,98 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } #[derive(Debug, Clone, Copy)] -enum LenOutput<'tcx> { +enum LenOutput { Integral, Option(DefId), - Result(DefId, Ty<'tcx>), + Result(DefId), } -fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option> { + +fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { + if let ty::Alias(_, alias_ty) = ty.kind() && + let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) && + let Item { kind: ItemKind::OpaqueTy(opaque), .. } = item && + opaque.bounds.len() == 1 && + let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0] && + generic_args.bindings.len() == 1 && + let TypeBindingKind::Equality { + term: rustc_hir::Term::Ty(rustc_hir::Ty {kind: TyKind::Path(QPath::Resolved(_, path)), .. }), + } = &generic_args.bindings[0].kind && + path.segments.len() == 1 { + return Some(&path.segments[0]); + } + + None +} + +fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool { + if let Some(generic_args) = segment.args { + if generic_args.args.is_empty() { + return false; + } + let arg = &generic_args.args[0]; + if let GenericArg::Type(rustc_hir::Ty { + kind: TyKind::Path(QPath::Resolved(_, path)), + .. + }) = arg + { + let segments = &path.segments; + let segment = &segments[0]; + let res = &segment.res; + if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) { + return true; + } + } + } + + false +} + +fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option { + if let Some(segment) = extract_future_output(cx, sig.output()) { + let res = segment.res; + + if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) { + return Some(LenOutput::Integral); + } + + if let Res::Def(_, def_id) = res { + if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) { + return Some(LenOutput::Option(def_id)); + } else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) { + return Some(LenOutput::Result(def_id)); + } + } + + return None; + } + match *sig.output().kind() { ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => { subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())) }, - ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs - .type_at(0) - .is_integral() - .then(|| LenOutput::Result(adt.did(), subs.type_at(1))), + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => { + subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())) + }, _ => None, } } -impl<'tcx> LenOutput<'tcx> { - fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool { +impl LenOutput { + fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let Some(segment) = extract_future_output(cx, ty) { + return match (self, segment.res) { + (_, Res::PrimTy(PrimTy::Bool)) => true, + (Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true, + (Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true, + _ => false, + }; + } + match (self, ty.kind()) { (_, &ty::Bool) => true, (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), - (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did() => { - subs.type_at(0).is_bool() && subs.type_at(1) == err_ty - }, + (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), _ => false, } } @@ -301,9 +366,14 @@ impl<'tcx> LenOutput<'tcx> { } /// Checks if the given signature matches the expectations for `is_empty` -fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool { +fn check_is_empty_sig<'tcx>( + cx: &LateContext<'tcx>, + sig: FnSig<'tcx>, + self_kind: ImplicitSelfKind, + len_output: LenOutput, +) -> bool { match &**sig.inputs_and_output { - [arg, res] if len_output.matches_is_empty_output(*res) => { + [arg, res] if len_output.matches_is_empty_output(cx, *res) => { matches!( (arg.kind(), self_kind), (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef) @@ -315,11 +385,11 @@ fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_o } /// Checks if the given type has an `is_empty` method with the appropriate signature. -fn check_for_is_empty<'tcx>( - cx: &LateContext<'tcx>, +fn check_for_is_empty( + cx: &LateContext<'_>, span: Span, self_kind: ImplicitSelfKind, - output: LenOutput<'tcx>, + output: LenOutput, impl_ty: DefId, item_name: Symbol, item_kind: &str, @@ -352,6 +422,7 @@ fn check_for_is_empty<'tcx>( Some(is_empty) if !(is_empty.fn_has_self_parameter && check_is_empty_sig( + cx, cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), self_kind, output, @@ -431,7 +502,7 @@ fn check_len( &format!("using `{op}is_empty` is clearer and more explicit"), format!( "{op}{}.is_empty()", - snippet_with_applicability(cx, receiver.span, "_", &mut applicability) + snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0, ), applicability, ); @@ -444,13 +515,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex let mut applicability = Applicability::MachineApplicable; let lit1 = peel_ref_operators(cx, lit1); - let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability); - - // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will - // cause the code to dereference boolean(won't compile). - if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind { - lit_str = Cow::from(format!("({lit_str})")); - } + let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par(); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 7600777fab9..51b5de27de8 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -124,7 +124,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.69.0"] pub LET_UNDERSCORE_UNTYPED, - pedantic, + restriction, "non-binding `let` without a type annotation" } diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs new file mode 100644 index 00000000000..ba51973f2f9 --- /dev/null +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -0,0 +1,45 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Detects when a variable is declared with an explicit type of `_`. + /// ### Why is this bad? + /// It adds noise, `: _` provides zero clarity or utility. + /// ### Example + /// ```rust,ignore + /// let my_number: _ = 1; + /// ``` + /// Use instead: + /// ```rust,ignore + /// let my_number = 1; + /// ``` + #[clippy::version = "1.69.0"] + pub LET_WITH_TYPE_UNDERSCORE, + complexity, + "unneeded underscore type (`_`) in a variable declaration" +} +declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); + +impl LateLintPass<'_> for UnderscoreTyped { + fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + if_chain! { + if !in_external_macro(cx.tcx.sess, local.span); + if let Some(ty) = local.ty; // Ensure that it has a type defined + if let TyKind::Infer = &ty.kind; // that type is '_' + if local.span.ctxt() == ty.span.ctxt(); + then { + span_lint_and_help(cx, + LET_WITH_TYPE_UNDERSCORE, + local.span, + "variable declared with type underscore", + Some(ty.span.with_lo(local.pat.span.hi())), + "remove the explicit type `_` declaration" + ) + } + }; + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c626e0bd998..491732be208 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -87,6 +87,7 @@ mod casts; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; +mod collection_is_never_read; mod comparison_chain; mod copies; mod copy_iterator; @@ -166,6 +167,7 @@ mod large_stack_arrays; mod len_zero; mod let_if_seq; mod let_underscore; +mod let_with_type_underscore; mod lifetimes; mod literal_representation; mod loops; @@ -192,6 +194,7 @@ mod minmax; mod misc; mod misc_early; mod mismatching_type_param_order; +mod missing_assert_message; mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; @@ -249,6 +252,7 @@ mod question_mark_used; mod ranges; mod rc_clone_in_vec_init; mod read_zero_byte_vec; +mod redundant_async_block; mod redundant_clone; mod redundant_closure_call; mod redundant_else; @@ -533,6 +537,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: .collect(), )) }); + store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector)); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); @@ -924,6 +929,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); + store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)); + store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); + store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock)); + store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 462d73cf0b9..bc815dc4a26 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -55,13 +56,17 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { if_chain! { if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; if let BinOpKind::Mul = &bin_op.node; + if !in_external_macro(cx.sess(), expr.span); + let ctxt = expr.span.ctxt(); + if left_expr.span.ctxt() == ctxt; + if right_expr.span.ctxt() == ctxt; if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); if let ExprKind::Lit(lit) = &other_expr.kind; if let LitKind::Int(8, _) = lit.node; then { let mut app = Applicability::MachineApplicable; - let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app); + let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0; let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); span_lint_and_sugg( diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 2fd32c009ea..31264261f5d 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,5 +1,5 @@ use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet}; +use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, sugg::Sugg}; use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; @@ -115,15 +115,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha CharRange::Otherwise => None, } { let default_snip = ".."; - // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for - // macro span, so we check applicability manually by comparing `recv` is not default. - let recv = snippet(cx, recv.span, default_snip); - - let applicability = if recv == default_snip { - Applicability::HasPlaceholders - } else { - Applicability::MachineApplicable - }; + let mut app = Applicability::MachineApplicable; + let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); span_lint_and_sugg( cx, @@ -132,7 +125,7 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha "manual check for common ascii range", "try", format!("{recv}.{sugg}()"), - applicability, + app, ); } } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 38f41d077c1..aafee92713f 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{constant_full_int, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::{in_constant, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; @@ -60,12 +60,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { return; } + // (x % c + c) % c if let ExprKind::Binary(op1, expr1, right) = expr.kind && op1.node == BinOpKind::Rem + && let ctxt = expr.span.ctxt() + && expr1.span.ctxt() == ctxt && let Some(const1) = check_for_unsigned_int_constant(cx, right) && let ExprKind::Binary(op2, left, right) = expr1.kind && op2.node == BinOpKind::Add && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right) + && expr2.span.ctxt() == ctxt && let ExprKind::Binary(op3, expr3, right) = expr2.kind && op3.node == BinOpKind::Rem && let Some(const3) = check_for_unsigned_int_constant(cx, right) @@ -86,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { }; let mut app = Applicability::MachineApplicable; - let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app); + let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0; span_lint_and_sugg( cx, MANUAL_REM_EUCLID, diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index a020282d234..6ec9784038c 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; -use clippy_utils::method_chain_args; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::is_res_lang_ctor; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind, QPath}; +use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -58,17 +58,18 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { }; if_chain! { - if let ExprKind::MethodCall(ok_path, result_types_0, ..) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) - if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation - if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized; - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result); - if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; - + if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) + if let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind; //get operation + if ok_path.ident.as_str() == "ok"; + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); + let ctxt = expr.span.ctxt(); + if let_expr.span.ctxt() == ctxt; + if let_pat.span.ctxt() == ctxt; then { - let mut applicability = Applicability::MachineApplicable; - let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); - let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability); + let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0; + let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0; let sugg = format!( "{ifwhile} let Ok({some_expr_string}) = {}", trimmed_ok.trim().trim_end_matches('.'), diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 7b15a307fec..97ecca450fa 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -925,7 +925,7 @@ declare_clippy_lint! { #[clippy::version = "1.66.0"] pub MANUAL_FILTER, complexity, - "reimplentation of `filter`" + "reimplementation of `filter`" } #[derive(Default)] diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 702df4b282b..56e3988bf09 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -340,8 +340,9 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for methods with certain name prefixes and which - /// doesn't match how self is taken. The actual rules are: + /// Checks for methods with certain name prefixes or suffixes, and which + /// do not adhere to standard conventions regarding how `self` is taken. + /// The actual rules are: /// /// |Prefix |Postfix |`self` taken | `self` type | /// |-------|------------|-------------------------------|--------------| diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs new file mode 100644 index 00000000000..2214a568d9c --- /dev/null +++ b/clippy_lints/src/missing_assert_message.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks assertions without a custom panic message. + /// + /// ### Why is this bad? + /// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails. + /// A good custom message should be more about why the failure of the assertion is problematic + /// and not what is failed because the assertion already conveys that. + /// + /// ### Known problems + /// This lint cannot check the quality of the custom panic messages. + /// Hence, you can suppress this lint simply by adding placeholder messages + /// like "assertion failed". However, we recommend coming up with good messages + /// that provide useful information instead of placeholder messages that + /// don't provide any extra information. + /// + /// ### Example + /// ```rust + /// # struct Service { ready: bool } + /// fn call(service: Service) { + /// assert!(service.ready); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # struct Service { ready: bool } + /// fn call(service: Service) { + /// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests"); + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub MISSING_ASSERT_MESSAGE, + restriction, + "checks assertions without a custom panic message" +} + +declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]); + +impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_macro | sym::debug_assert_macro) => true, + Some( + sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro, + ) => false, + _ => return, + }; + + // This lint would be very noisy in tests, so just ignore if we're in test context + if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) { + return; + } + + let panic_expn = if single_argument { + let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return }; + panic_expn + } else { + let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + panic_expn + }; + + if let PanicExpn::Empty = panic_expn { + span_lint_and_help( + cx, + MISSING_ASSERT_MESSAGE, + macro_call.span, + "assert without any message", + None, + "consider describing why the failing assert is problematic", + ); + } + } +} diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 5b1f03fc16c..f2773cad400 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -8,10 +8,10 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; -use hir::def_id::LocalDefId; use if_chain::if_chain; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::Visibility; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -21,8 +21,7 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Warns if there is missing doc for any documentable item - /// (public or private). + /// Warns if there is missing doc for any private documentable item /// /// ### Why is this bad? /// Doc is good. *rustc* has a `MISSING_DOCS` @@ -32,7 +31,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub MISSING_DOCS_IN_PRIVATE_ITEMS, restriction, - "detects missing documentation for public and private members" + "detects missing documentation for private members" } pub struct MissingDoc { @@ -107,11 +106,14 @@ impl MissingDoc { if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) { return; } + } else if def_id != CRATE_DEF_ID && cx.effective_visibilities.is_exported(def_id) { + return; } let has_doc = attrs .iter() .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); + if !has_doc { span_lint( cx, diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 63c575fca30..5418616ded0 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -11,6 +11,7 @@ use rustc_ast::Mutability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -120,33 +121,15 @@ fn collect_unsafe_exprs<'tcx>( unsafe_ops.push(("raw pointer dereference occurs here", expr.span)); }, - ExprKind::Call(path_expr, _) => match path_expr.kind { - ExprKind::Path(QPath::Resolved( - _, - hir::Path { - res: Res::Def(kind, def_id), - .. - }, - )) if kind.is_fn_like() => { - let sig = cx.tcx.fn_sig(*def_id); - if sig.0.unsafety() == Unsafety::Unsafe { - unsafe_ops.push(("unsafe function call occurs here", expr.span)); - } - }, - - ExprKind::Path(QPath::TypeRelative(..)) => { - if let Some(sig) = cx - .typeck_results() - .type_dependent_def_id(path_expr.hir_id) - .map(|def_id| cx.tcx.fn_sig(def_id)) - { - if sig.0.unsafety() == Unsafety::Unsafe { - unsafe_ops.push(("unsafe function call occurs here", expr.span)); - } - } - }, - - _ => {}, + ExprKind::Call(path_expr, _) => { + let sig = match *cx.typeck_results().expr_ty(path_expr).kind() { + ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(), + ty::FnPtr(sig) => sig, + _ => return Continue(Descend::Yes), + }; + if sig.unsafety() == Unsafety::Unsafe { + unsafe_ops.push(("unsafe function call occurs here", expr.span)); + } }, ExprKind::MethodCall(..) => { diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index fb9a4abd0b4..ed3e2c6e7f4 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::has_enclosing_paren; use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; @@ -60,8 +60,8 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { then { let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability); - let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { + let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); + let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 2ecb0487484..e1de494eb41 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -53,6 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder))) || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS)); if let ExprKind::Lit(_) = param.kind; + if param.span.ctxt() == expr.span.ctxt(); then { let Some(snip) = snippet_opt(cx, param.span) else { @@ -71,6 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); if let ExprKind::Lit(_) = param.kind; + if param.span.ctxt() == expr.span.ctxt(); if let Some(snip) = snippet_opt(cx, param.span); if !snip.starts_with("0o"); then { diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 87a8a2ed12b..25e8de94863 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -143,6 +143,10 @@ impl ArithmeticSideEffects { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { + if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node { + // At least for integers, shifts are already handled by the CTFE + return; + } let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); match ( @@ -151,10 +155,13 @@ impl ArithmeticSideEffects { ) { (None, None) => false, (None, Some(n)) | (Some(n), None) => match (&op.node, n) { - (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + // Division and module are always valid if applied to non-zero integers + (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true, + // Addition or subtracting zeros is always a no-op (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) - | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) - | (hir::BinOpKind::Mul, 0 | 1) => true, + // Multiplication by 1 or 0 will never overflow + | (hir::BinOpKind::Mul, 0 | 1) + => true, _ => false, }, (Some(_), Some(_)) => { diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index e7095ec191f..664d44d6504 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// let mut permissions = metadata.permissions(); /// permissions.set_readonly(false); /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub PERMISSIONS_SET_READONLY_FALSE, suspicious, "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`" diff --git a/clippy_lints/src/redundant_async_block.rs b/clippy_lints/src/redundant_async_block.rs new file mode 100644 index 00000000000..27ad4308637 --- /dev/null +++ b/clippy_lints/src/redundant_async_block.rs @@ -0,0 +1,84 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet}; +use rustc_ast::ast::*; +use rustc_ast::visit::Visitor as AstVisitor; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `async` block that only returns `await` on a future. + /// + /// ### Why is this bad? + /// It is simpler and more efficient to use the future directly. + /// + /// ### Example + /// ```rust + /// async fn f() -> i32 { + /// 1 + 2 + /// } + /// + /// let fut = async { + /// f().await + /// }; + /// ``` + /// Use instead: + /// ```rust + /// async fn f() -> i32 { + /// 1 + 2 + /// } + /// + /// let fut = f(); + /// ``` + #[clippy::version = "1.69.0"] + pub REDUNDANT_ASYNC_BLOCK, + complexity, + "`async { future.await }` can be replaced by `future`" +} +declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]); + +impl EarlyLintPass for RedundantAsyncBlock { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::Async(_, _, block) = &expr.kind && block.stmts.len() == 1 && + let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() && + let ExprKind::Await(future) = &last.kind && + !future.span.from_expansion() && + !await_in_expr(future) + { + span_lint_and_sugg( + cx, + REDUNDANT_ASYNC_BLOCK, + expr.span, + "this async expression only awaits a single future", + "you can reduce it to", + snippet(cx, future.span, "..").into_owned(), + Applicability::MachineApplicable, + ); + } + } +} + +/// Check whether an expression contains `.await` +fn await_in_expr(expr: &Expr) -> bool { + let mut detector = AwaitDetector::default(); + detector.visit_expr(expr); + detector.await_found +} + +#[derive(Default)] +struct AwaitDetector { + await_found: bool, +} + +impl<'ast> AstVisitor<'ast> for AwaitDetector { + fn visit_expr(&mut self, ex: &'ast Expr) { + match (&ex.kind, self.await_found) { + (ExprKind::Await(_), _) => self.await_found = true, + (_, false) => rustc_ast::visit::walk_expr(self, ex), + _ => (), + } + } +} diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 3fcdb4288ce..8abec06c641 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.67.0"] + #[clippy::version = "1.68.0"] pub SIZE_OF_REF, suspicious, "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 0f062cecf88..1aeac724ab1 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; @@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use rustc_span::SyntaxContext; use rustc_span::{sym, symbol::Ident, Span}; declare_clippy_lint! { @@ -80,43 +81,45 @@ impl<'tcx> LateLintPass<'tcx> for Swap { } fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) { + let ctxt = span.ctxt(); let mut applicability = Applicability::MachineApplicable; if !can_mut_borrow_both(cx, e1, e2) { - if let ExprKind::Index(lhs1, idx1) = e1.kind { - if let ExprKind::Index(lhs2, idx2) = e2.kind { - if eq_expr_value(cx, lhs1, lhs2) { - let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); + if let ExprKind::Index(lhs1, idx1) = e1.kind + && let ExprKind::Index(lhs2, idx2) = e2.kind + && eq_expr_value(cx, lhs1, lhs2) + && e1.span.ctxt() == ctxt + && e2.span.ctxt() == ctxt + { + let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); - if matches!(ty.kind(), ty::Slice(_)) - || matches!(ty.kind(), ty::Array(_, _)) - || is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_diagnostic_item(cx, ty, sym::VecDeque) - { - let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); - span_lint_and_sugg( - cx, - MANUAL_SWAP, - span, - &format!("this looks like you are swapping elements of `{slice}` manually"), - "try", - format!( - "{}.swap({}, {})", - slice.maybe_par(), - snippet_with_applicability(cx, idx1.span, "..", &mut applicability), - snippet_with_applicability(cx, idx2.span, "..", &mut applicability), - ), - applicability, - ); - } - } + if matches!(ty.kind(), ty::Slice(_)) + || matches!(ty.kind(), ty::Array(_, _)) + || is_type_diagnostic_item(cx, ty, sym::Vec) + || is_type_diagnostic_item(cx, ty, sym::VecDeque) + { + let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_SWAP, + span, + &format!("this looks like you are swapping elements of `{slice}` manually"), + "try", + format!( + "{}.swap({}, {});", + slice.maybe_par(), + snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0, + snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0, + ), + applicability, + ); } } return; } - let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability); - let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability); + let first = Sugg::hir_with_context(cx, e1, ctxt, "..", &mut applicability); + let second = Sugg::hir_with_context(cx, e2, ctxt, "..", &mut applicability); let Some(sugg) = std_or_core(cx) else { return }; span_lint_and_then( @@ -128,7 +131,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa diag.span_suggestion( span, "try", - format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()), + format!("{sugg}::mem::swap({}, {});", first.mut_addr(), second.mut_addr()), applicability, ); if !is_xor_based { @@ -144,19 +147,19 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { return; } - for w in block.stmts.windows(3) { + for [s1, s2, s3] in block.stmts.array_windows::<3>() { if_chain! { // let t = foo(); - if let StmtKind::Local(tmp) = w[0].kind; + if let StmtKind::Local(tmp) = s1.kind; if let Some(tmp_init) = tmp.init; if let PatKind::Binding(.., ident, None) = tmp.pat.kind; // foo() = bar(); - if let StmtKind::Semi(first) = w[1].kind; + if let StmtKind::Semi(first) = s2.kind; if let ExprKind::Assign(lhs1, rhs1, _) = first.kind; // bar() = t; - if let StmtKind::Semi(second) = w[2].kind; + if let StmtKind::Semi(second) = s3.kind; if let ExprKind::Assign(lhs2, rhs2, _) = second.kind; if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind; if rhs2.segments.len() == 1; @@ -164,8 +167,15 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if ident.name == rhs2.segments[0].ident.name; if eq_expr_value(cx, tmp_init, lhs1); if eq_expr_value(cx, rhs1, lhs2); + + let ctxt = s1.span.ctxt(); + if s2.span.ctxt() == ctxt; + if s3.span.ctxt() == ctxt; + if first.span.ctxt() == ctxt; + if second.span.ctxt() == ctxt; + then { - let span = w[0].span.to(second.span); + let span = s1.span.to(s3.span); generate_swap_warning(cx, lhs1, lhs2, span, false); } } @@ -246,17 +256,20 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr< /// Implementation of the xor case for `MANUAL_SWAP` lint. fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { - for window in block.stmts.windows(3) { + for [s1, s2, s3] in block.stmts.array_windows::<3>() { if_chain! { - if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]); - if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]); - if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]); + let ctxt = s1.span.ctxt(); + if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt); + if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt); + if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt); if eq_expr_value(cx, lhs0, rhs1); if eq_expr_value(cx, lhs2, rhs1); if eq_expr_value(cx, lhs1, rhs0); if eq_expr_value(cx, lhs1, rhs2); + if s2.span.ctxt() == ctxt; + if s3.span.ctxt() == ctxt; then { - let span = window[0].span.to(window[2].span); + let span = s1.span.to(s3.span); generate_swap_warning(cx, lhs0, rhs0, span, true); } }; @@ -264,9 +277,12 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { } /// Returns the lhs and rhs of an xor assignment statement. -fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> { - if let StmtKind::Semi(expr) = stmt.kind { - if let ExprKind::AssignOp( +fn extract_sides_of_xor_assign<'a, 'hir>( + stmt: &'a Stmt<'hir>, + ctxt: SyntaxContext, +) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> { + if let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::AssignOp( Spanned { node: BinOpKind::BitXor, .. @@ -274,9 +290,10 @@ fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Ex lhs, rhs, ) = expr.kind - { - return Some((lhs, rhs)); - } + && expr.span.ctxt() == ctxt + { + Some((lhs, rhs)) + } else { + None } - None } diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index c01cbe5090f..0dc30f7a935 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -458,7 +458,7 @@ declare_clippy_lint! { /// ```rust /// let null_fn: Option = None; /// ``` - #[clippy::version = "1.67.0"] + #[clippy::version = "1.68.0"] pub TRANSMUTE_NULL_TO_FN, correctness, "transmute results in a null function pointer, which is undefined behavior" diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index d6167a62169..3430b6e3734 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -5,7 +5,7 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind}; +use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -41,6 +41,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { ); } } else { + if let ExprKind::Match(_, _, MatchSource::AwaitDesugar) = init.kind { + return + } + span_lint_and_then( cx, LET_UNIT_VALUE, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index e7c54000684..8ea5475fb25 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -10,8 +10,8 @@ use rustc_hir::{ def::{CtorOf, DefKind, Res}, def_id::LocalDefId, intravisit::{walk_inf, walk_ty, Visitor}, - Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, - TyKind, + Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item, + ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that // we're in an `impl` or nested item, that we don't want to lint let stack_item = if_chain! { - if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind; + if let ItemKind::Impl(Impl { self_ty, generics,.. }) = item.kind; if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind; let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; if parameters.as_ref().map_or(true, |params| { @@ -105,10 +105,17 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if !item.span.from_expansion(); if !is_from_proc_macro(cx, item); // expensive, should be last check then { + // Self cannot be used inside const generic parameters + let types_to_skip = generics.params.iter().filter_map(|param| { + match param { + GenericParam { kind: GenericParamKind::Const { ty: Ty { hir_id, ..}, ..}, ..} => Some(*hir_id), + _ => None, + } + }).chain(std::iter::once(self_ty.hir_id)).collect(); StackItem::Check { impl_id: item.owner_id.def_id, in_body: 0, - types_to_skip: std::iter::once(self_ty.hir_id).collect(), + types_to_skip, } } else { StackItem::NoCheck diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index c37e5bb6716..f31c3fdb095 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -588,7 +588,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }, } }, - ExprKind::Err(_) => kind!("Err"), + ExprKind::Err(_) => kind!("Err(_)"), ExprKind::DropTemps(expr) => { bind!(self, expr); kind!("DropTemps({expr})"); diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs new file mode 100644 index 00000000000..be56b842b98 --- /dev/null +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -0,0 +1,23 @@ +use clippy_utils::macros::collect_ast_format_args; +use rustc_ast::{Expr, ExprKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Collects [`rustc_ast::FormatArgs`] so that future late passes can call + /// [`clippy_utils::macros::find_format_args`] + pub FORMAT_ARGS_COLLECTOR, + internal_warn, + "collects `format_args` AST nodes for use in later lints" +} + +declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); + +impl EarlyLintPass for FormatArgsCollector { + fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::FormatArgs(args) = &expr.kind { + collect_ast_format_args(expr.span, args); + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index b1b5164ffb3..3d0d4a52511 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -26,7 +26,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Ident; use rustc_span::{sym, Loc, Span, Symbol}; use serde::{ser::SerializeStruct, Serialize, Serializer}; -use std::collections::BinaryHeap; +use std::collections::{BTreeSet, BinaryHeap}; use std::fmt; use std::fmt::Write as _; use std::fs::{self, OpenOptions}; @@ -264,6 +264,9 @@ struct LintMetadata { /// This field is only used in the output and will only be /// mapped shortly before the actual output. applicability: Option, + /// All the past names of lints which have been renamed. + #[serde(skip_serializing_if = "BTreeSet::is_empty")] + former_ids: BTreeSet, } impl LintMetadata { @@ -283,6 +286,7 @@ impl LintMetadata { version, docs, applicability: None, + former_ids: BTreeSet::new(), } } } @@ -901,6 +905,7 @@ fn collect_renames(lints: &mut Vec) { if name == lint_name; if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); then { + lint.former_ids.insert(past_name.to_owned()); writeln!(collected, "* `{past_name}`").unwrap(); names.push(past_name.to_string()); } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 787e9fd982c..dc647af264c 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1,5 +1,6 @@ pub mod author; pub mod conf; pub mod dump_hir; +pub mod format_args_collector; #[cfg(feature = "internal")] pub mod internal_lints; diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index df335038881..8114a8463fa 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; +use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::{is_in_cfg_test, is_in_test_function}; -use rustc_ast::LitKind; +use rustc_ast::token::LitKind; +use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; +use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, BytePos}; @@ -297,34 +298,40 @@ impl<'tcx> LateLintPass<'tcx> for Write { _ => return, } - let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return }; + find_format_args(cx, expr, macro_call.expn, |format_args| { + // ignore `writeln!(w)` and `write!(v, some_macro!())` + if format_args.span.from_expansion() { + return; + } - // ignore `writeln!(w)` and `write!(v, some_macro!())` - if format_args.format_string.span.from_expansion() { - return; - } + match diag_name { + sym::print_macro | sym::eprint_macro | sym::write_macro => { + check_newline(cx, format_args, ¯o_call, name); + }, + sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { + check_empty_string(cx, format_args, ¯o_call, name); + }, + _ => {}, + } - match diag_name { - sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, &format_args, ¯o_call, name); - }, - sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, &format_args, ¯o_call, name); - }, - _ => {}, - } + check_literal(cx, format_args, name); - check_literal(cx, &format_args, name); - - if !self.in_debug_impl { - for arg in &format_args.args { - if arg.format.r#trait == sym::Debug { - span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting"); + if !self.in_debug_impl { + for piece in &format_args.template { + if let &FormatArgsPiece::Placeholder(FormatPlaceholder { + span: Some(span), + format_trait: FormatTrait::Debug, + .. + }) = piece + { + span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); + } } } - } + }); } } + fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind && let Some(trait_id) = trait_ref.trait_def_id() @@ -335,16 +342,18 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { } } -fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { - let format_string_parts = &format_args.format_string.parts; - let mut format_string_span = format_args.format_string.span; - - let Some(last) = format_string_parts.last() else { return }; +fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { + let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return }; let count_vertical_whitespace = || { - format_string_parts + format_args + .template .iter() - .flat_map(|part| part.as_str().chars()) + .filter_map(|piece| match piece { + FormatArgsPiece::Literal(literal) => Some(literal), + FormatArgsPiece::Placeholder(_) => None, + }) + .flat_map(|literal| literal.as_str().chars()) .filter(|ch| matches!(ch, '\r' | '\n')) .count() }; @@ -352,10 +361,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c if last.as_str().ends_with('\n') // ignore format strings with other internal vertical whitespace && count_vertical_whitespace() == 1 - - // ignore trailing arguments: `print!("Issue\n{}", 1265);` - && format_string_parts.len() > format_args.args.len() { + let mut format_string_span = format_args.span; + let lint = if name == "write" { format_string_span = expand_past_previous_comma(cx, format_string_span); @@ -373,7 +381,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return }; - if format_string_parts.len() == 1 && last.as_str() == "\n" { + if format_args.template.len() == 1 && last.as_str() == "\n" { // print!("\n"), write!(f, "\n") diag.multipart_suggestion( @@ -398,11 +406,12 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c } } -fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { - if let [part] = &format_args.format_string.parts[..] - && let mut span = format_args.format_string.span - && part.as_str() == "\n" +fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { + if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..] + && literal.as_str() == "\n" { + let mut span = format_args.span; + let lint = if name == "writeln" { span = expand_past_previous_comma(cx, span); @@ -428,33 +437,43 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, ma } } -fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) { - let mut counts = HirIdMap::::default(); - for param in format_args.params() { - *counts.entry(param.value.hir_id).or_default() += 1; +fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { + let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos); + + let mut counts = vec![0u32; format_args.arguments.all_args().len()]; + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(placeholder) = piece { + counts[arg_index(&placeholder.argument)] += 1; + } } - for arg in &format_args.args { - let value = arg.param.value; - - if counts[&value.hir_id] == 1 - && arg.format.is_default() - && let ExprKind::Lit(lit) = &value.kind - && !value.span.from_expansion() - && let Some(value_string) = snippet_opt(cx, value.span) - { - let (replacement, replace_raw) = match lit.node { - LitKind::Str(..) => extract_str_literal(&value_string), - LitKind::Char(ch) => ( - match ch { - '"' => "\\\"", - '\'' => "'", + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(FormatPlaceholder { + argument, + span: Some(placeholder_span), + format_trait: FormatTrait::Display, + format_options, + }) = piece + && *format_options == FormatOptions::default() + && let index = arg_index(argument) + && counts[index] == 1 + && let Some(arg) = format_args.arguments.by_index(index) + && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind + && !arg.expr.span.from_expansion() + && let Some(value_string) = snippet_opt(cx, arg.expr.span) + { + let (replacement, replace_raw) = match lit.kind { + LitKind::Str | LitKind::StrRaw(_) => extract_str_literal(&value_string), + LitKind::Char => ( + match lit.symbol.as_str() { + "\"" => "\\\"", + "\\'" => "'", _ => &value_string[1..value_string.len() - 1], } .to_string(), false, ), - LitKind::Bool(b) => (b.to_string(), false), + LitKind::Bool => (lit.symbol.to_string(), false), _ => continue, }; @@ -464,7 +483,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: & PRINT_LITERAL }; - let format_string_is_raw = format_args.format_string.style.is_some(); + let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue }; + let format_string_is_raw = format_string_snippet.starts_with('r'); + let replacement = match (format_string_is_raw, replace_raw) { (false, false) => Some(replacement), (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")), @@ -485,23 +506,24 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: & span_lint_and_then( cx, lint, - value.span, + arg.expr.span, "literal with an empty format string", |diag| { if let Some(replacement) = replacement // `format!("{}", "a")`, `format!("{named}", named = "b") // ~~~~~ ~~~~~~~~~~~~~ - && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id) + && let Some(removal_span) = format_arg_removal_span(format_args, index) { let replacement = replacement.replace('{', "{{").replace('}', "}}"); diag.multipart_suggestion( "try this", - vec![(arg.span, replacement), (value_span, String::new())], + vec![(*placeholder_span, replacement), (removal_span, String::new())], Applicability::MachineApplicable, ); } }, ); + } } } diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 173469f6cdc..124ebd164e6 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.69" +version = "0.1.70" edition = "2021" publish = false diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index bcfedd07ed1..44b6b9f7b0b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -617,7 +617,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec, def_id: DefId) -> bool { matches!(name, sym::assert_macro | sym::debug_assert_macro) } +#[derive(Debug)] pub enum PanicExpn<'a> { /// No arguments - `panic!()` Empty, @@ -226,10 +231,7 @@ pub enum PanicExpn<'a> { impl<'a> PanicExpn<'a> { pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option { - if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) { - return None; - } - let ExprKind::Call(callee, [arg]) = &expr.kind else { return None }; + let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None }; let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None }; let result = match path.segments.last().unwrap().ident.as_str() { "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty, @@ -239,6 +241,21 @@ impl<'a> PanicExpn<'a> { Self::Display(e) }, "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?), + // Since Rust 1.52, `assert_{eq,ne}` macros expand to use: + // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));` + "assert_failed" => { + // It should have 4 arguments in total (we already matched with the first argument, + // so we're just checking for 3) + if rest.len() != 3 { + return None; + } + // `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message) + let msg_arg = &rest[2]; + match msg_arg.kind { + ExprKind::Call(_, [fmt_arg]) => Self::Format(FormatArgsExpn::parse(cx, fmt_arg)?), + _ => Self::Empty, + } + }, _ => return None, }; Some(result) @@ -251,7 +268,17 @@ pub fn find_assert_args<'a>( expr: &'a Expr<'a>, expn: ExpnId, ) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> { - find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p)) + find_assert_args_inner(cx, expr, expn).map(|([e], mut p)| { + // `assert!(..)` expands to `core::panicking::panic("assertion failed: ...")` (which we map to + // `PanicExpn::Str(..)`) and `assert!(.., "..")` expands to + // `core::panicking::panic_fmt(format_args!(".."))` (which we map to `PanicExpn::Format(..)`). + // So even we got `PanicExpn::Str(..)` that means there is no custom message provided + if let PanicExpn::Str(_) = p { + p = PanicExpn::Empty; + } + + (e, p) + }) } /// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro @@ -275,13 +302,12 @@ fn find_assert_args_inner<'a, const N: usize>( Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?, }; let mut args = ArrayVec::new(); - let mut panic_expn = None; - let _: Option = for_each_expr(expr, |e| { + let panic_expn = for_each_expr(expr, |e| { if args.is_full() { - if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() { - panic_expn = PanicExpn::parse(cx, e); + match PanicExpn::parse(cx, e) { + Some(expn) => ControlFlow::Break(expn), + None => ControlFlow::Continue(Descend::Yes), } - ControlFlow::Continue(Descend::from(panic_expn.is_none())) } else if is_assert_arg(cx, e, expn) { args.push(e); ControlFlow::Continue(Descend::No) @@ -339,6 +365,77 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> } } +thread_local! { + /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be + /// able to access the many features of a [`LateContext`]. + /// + /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an + /// assumption that the early pass the populates the map and the later late passes will all be + /// running on the same thread. + static AST_FORMAT_ARGS: RefCell> = { + static CALLED: AtomicBool = AtomicBool::new(false); + debug_assert!( + !CALLED.swap(true, Ordering::SeqCst), + "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread", + ); + + RefCell::default() + }; +} + +/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by +/// `FormatArgsCollector` +pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) { + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args.borrow_mut().insert(span, format_args.clone()); + }); +} + +/// Calls `callback` with an AST [`FormatArgs`] node if one is found +pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) { + let format_args_expr = for_each_expr(start, |expr| { + let ctxt = expr.span.ctxt(); + if ctxt == start.span.ctxt() { + ControlFlow::Continue(Descend::Yes) + } else if ctxt.outer_expn().is_descendant_of(expn_id) + && macro_backtrace(expr.span) + .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) + .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) + { + ControlFlow::Break(expr) + } else { + ControlFlow::Continue(Descend::No) + } + }); + + if let Some(format_args_expr) = format_args_expr { + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args.borrow().get(&format_args_expr.span).map(callback); + }); + } +} + +/// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value +/// `10` +/// +/// ```ignore +/// format("{}.{}", 10, 11) +/// // ^^^^ +/// ``` +pub fn format_arg_removal_span(format_args: &FormatArgs, index: usize) -> Option { + let ctxt = format_args.span.ctxt(); + + let current = hygiene::walk_chain(format_args.arguments.by_index(index)?.expr.span, ctxt); + + let prev = if index == 0 { + format_args.span + } else { + hygiene::walk_chain(format_args.arguments.by_index(index - 1)?.expr.span, ctxt) + }; + + Some(current.with_lo(prev.hi())) +} + /// The format string doesn't exist in the HIR, so we reassemble it from source code #[derive(Debug)] pub struct FormatString { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 41e34eba0ad..e0ea3952785 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -16,9 +16,9 @@ use rustc_infer::infer::{ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, - PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind, + Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 80eee368178..5c9f76dbbc6 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.69" +version = "0.1.70" edition = "2021" publish = false diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 653121af54d..27d32f39003 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -8,12 +8,16 @@ repository = "https://github.com/rust-lang/rust-clippy" categories = ["development-tools"] edition = "2021" publish = false +default-run = "lintcheck" [dependencies] +anyhow = "1.0.69" cargo_metadata = "0.15.3" -clap = "4.1.4" +clap = { version = "4.1.8", features = ["derive", "env"] } +crates_io_api = "0.8.1" crossbeam-channel = "0.5.6" flate2 = "1.0" +indicatif = "0.17.3" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" @@ -24,3 +28,11 @@ walkdir = "2.3" [features] deny-warnings = [] + +[[bin]] +name = "lintcheck" +path = "src/main.rs" + +[[bin]] +name = "popular-crates" +path = "src/popular-crates.rs" diff --git a/lintcheck/README.md b/lintcheck/README.md index 6142de5e313..e997eb47e32 100644 --- a/lintcheck/README.md +++ b/lintcheck/README.md @@ -25,6 +25,15 @@ the repo root. The results will then be saved to `lintcheck-logs/custom_logs.toml`. +The `custom.toml` file may be built using recently most +downloaded crates by using the `popular-crates` binary from the `lintcheck` +directory. For example, to retrieve the 100 recently most downloaded crates: + +``` +cargo run --release --bin popular-crates -- -n 100 custom.toml +``` + + ### Configuring the Crate Sources The sources to check are saved in a `toml` file. There are three types of diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index e0244ddcecb..3f01e9bb0a7 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -1,131 +1,79 @@ -use clap::{Arg, ArgAction, ArgMatches, Command}; -use std::env; -use std::path::PathBuf; +use clap::Parser; +use std::{num::NonZeroUsize, path::PathBuf}; -fn get_clap_config() -> ArgMatches { - Command::new("lintcheck") - .about("run clippy on a set of crates and check output") - .args([ - Arg::new("only") - .action(ArgAction::Set) - .value_name("CRATE") - .long("only") - .help("Only process a single crate of the list"), - Arg::new("crates-toml") - .action(ArgAction::Set) - .value_name("CRATES-SOURCES-TOML-PATH") - .long("crates-toml") - .help("Set the path for a crates.toml where lintcheck should read the sources from"), - Arg::new("threads") - .action(ArgAction::Set) - .value_name("N") - .value_parser(clap::value_parser!(usize)) - .short('j') - .long("jobs") - .help("Number of threads to use, 0 automatic choice"), - Arg::new("fix") - .long("fix") - .help("Runs cargo clippy --fix and checks if all suggestions apply"), - Arg::new("filter") - .long("filter") - .action(ArgAction::Append) - .value_name("clippy_lint_name") - .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"), - Arg::new("markdown") - .long("markdown") - .help("Change the reports table to use markdown links"), - Arg::new("recursive") - .long("recursive") - .help("Run clippy on the dependencies of crates specified in crates-toml") - .conflicts_with("threads") - .conflicts_with("fix"), - ]) - .get_matches() -} - -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Parser)] pub(crate) struct LintcheckConfig { - /// max number of jobs to spawn (default 1) + /// Number of threads to use (default: all unless --fix or --recursive) + #[clap( + long = "jobs", + short = 'j', + value_name = "N", + default_value_t = 0, + hide_default_value = true + )] pub max_jobs: usize, - /// we read the sources to check from here + /// Set the path for a crates.toml where lintcheck should read the sources from + #[clap( + long = "crates-toml", + value_name = "CRATES-SOURCES-TOML-PATH", + default_value = "lintcheck/lintcheck_crates.toml", + hide_default_value = true, + env = "LINTCHECK_TOML", + hide_env = true + )] pub sources_toml_path: PathBuf, - /// we save the clippy lint results here - pub lintcheck_results_path: PathBuf, - /// Check only a specified package + /// File to save the clippy lint results here + #[clap(skip = "")] + pub lintcheck_results_path: PathBuf, // Overridden in new() + /// Only process a single crate on the list + #[clap(long, value_name = "CRATE")] pub only: Option, - /// whether to just run --fix and not collect all the warnings + /// Runs cargo clippy --fix and checks if all suggestions apply + #[clap(long, conflicts_with("max_jobs"))] pub fix: bool, - /// A list of lints that this lintcheck run should focus on + /// Apply a filter to only collect specified lints, this also overrides `allow` attributes + #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)] pub lint_filter: Vec, - /// Indicate if the output should support markdown syntax + /// Change the reports table to use markdown links + #[clap(long)] pub markdown: bool, - /// Run clippy on the dependencies of crates + /// Run clippy on the dependencies of crates specified in crates-toml + #[clap(long, conflicts_with("max_jobs"))] pub recursive: bool, } impl LintcheckConfig { pub fn new() -> Self { - let clap_config = get_clap_config(); - - // first, check if we got anything passed via the LINTCHECK_TOML env var, - // if not, ask clap if we got any value for --crates-toml - // if not, use the default "lintcheck/lintcheck_crates.toml" - let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { - clap_config - .get_one::("crates-toml") - .map_or("lintcheck/lintcheck_crates.toml", |s| &**s) - .into() - }); - - let markdown = clap_config.contains_id("markdown"); - let sources_toml_path = PathBuf::from(sources_toml); + let mut config = LintcheckConfig::parse(); // for the path where we save the lint results, get the filename without extension (so for // wasd.toml, use "wasd"...) - let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); - let lintcheck_results_path = PathBuf::from(format!( + let filename: PathBuf = config.sources_toml_path.file_stem().unwrap().into(); + config.lintcheck_results_path = PathBuf::from(format!( "lintcheck-logs/{}_logs.{}", filename.display(), - if markdown { "md" } else { "txt" } + if config.markdown { "md" } else { "txt" } )); - // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and - // use half of that for the physical core count - // by default use a single thread - let max_jobs = match clap_config.get_one::("threads") { - Some(&0) => { - // automatic choice - // Rayon seems to return thread count so half that for core count - rayon::current_num_threads() / 2 - }, - Some(&threads) => threads, - // no -j passed, use a single thread - None => 1, + // look at the --threads arg, if 0 is passed, use the threads count + if config.max_jobs == 0 { + config.max_jobs = if config.fix || config.recursive { + 1 + } else { + std::thread::available_parallelism().map_or(1, NonZeroUsize::get) + }; }; - let lint_filter: Vec = clap_config - .get_many::("filter") - .map(|iter| { - iter.map(|lint_name| { - let mut filter = lint_name.replace('_', "-"); - if !filter.starts_with("clippy::") { - filter.insert_str(0, "clippy::"); - } - filter - }) - .collect() - }) - .unwrap_or_default(); - - LintcheckConfig { - max_jobs, - sources_toml_path, - lintcheck_results_path, - only: clap_config.get_one::("only").map(String::from), - fix: clap_config.contains_id("fix"), - lint_filter, - markdown, - recursive: clap_config.contains_id("recursive"), + for lint_name in &mut config.lint_filter { + *lint_name = format!( + "clippy::{}", + lint_name + .strip_prefix("clippy::") + .unwrap_or(lint_name) + .replace('_', "-") + ); } + + config } } diff --git a/lintcheck/src/popular-crates.rs b/lintcheck/src/popular-crates.rs new file mode 100644 index 00000000000..fdab984ad86 --- /dev/null +++ b/lintcheck/src/popular-crates.rs @@ -0,0 +1,65 @@ +#![deny(clippy::pedantic)] + +use clap::Parser; +use crates_io_api::{CratesQueryBuilder, Sort, SyncClient}; +use indicatif::ProgressBar; +use std::collections::HashSet; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::PathBuf; +use std::time::Duration; + +#[derive(Parser)] +struct Opts { + /// Output TOML file name + output: PathBuf, + /// Number of crate names to download + #[clap(short, long, default_value_t = 100)] + number: usize, + /// Do not output progress + #[clap(short, long)] + quiet: bool, +} + +fn main() -> anyhow::Result<()> { + let opts = Opts::parse(); + let mut output = BufWriter::new(File::create(opts.output)?); + output.write_all(b"[crates]\n")?; + let client = SyncClient::new( + "clippy/lintcheck (github.com/rust-lang/rust-clippy/)", + Duration::from_secs(1), + )?; + let mut seen_crates = HashSet::new(); + let pb = if opts.quiet { + None + } else { + Some(ProgressBar::new(opts.number as u64)) + }; + let mut query = CratesQueryBuilder::new() + .sort(Sort::RecentDownloads) + .page_size(100) + .build(); + while seen_crates.len() < opts.number { + let retrieved = client.crates(query.clone())?.crates; + if retrieved.is_empty() { + eprintln!("No more than {} crates available from API", seen_crates.len()); + break; + } + for c in retrieved { + if seen_crates.insert(c.name.clone()) { + output.write_all( + format!( + "{} = {{ name = '{}', versions = ['{}'] }}\n", + c.name, c.name, c.max_version + ) + .as_bytes(), + )?; + if let Some(pb) = &pb { + pb.inc(1); + } + } + } + query.set_page(query.page() + 1); + } + Ok(()) +} diff --git a/rust-toolchain b/rust-toolchain index cfe845ec78f..d788c6359d7 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-02-25" +channel = "nightly-2023-03-10" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/driver.rs b/src/driver.rs index dd183362f27..f08393c303e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -176,7 +176,7 @@ Common options: --rustc Pass all args to rustc -V, --version Print version info and exit -Other options are the same as `cargo check`. +For the other options see `cargo check --help`. To allow or deny a lint from the command line you can use `cargo clippy --` with: diff --git a/src/main.rs b/src/main.rs index 82147eba33f..c5e9b96cf3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ Common options: -V, --version Print version info and exit --explain LINT Print the documentation for a given lint -Other options are the same as `cargo check`. +For the other options see `cargo check --help`. To allow or deny a lint from the command line you can use `cargo clippy --` with: diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 6d0022f7a5c..9643c2c9707 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -7,6 +7,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] +use itertools::Itertools; use std::path::PathBuf; use std::process::Command; use test_utils::IS_RUSTC_TEST_SUITE; @@ -19,8 +20,10 @@ fn dogfood_clippy() { return; } + let mut failed_packages = Vec::new(); + // "" is the root package - for package in &[ + for package in [ "", "clippy_dev", "clippy_lints", @@ -28,8 +31,16 @@ fn dogfood_clippy() { "lintcheck", "rustc_tools_util", ] { - run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); + if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) { + failed_packages.push(if package.is_empty() { "root" } else { package }); + } } + + assert!( + !failed_packages.is_empty(), + "Dogfood failed for packages `{}`", + failed_packages.iter().format(", "), + ) } #[test] @@ -71,7 +82,7 @@ fn run_metadata_collection_lint() { run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]); } -fn run_clippy_for_package(project: &str, args: &[&str]) { +fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); @@ -107,5 +118,5 @@ fn run_clippy_for_package(project: &str, args: &[&str]) { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - assert!(output.status.success()); + output.status.success() } diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index 2611e3a785f..ee7d2ba444b 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -45,24 +45,32 @@ impl_arith!( Div, Custom, Custom, div; Mul, Custom, Custom, mul; Rem, Custom, Custom, rem; + Shl, Custom, Custom, shl; + Shr, Custom, Custom, shr; Sub, Custom, Custom, sub; Add, Custom, &Custom, add; Div, Custom, &Custom, div; Mul, Custom, &Custom, mul; Rem, Custom, &Custom, rem; + Shl, Custom, &Custom, shl; + Shr, Custom, &Custom, shr; Sub, Custom, &Custom, sub; Add, &Custom, Custom, add; Div, &Custom, Custom, div; Mul, &Custom, Custom, mul; Rem, &Custom, Custom, rem; + Shl, &Custom, Custom, shl; + Shr, &Custom, Custom, shr; Sub, &Custom, Custom, sub; Add, &Custom, &Custom, add; Div, &Custom, &Custom, div; Mul, &Custom, &Custom, mul; Rem, &Custom, &Custom, rem; + Shl, &Custom, &Custom, shl; + Shr, &Custom, &Custom, shr; Sub, &Custom, &Custom, sub; ); @@ -71,24 +79,32 @@ impl_assign_arith!( DivAssign, Custom, Custom, div_assign; MulAssign, Custom, Custom, mul_assign; RemAssign, Custom, Custom, rem_assign; + ShlAssign, Custom, Custom, shl_assign; + ShrAssign, Custom, Custom, shr_assign; SubAssign, Custom, Custom, sub_assign; AddAssign, Custom, &Custom, add_assign; DivAssign, Custom, &Custom, div_assign; MulAssign, Custom, &Custom, mul_assign; RemAssign, Custom, &Custom, rem_assign; + ShlAssign, Custom, &Custom, shl_assign; + ShrAssign, Custom, &Custom, shr_assign; SubAssign, Custom, &Custom, sub_assign; AddAssign, &Custom, Custom, add_assign; DivAssign, &Custom, Custom, div_assign; MulAssign, &Custom, Custom, mul_assign; RemAssign, &Custom, Custom, rem_assign; + ShlAssign, &Custom, Custom, shl_assign; + ShrAssign, &Custom, Custom, shr_assign; SubAssign, &Custom, Custom, sub_assign; AddAssign, &Custom, &Custom, add_assign; DivAssign, &Custom, &Custom, div_assign; MulAssign, &Custom, &Custom, mul_assign; RemAssign, &Custom, &Custom, rem_assign; + ShlAssign, &Custom, &Custom, shl_assign; + ShrAssign, &Custom, &Custom, shr_assign; SubAssign, &Custom, &Custom, sub_assign; ); @@ -297,6 +313,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _custom %= &Custom; _custom *= Custom; _custom *= &Custom; + _custom >>= Custom; + _custom >>= &Custom; + _custom <<= Custom; + _custom <<= &Custom; _custom += -Custom; _custom += &-Custom; _custom -= -Custom; @@ -307,6 +327,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _custom %= &-Custom; _custom *= -Custom; _custom *= &-Custom; + _custom >>= -Custom; + _custom >>= &-Custom; + _custom <<= -Custom; + _custom <<= &-Custom; // Binary _n = _n + 1; @@ -347,6 +371,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _custom = Custom + &Custom; _custom = &Custom + Custom; _custom = &Custom + &Custom; + _custom = _custom >> _custom; + _custom = _custom >> &_custom; + _custom = Custom << _custom; + _custom = &Custom << _custom; // Unary _n = -_n; diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 17a2448fbfc..3895f08964c 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,5 +1,5 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:270:5 + --> $DIR/arithmetic_side_effects.rs:286:5 | LL | _n += 1; | ^^^^^^^ @@ -7,592 +7,640 @@ LL | _n += 1; = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:271:5 + --> $DIR/arithmetic_side_effects.rs:287:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:272:5 + --> $DIR/arithmetic_side_effects.rs:288:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:273:5 + --> $DIR/arithmetic_side_effects.rs:289:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:274:5 + --> $DIR/arithmetic_side_effects.rs:290:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:275:5 + --> $DIR/arithmetic_side_effects.rs:291:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:276:5 + --> $DIR/arithmetic_side_effects.rs:292:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:277:5 + --> $DIR/arithmetic_side_effects.rs:293:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:278:5 + --> $DIR/arithmetic_side_effects.rs:294:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:279:5 + --> $DIR/arithmetic_side_effects.rs:295:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:280:5 + --> $DIR/arithmetic_side_effects.rs:296:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:281:5 + --> $DIR/arithmetic_side_effects.rs:297:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:282:5 + --> $DIR/arithmetic_side_effects.rs:298:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:283:5 + --> $DIR/arithmetic_side_effects.rs:299:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:284:5 + --> $DIR/arithmetic_side_effects.rs:300:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:285:5 + --> $DIR/arithmetic_side_effects.rs:301:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:286:5 + --> $DIR/arithmetic_side_effects.rs:302:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:287:5 + --> $DIR/arithmetic_side_effects.rs:303:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:288:5 + --> $DIR/arithmetic_side_effects.rs:304:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:289:5 + --> $DIR/arithmetic_side_effects.rs:305:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:290:5 + --> $DIR/arithmetic_side_effects.rs:306:5 | LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:291:5 + --> $DIR/arithmetic_side_effects.rs:307:5 | LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:292:5 + --> $DIR/arithmetic_side_effects.rs:308:5 | LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:293:5 + --> $DIR/arithmetic_side_effects.rs:309:5 | LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:294:5 + --> $DIR/arithmetic_side_effects.rs:310:5 | LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:295:5 + --> $DIR/arithmetic_side_effects.rs:311:5 | LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:296:5 + --> $DIR/arithmetic_side_effects.rs:312:5 | LL | _custom %= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:297:5 + --> $DIR/arithmetic_side_effects.rs:313:5 | LL | _custom %= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:298:5 + --> $DIR/arithmetic_side_effects.rs:314:5 | LL | _custom *= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:299:5 + --> $DIR/arithmetic_side_effects.rs:315:5 | LL | _custom *= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:300:5 + --> $DIR/arithmetic_side_effects.rs:316:5 + | +LL | _custom >>= Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:317:5 + | +LL | _custom >>= &Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:318:5 + | +LL | _custom <<= Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:319:5 + | +LL | _custom <<= &Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:320:5 | LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:301:5 + --> $DIR/arithmetic_side_effects.rs:321:5 | LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:302:5 + --> $DIR/arithmetic_side_effects.rs:322:5 | LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:303:5 + --> $DIR/arithmetic_side_effects.rs:323:5 | LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:304:5 + --> $DIR/arithmetic_side_effects.rs:324:5 | LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:305:5 + --> $DIR/arithmetic_side_effects.rs:325:5 | LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:306:5 + --> $DIR/arithmetic_side_effects.rs:326:5 | LL | _custom %= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:307:5 + --> $DIR/arithmetic_side_effects.rs:327:5 | LL | _custom %= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:308:5 + --> $DIR/arithmetic_side_effects.rs:328:5 | LL | _custom *= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:309:5 + --> $DIR/arithmetic_side_effects.rs:329:5 | LL | _custom *= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:312:10 + --> $DIR/arithmetic_side_effects.rs:330:5 + | +LL | _custom >>= -Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:331:5 + | +LL | _custom >>= &-Custom; + | ^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:332:5 + | +LL | _custom <<= -Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:333:5 + | +LL | _custom <<= &-Custom; + | ^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:336:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:313:10 + --> $DIR/arithmetic_side_effects.rs:337:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:314:10 + --> $DIR/arithmetic_side_effects.rs:338:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:315:10 + --> $DIR/arithmetic_side_effects.rs:339:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:316:10 + --> $DIR/arithmetic_side_effects.rs:340:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:317:10 + --> $DIR/arithmetic_side_effects.rs:341:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:318:10 + --> $DIR/arithmetic_side_effects.rs:342:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:319:10 + --> $DIR/arithmetic_side_effects.rs:343:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:320:10 + --> $DIR/arithmetic_side_effects.rs:344:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:321:10 + --> $DIR/arithmetic_side_effects.rs:345:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:322:10 + --> $DIR/arithmetic_side_effects.rs:346:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:323:10 + --> $DIR/arithmetic_side_effects.rs:347:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:324:10 + --> $DIR/arithmetic_side_effects.rs:348:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:325:10 + --> $DIR/arithmetic_side_effects.rs:349:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:326:10 + --> $DIR/arithmetic_side_effects.rs:350:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:327:10 + --> $DIR/arithmetic_side_effects.rs:351:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:328:10 + --> $DIR/arithmetic_side_effects.rs:352:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:329:10 + --> $DIR/arithmetic_side_effects.rs:353:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:330:10 + --> $DIR/arithmetic_side_effects.rs:354:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:331:15 + --> $DIR/arithmetic_side_effects.rs:355:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:332:15 + --> $DIR/arithmetic_side_effects.rs:356:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:333:15 + --> $DIR/arithmetic_side_effects.rs:357:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:334:15 + --> $DIR/arithmetic_side_effects.rs:358:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:335:15 + --> $DIR/arithmetic_side_effects.rs:359:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:336:15 + --> $DIR/arithmetic_side_effects.rs:360:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:337:15 + --> $DIR/arithmetic_side_effects.rs:361:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:338:15 + --> $DIR/arithmetic_side_effects.rs:362:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:339:15 + --> $DIR/arithmetic_side_effects.rs:363:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:340:15 + --> $DIR/arithmetic_side_effects.rs:364:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:341:15 + --> $DIR/arithmetic_side_effects.rs:365:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:342:15 + --> $DIR/arithmetic_side_effects.rs:366:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:343:15 + --> $DIR/arithmetic_side_effects.rs:367:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:344:15 + --> $DIR/arithmetic_side_effects.rs:368:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:345:15 + --> $DIR/arithmetic_side_effects.rs:369:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:346:15 + --> $DIR/arithmetic_side_effects.rs:370:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:347:15 + --> $DIR/arithmetic_side_effects.rs:371:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:348:15 + --> $DIR/arithmetic_side_effects.rs:372:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:349:15 + --> $DIR/arithmetic_side_effects.rs:373:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:352:10 + --> $DIR/arithmetic_side_effects.rs:374:15 + | +LL | _custom = _custom >> _custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:375:15 + | +LL | _custom = _custom >> &_custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:376:15 + | +LL | _custom = Custom << _custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:377:15 + | +LL | _custom = &Custom << _custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:380:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:353:10 + --> $DIR/arithmetic_side_effects.rs:381:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:354:15 + --> $DIR/arithmetic_side_effects.rs:382:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:355:15 + --> $DIR/arithmetic_side_effects.rs:383:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:364:5 + --> $DIR/arithmetic_side_effects.rs:392:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:365:5 + --> $DIR/arithmetic_side_effects.rs:393:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:367:5 + --> $DIR/arithmetic_side_effects.rs:395:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:368:5 + --> $DIR/arithmetic_side_effects.rs:396:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:369:5 - | -LL | i >> 1; - | ^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:370:5 - | -LL | i << 1; - | ^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:379:5 + --> $DIR/arithmetic_side_effects.rs:407:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:380:5 + --> $DIR/arithmetic_side_effects.rs:408:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:381:5 + --> $DIR/arithmetic_side_effects.rs:409:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:383:5 + --> $DIR/arithmetic_side_effects.rs:411:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:385:5 + --> $DIR/arithmetic_side_effects.rs:413:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:386:5 + --> $DIR/arithmetic_side_effects.rs:414:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:388:5 + --> $DIR/arithmetic_side_effects.rs:416:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:390:5 + --> $DIR/arithmetic_side_effects.rs:418:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:391:5 + --> $DIR/arithmetic_side_effects.rs:419:5 | LL | i %= var2; | ^^^^^^^^^ -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:392:5 - | -LL | i <<= 3; - | ^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:393:5 - | -LL | i >>= 2; - | ^^^^^^^ - -error: aborting due to 99 previous errors +error: aborting due to 107 previous errors diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed index 3cf380d2b95..579a63ea477 100644 --- a/tests/ui/async_yields_async.fixed +++ b/tests/ui/async_yields_async.fixed @@ -2,6 +2,7 @@ #![feature(lint_reasons)] #![feature(async_closure)] #![warn(clippy::async_yields_async)] +#![allow(clippy::redundant_async_block)] use core::future::Future; use core::pin::Pin; diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs index dd4131b60ab..5aec2fb50f6 100644 --- a/tests/ui/async_yields_async.rs +++ b/tests/ui/async_yields_async.rs @@ -2,6 +2,7 @@ #![feature(lint_reasons)] #![feature(async_closure)] #![warn(clippy::async_yields_async)] +#![allow(clippy::redundant_async_block)] use core::future::Future; use core::pin::Pin; diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index 22ce1c6f647..7f72534832b 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -1,5 +1,5 @@ error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:39:9 + --> $DIR/async_yields_async.rs:40:9 | LL | let _h = async { | _____________________- @@ -19,7 +19,7 @@ LL + }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:44:9 + --> $DIR/async_yields_async.rs:45:9 | LL | let _i = async { | ____________________- @@ -32,7 +32,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:50:9 + --> $DIR/async_yields_async.rs:51:9 | LL | let _j = async || { | ________________________- @@ -51,7 +51,7 @@ LL + }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:55:9 + --> $DIR/async_yields_async.rs:56:9 | LL | let _k = async || { | _______________________- @@ -64,7 +64,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:57:23 + --> $DIR/async_yields_async.rs:58:23 | LL | let _l = async || CustomFutureType; | ^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL | let _l = async || CustomFutureType; | help: consider awaiting this value: `CustomFutureType.await` error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:63:9 + --> $DIR/async_yields_async.rs:64:9 | LL | let _m = async || { | _______________________- diff --git a/tests/ui/collection_is_never_read.rs b/tests/ui/collection_is_never_read.rs new file mode 100644 index 00000000000..068a49486cf --- /dev/null +++ b/tests/ui/collection_is_never_read.rs @@ -0,0 +1,165 @@ +#![allow(unused)] +#![warn(clippy::collection_is_never_read)] + +use std::collections::{HashMap, HashSet}; + +fn main() {} + +fn not_a_collection() { + // TODO: Expand `collection_is_never_read` beyond collections? + let mut x = 10; // Ok + x += 1; +} + +fn no_access_at_all() { + // Other lints should catch this. + let x = vec![1, 2, 3]; // Ok +} + +fn write_without_read() { + // The main use case for `collection_is_never_read`. + let mut x = HashMap::new(); // WARNING + x.insert(1, 2); +} + +fn read_without_write() { + let mut x = vec![1, 2, 3]; // Ok + let _ = x.len(); +} + +fn write_and_read() { + let mut x = vec![1, 2, 3]; // Ok + x.push(4); + let _ = x.len(); +} + +fn write_after_read() { + // TODO: Warn here, but this requires more extensive data flow analysis. + let mut x = vec![1, 2, 3]; // Ok + let _ = x.len(); + x.push(4); // Pointless +} + +fn write_before_reassign() { + // TODO: Warn here, but this requires more extensive data flow analysis. + let mut x = HashMap::new(); // Ok + x.insert(1, 2); // Pointless + x = HashMap::new(); + let _ = x.len(); +} + +fn read_in_closure() { + let mut x = HashMap::new(); // Ok + x.insert(1, 2); + let _ = || { + let _ = x.len(); + }; +} + +fn write_in_closure() { + let mut x = vec![1, 2, 3]; // WARNING + let _ = || { + x.push(4); + }; +} + +fn read_in_format() { + let mut x = HashMap::new(); // Ok + x.insert(1, 2); + format!("{x:?}"); +} + +fn shadowing_1() { + let x = HashMap::::new(); // Ok + let _ = x.len(); + let mut x = HashMap::new(); // WARNING + x.insert(1, 2); +} + +fn shadowing_2() { + let mut x = HashMap::new(); // WARNING + x.insert(1, 2); + let x = HashMap::::new(); // Ok + let _ = x.len(); +} + +#[allow(clippy::let_unit_value)] +fn fake_read() { + let mut x = vec![1, 2, 3]; // Ok + x.reverse(); + // `collection_is_never_read` gets fooled, but other lints should catch this. + let _: () = x.clear(); +} + +fn assignment() { + let mut x = vec![1, 2, 3]; // WARNING + let y = vec![4, 5, 6]; // Ok + x = y; +} + +#[allow(clippy::self_assignment)] +fn self_assignment() { + let mut x = vec![1, 2, 3]; // WARNING + x = x; +} + +fn method_argument_but_not_target() { + struct MyStruct; + impl MyStruct { + fn my_method(&self, _argument: &[usize]) {} + } + let my_struct = MyStruct; + + let mut x = vec![1, 2, 3]; // Ok + x.reverse(); + my_struct.my_method(&x); +} + +fn insert_is_not_a_read() { + let mut x = HashSet::new(); // WARNING + x.insert(5); +} + +fn insert_is_a_read() { + let mut x = HashSet::new(); // Ok + if x.insert(5) { + println!("5 was inserted"); + } +} + +fn not_read_if_return_value_not_used() { + // `is_empty` does not modify the set, so it's a query. But since the return value is not used, the + // lint does not consider it a read here. + let x = vec![1, 2, 3]; // WARNING + x.is_empty(); +} + +fn extension_traits() { + trait VecExt { + fn method_with_side_effect(&self); + fn method_without_side_effect(&self); + } + + impl VecExt for Vec { + fn method_with_side_effect(&self) { + println!("my length: {}", self.len()); + } + fn method_without_side_effect(&self) {} + } + + let x = vec![1, 2, 3]; // Ok + x.method_with_side_effect(); + + let y = vec![1, 2, 3]; // Ok (false negative) + y.method_without_side_effect(); +} + +fn function_argument() { + #[allow(clippy::ptr_arg)] + fn foo(v: &Vec) -> usize { + v.len() + } + + let x = vec![1, 2, 3]; // Ok + foo(&x); +} diff --git a/tests/ui/collection_is_never_read.stderr b/tests/ui/collection_is_never_read.stderr new file mode 100644 index 00000000000..7654b74be3d --- /dev/null +++ b/tests/ui/collection_is_never_read.stderr @@ -0,0 +1,52 @@ +error: collection is never read + --> $DIR/collection_is_never_read.rs:21:5 + | +LL | let mut x = HashMap::new(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::collection-is-never-read` implied by `-D warnings` + +error: collection is never read + --> $DIR/collection_is_never_read.rs:60:5 + | +LL | let mut x = vec![1, 2, 3]; // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:75:5 + | +LL | let mut x = HashMap::new(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:80:5 + | +LL | let mut x = HashMap::new(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:95:5 + | +LL | let mut x = vec![1, 2, 3]; // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:102:5 + | +LL | let mut x = vec![1, 2, 3]; // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:119:5 + | +LL | let mut x = HashSet::new(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:133:5 + | +LL | let x = vec![1, 2, 3]; // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/crashes/ice-10148.rs b/tests/ui/crashes/ice-10148.rs new file mode 100644 index 00000000000..af33b10c693 --- /dev/null +++ b/tests/ui/crashes/ice-10148.rs @@ -0,0 +1,9 @@ +// aux-build:../../auxiliary/proc_macro_with_span.rs + +extern crate proc_macro_with_span; + +use proc_macro_with_span::with_span; + +fn main() { + println!(with_span!(""something "")); +} diff --git a/tests/ui/crashes/ice-10148.stderr b/tests/ui/crashes/ice-10148.stderr new file mode 100644 index 00000000000..f23e4433f95 --- /dev/null +++ b/tests/ui/crashes/ice-10148.stderr @@ -0,0 +1,12 @@ +error: empty string literal in `println!` + --> $DIR/ice-10148.rs:8:5 + | +LL | println!(with_span!(""something "")); + | ^^^^^^^^^^^^^^^^^^^^-----------^^^^^ + | | + | help: remove the empty string + | + = note: `-D clippy::println-empty-string` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-6179.rs b/tests/ui/crashes/ice-6179.rs index 4fe92d356c4..ce1895851e2 100644 --- a/tests/ui/crashes/ice-6179.rs +++ b/tests/ui/crashes/ice-6179.rs @@ -2,7 +2,7 @@ //! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details. #![warn(clippy::use_self)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_with_type_underscore)] struct Foo; diff --git a/tests/ui/crashes/ice-rust-107877.rs b/tests/ui/crashes/ice-rust-107877.rs new file mode 100644 index 00000000000..7f5bae60d55 --- /dev/null +++ b/tests/ui/crashes/ice-rust-107877.rs @@ -0,0 +1,17 @@ +#![allow(dead_code)] + +struct Foo; + +impl<'a> std::convert::TryFrom<&'a String> for Foo { + type Error = std::convert::Infallible; + + fn try_from(_: &'a String) -> Result { + Ok(Foo) + } +} + +fn find(_: impl std::convert::TryInto) {} + +fn main() { + find(&String::new()); +} diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index a370ccc7696..a9e5fd159af 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -9,7 +9,8 @@ clippy::unnecessary_operation, clippy::branches_sharing_code, clippy::match_single_binding, - clippy::let_unit_value + clippy::let_unit_value, + clippy::let_with_type_underscore )] #[macro_use] diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index 2476fe95141..085f8f452b2 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -9,7 +9,8 @@ clippy::unnecessary_operation, clippy::branches_sharing_code, clippy::match_single_binding, - clippy::let_unit_value + clippy::let_unit_value, + clippy::let_with_type_underscore )] #[macro_use] diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index 5df2f642388..44c6f1a9bea 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:21:17 + --> $DIR/default_numeric_fallback_f64.rs:22:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` @@ -7,139 +7,139 @@ LL | let x = 0.12; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:18 + --> $DIR/default_numeric_fallback_f64.rs:23:18 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:22 + --> $DIR/default_numeric_fallback_f64.rs:23:22 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:26 + --> $DIR/default_numeric_fallback_f64.rs:23:26 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:23:28 + --> $DIR/default_numeric_fallback_f64.rs:24:28 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:23:32 + --> $DIR/default_numeric_fallback_f64.rs:24:32 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:23:46 + --> $DIR/default_numeric_fallback_f64.rs:24:46 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:23:50 + --> $DIR/default_numeric_fallback_f64.rs:24:50 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `4.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:24:23 + --> $DIR/default_numeric_fallback_f64.rs:25:23 | LL | let x = match 1. { | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:25:18 + --> $DIR/default_numeric_fallback_f64.rs:26:18 | LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:44:21 + --> $DIR/default_numeric_fallback_f64.rs:45:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:52:21 + --> $DIR/default_numeric_fallback_f64.rs:53:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:58:21 + --> $DIR/default_numeric_fallback_f64.rs:59:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:66:21 + --> $DIR/default_numeric_fallback_f64.rs:67:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:78:9 + --> $DIR/default_numeric_fallback_f64.rs:79:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:84:27 + --> $DIR/default_numeric_fallback_f64.rs:85:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:88:29 + --> $DIR/default_numeric_fallback_f64.rs:89:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:102:21 + --> $DIR/default_numeric_fallback_f64.rs:103:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:105:32 + --> $DIR/default_numeric_fallback_f64.rs:106:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:123:28 + --> $DIR/default_numeric_fallback_f64.rs:124:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:126:36 + --> $DIR/default_numeric_fallback_f64.rs:127:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:144:24 + --> $DIR/default_numeric_fallback_f64.rs:145:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:164:23 + --> $DIR/default_numeric_fallback_f64.rs:165:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:171:21 + --> $DIR/default_numeric_fallback_f64.rs:172:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index 3f4994f0453..63ac4d5aeb6 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -9,7 +9,8 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::branches_sharing_code, - clippy::let_unit_value + clippy::let_unit_value, + clippy::let_with_type_underscore )] #[macro_use] diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 2df0e09787f..28e6eceb80e 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -9,7 +9,8 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::branches_sharing_code, - clippy::let_unit_value + clippy::let_unit_value, + clippy::let_with_type_underscore )] #[macro_use] diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index 6f219c3fc2b..dd91574d5b3 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:17 + --> $DIR/default_numeric_fallback_i32.rs:22:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,151 +7,151 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:18 + --> $DIR/default_numeric_fallback_i32.rs:23:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:21 + --> $DIR/default_numeric_fallback_i32.rs:23:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:24 + --> $DIR/default_numeric_fallback_i32.rs:23:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:23:28 + --> $DIR/default_numeric_fallback_i32.rs:24:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:23:31 + --> $DIR/default_numeric_fallback_i32.rs:24:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:23:44 + --> $DIR/default_numeric_fallback_i32.rs:24:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:23:47 + --> $DIR/default_numeric_fallback_i32.rs:24:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:24:23 + --> $DIR/default_numeric_fallback_i32.rs:25:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:25:13 + --> $DIR/default_numeric_fallback_i32.rs:26:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:25:18 + --> $DIR/default_numeric_fallback_i32.rs:26:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:26:18 + --> $DIR/default_numeric_fallback_i32.rs:27:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:45:21 + --> $DIR/default_numeric_fallback_i32.rs:46:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:53:21 + --> $DIR/default_numeric_fallback_i32.rs:54:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:59:21 + --> $DIR/default_numeric_fallback_i32.rs:60:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:67:21 + --> $DIR/default_numeric_fallback_i32.rs:68:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:79:9 + --> $DIR/default_numeric_fallback_i32.rs:80:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:85:27 + --> $DIR/default_numeric_fallback_i32.rs:86:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:89:29 + --> $DIR/default_numeric_fallback_i32.rs:90:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:103:21 + --> $DIR/default_numeric_fallback_i32.rs:104:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:106:32 + --> $DIR/default_numeric_fallback_i32.rs:107:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:124:28 + --> $DIR/default_numeric_fallback_i32.rs:125:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:127:36 + --> $DIR/default_numeric_fallback_i32.rs:128:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:145:24 + --> $DIR/default_numeric_fallback_i32.rs:146:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:165:23 + --> $DIR/default_numeric_fallback_i32.rs:166:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:172:21 + --> $DIR/default_numeric_fallback_i32.rs:173:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` diff --git a/tests/ui/derivable_impls.fixed b/tests/ui/derivable_impls.fixed index ee8456f5deb..89ec33a0d8f 100644 --- a/tests/ui/derivable_impls.fixed +++ b/tests/ui/derivable_impls.fixed @@ -231,4 +231,41 @@ impl Default for NonExhaustiveEnum { } } +// https://github.com/rust-lang/rust-clippy/issues/10396 + +#[derive(Default)] +struct DefaultType; + +struct GenericType { + t: T, +} + +impl Default for GenericType { + fn default() -> Self { + Self { t: Default::default() } + } +} + +struct InnerGenericType { + t: T, +} + +impl Default for InnerGenericType { + fn default() -> Self { + Self { t: Default::default() } + } +} + +struct OtherGenericType { + inner: InnerGenericType, +} + +impl Default for OtherGenericType { + fn default() -> Self { + Self { + inner: Default::default(), + } + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index 14af419bcad..def6e41162f 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -267,4 +267,41 @@ impl Default for NonExhaustiveEnum { } } +// https://github.com/rust-lang/rust-clippy/issues/10396 + +#[derive(Default)] +struct DefaultType; + +struct GenericType { + t: T, +} + +impl Default for GenericType { + fn default() -> Self { + Self { t: Default::default() } + } +} + +struct InnerGenericType { + t: T, +} + +impl Default for InnerGenericType { + fn default() -> Self { + Self { t: Default::default() } + } +} + +struct OtherGenericType { + inner: InnerGenericType, +} + +impl Default for OtherGenericType { + fn default() -> Self { + Self { + inner: Default::default(), + } + } +} + fn main() {} diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index cd2f70ee8b0..beedf2c1db2 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -1,5 +1,4 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs #![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, @@ -10,8 +9,6 @@ clippy::uninlined_format_args )] -extern crate proc_macro_with_span; - struct Foo(pub String); macro_rules! foo { @@ -90,7 +87,4 @@ fn main() { let _ = abc.to_string(); let xx = "xx"; let _ = xx.to_string(); - - // Issue #10148 - println!(proc_macro_with_span::with_span!(""something "")); } diff --git a/tests/ui/format.rs b/tests/ui/format.rs index c22345a79d4..e805f181889 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -1,5 +1,4 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs #![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, @@ -10,8 +9,6 @@ clippy::uninlined_format_args )] -extern crate proc_macro_with_span; - struct Foo(pub String); macro_rules! foo { @@ -92,7 +89,4 @@ fn main() { let _ = format!("{abc}"); let xx = "xx"; let _ = format!("{xx}"); - - // Issue #10148 - println!(proc_macro_with_span::with_span!(""something "")); } diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index a0e5d5c8ad2..0ef0ac655d3 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -1,5 +1,5 @@ error: useless use of `format!` - --> $DIR/format.rs:22:5 + --> $DIR/format.rs:19:5 | LL | format!("foo"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` @@ -7,19 +7,19 @@ LL | format!("foo"); = note: `-D clippy::useless-format` implied by `-D warnings` error: useless use of `format!` - --> $DIR/format.rs:23:5 + --> $DIR/format.rs:20:5 | LL | format!("{{}}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:24:5 + --> $DIR/format.rs:21:5 | LL | format!("{{}} abc {{}}"); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:25:5 + --> $DIR/format.rs:22:5 | LL | / format!( LL | | r##"foo {{}} @@ -34,67 +34,67 @@ LL ~ " bar"##.to_string(); | error: useless use of `format!` - --> $DIR/format.rs:30:13 + --> $DIR/format.rs:27:13 | LL | let _ = format!(""); | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()` error: useless use of `format!` - --> $DIR/format.rs:32:5 + --> $DIR/format.rs:29:5 | LL | format!("{}", "foo"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> $DIR/format.rs:40:5 + --> $DIR/format.rs:37:5 | LL | format!("{}", arg); | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> $DIR/format.rs:70:5 + --> $DIR/format.rs:67:5 | LL | format!("{}", 42.to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` error: useless use of `format!` - --> $DIR/format.rs:72:5 + --> $DIR/format.rs:69:5 | LL | format!("{}", x.display().to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` error: useless use of `format!` - --> $DIR/format.rs:76:18 + --> $DIR/format.rs:73:18 | LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` error: useless use of `format!` - --> $DIR/format.rs:80:22 + --> $DIR/format.rs:77:22 | LL | let _s: String = format!("{}", &*v.join("/n")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` error: useless use of `format!` - --> $DIR/format.rs:86:13 + --> $DIR/format.rs:83:13 | LL | let _ = format!("{x}"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:88:13 + --> $DIR/format.rs:85:13 | LL | let _ = format!("{y}", y = x); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:92:13 + --> $DIR/format.rs:89:13 | LL | let _ = format!("{abc}"); | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` error: useless use of `format!` - --> $DIR/format.rs:94:13 + --> $DIR/format.rs:91:13 | LL | let _ = format!("{xx}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()` diff --git a/tests/ui/impl_trait_in_params.stderr b/tests/ui/impl_trait_in_params.stderr index acfcc21445e..80383743525 100644 --- a/tests/ui/impl_trait_in_params.stderr +++ b/tests/ui/impl_trait_in_params.stderr @@ -5,7 +5,7 @@ LL | pub fn a(_: impl Trait) {} | ^^^^^^^^^^ | = note: `-D clippy::impl-trait-in-params` implied by `-D warnings` -help: add a type paremeter +help: add a type parameter | LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {} | +++++++++++++++++++++++++++++++ @@ -16,7 +16,7 @@ error: '`impl Trait` used as a function parameter' LL | pub fn c(_: C, _: impl Trait) {} | ^^^^^^^^^^ | -help: add a type paremeter +help: add a type parameter | LL | pub fn c(_: C, _: impl Trait) {} | +++++++++++++++++++++++++++++++ diff --git a/tests/ui/implicit_clone.fixed b/tests/ui/implicit_clone.fixed index 51b1afbe5ac..8ccc3da7b47 100644 --- a/tests/ui/implicit_clone.fixed +++ b/tests/ui/implicit_clone.fixed @@ -87,7 +87,7 @@ fn main() { let kitten = Kitten {}; let _ = kitten.clone(); let _ = own_same_from_ref(&kitten); - // this shouln't lint + // this shouldn't lint let _ = kitten.to_vec(); // we expect no lints for this diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index 8a9027433d9..59333312607 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -87,7 +87,7 @@ fn main() { let kitten = Kitten {}; let _ = kitten.to_owned(); let _ = own_same_from_ref(&kitten); - // this shouln't lint + // this shouldn't lint let _ = kitten.to_vec(); // we expect no lints for this diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index b5dec6c46bd..52aabefaed2 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -282,6 +282,87 @@ impl AsyncLen { } } +// issue #7232 +pub struct AsyncLenWithoutIsEmpty; +impl AsyncLenWithoutIsEmpty { + pub async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> usize { + usize::from(!self.async_task().await) + } +} + +// issue #7232 +pub struct AsyncOptionLenWithoutIsEmpty; +impl AsyncOptionLenWithoutIsEmpty { + async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> Option { + None + } +} + +// issue #7232 +pub struct AsyncOptionLenNonIntegral; +impl AsyncOptionLenNonIntegral { + // don't lint + pub async fn len(&self) -> Option { + None + } +} + +// issue #7232 +pub struct AsyncResultLenWithoutIsEmpty; +impl AsyncResultLenWithoutIsEmpty { + async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> Result { + Err(()) + } +} + +// issue #7232 +pub struct AsyncOptionLen; +impl AsyncOptionLen { + async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> Result { + Err(()) + } + + pub async fn is_empty(&self) -> bool { + true + } +} + +pub struct AsyncLenSyncIsEmpty; +impl AsyncLenSyncIsEmpty { + pub async fn len(&self) -> u32 { + 0 + } + + pub fn is_empty(&self) -> bool { + true + } +} + +// issue #9520 +pub struct NonStandardLen; +impl NonStandardLen { + // don't lint + pub fn len(&self, something: usize) -> usize { + something + } +} + // issue #9520 pub struct NonStandardLenAndIsEmptySignature; impl NonStandardLenAndIsEmptySignature { @@ -328,4 +409,15 @@ impl NonStandardSignatureWithGenerics { } } +pub struct DifferingErrors; +impl DifferingErrors { + pub fn len(&self) -> Result { + Ok(0) + } + + pub fn is_empty(&self) -> Result { + Ok(true) + } +} + fn main() {} diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index 8e890e2e259..1bce1734b81 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -119,5 +119,23 @@ LL | pub fn len(&self) -> Result { | = help: use a custom `Error` type instead -error: aborting due to 12 previous errors +error: struct `AsyncLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:292:5 + | +LL | pub async fn len(&self) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: struct `AsyncOptionLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:304:5 + | +LL | pub async fn len(&self) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:325:5 + | +LL | pub async fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 6343cff0f7f..76ff0645f41 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -175,3 +175,7 @@ fn attributes() { #[expect(clippy::let_unit_value)] let _ = f(); } + +async fn issue10433() { + let _pending: () = std::future::pending().await; +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index c9bb2849f5c..895ccfe366a 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -175,3 +175,7 @@ fn attributes() { #[expect(clippy::let_unit_value)] let _ = f(); } + +async fn issue10433() { + let _pending: () = std::future::pending().await; +} diff --git a/tests/ui/let_with_type_underscore.rs b/tests/ui/let_with_type_underscore.rs new file mode 100644 index 00000000000..175718b94c8 --- /dev/null +++ b/tests/ui/let_with_type_underscore.rs @@ -0,0 +1,19 @@ +#![allow(unused)] +#![warn(clippy::let_with_type_underscore)] +#![allow(clippy::let_unit_value)] + +fn func() -> &'static str { + "" +} + +fn main() { + // Will lint + let x: _ = 1; + let _: _ = 2; + let x: _ = func(); + + let x = 1; // Will not lint, Rust inferres this to an integer before Clippy + let x = func(); + let x: Vec<_> = Vec::::new(); + let x: [_; 1] = [1]; +} diff --git a/tests/ui/let_with_type_underscore.stderr b/tests/ui/let_with_type_underscore.stderr new file mode 100644 index 00000000000..16bf83c708f --- /dev/null +++ b/tests/ui/let_with_type_underscore.stderr @@ -0,0 +1,39 @@ +error: variable declared with type underscore + --> $DIR/let_with_type_underscore.rs:11:5 + | +LL | let x: _ = 1; + | ^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + --> $DIR/let_with_type_underscore.rs:11:10 + | +LL | let x: _ = 1; + | ^^^ + = note: `-D clippy::let-with-type-underscore` implied by `-D warnings` + +error: variable declared with type underscore + --> $DIR/let_with_type_underscore.rs:12:5 + | +LL | let _: _ = 2; + | ^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + --> $DIR/let_with_type_underscore.rs:12:10 + | +LL | let _: _ = 2; + | ^^^ + +error: variable declared with type underscore + --> $DIR/let_with_type_underscore.rs:13:5 + | +LL | let x: _ = func(); + | ^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + --> $DIR/let_with_type_underscore.rs:13:10 + | +LL | let x: _ = func(); + | ^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index 4cdc0546a74..6916a284a20 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -2,6 +2,7 @@ // aux-build:macro_rules.rs #![warn(clippy::manual_rem_euclid)] +#![allow(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 58a9e20f38b..412dbddb426 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -2,6 +2,7 @@ // aux-build:macro_rules.rs #![warn(clippy::manual_rem_euclid)] +#![allow(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index e3122a588b6..6d06654638b 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:19:18 + --> $DIR/manual_rem_euclid.rs:20:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:21:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:22:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:23:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:22 + --> $DIR/manual_rem_euclid.rs:24:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:12:22 + --> $DIR/manual_rem_euclid.rs:13:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -42,25 +42,25 @@ LL | internal_rem_euclid!(); = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:49:5 + --> $DIR/manual_rem_euclid.rs:50:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:54:5 + --> $DIR/manual_rem_euclid.rs:55:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:66:18 + --> $DIR/manual_rem_euclid.rs:67:18 | LL | let _: i32 = ((x % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:79:18 + --> $DIR/manual_rem_euclid.rs:80:18 | LL | let _: i32 = ((x % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` diff --git a/tests/ui/match_result_ok.fixed b/tests/ui/match_result_ok.fixed index 8b91b9854a0..10ae1ee5245 100644 --- a/tests/ui/match_result_ok.fixed +++ b/tests/ui/match_result_ok.fixed @@ -16,7 +16,7 @@ fn str_to_int_ok(x: &str) -> i32 { #[rustfmt::skip] fn strange_some_no_else(x: &str) -> i32 { { - if let Ok(y) = x . parse() { + if let Ok(y) = x . parse() { return y; }; 0 diff --git a/tests/ui/match_result_ok.stderr b/tests/ui/match_result_ok.stderr index 98a95705ca5..cbdc56aa28c 100644 --- a/tests/ui/match_result_ok.stderr +++ b/tests/ui/match_result_ok.stderr @@ -18,7 +18,7 @@ LL | if let Some(y) = x . parse() . ok () { | help: consider matching on `Ok(y)` and removing the call to `ok` instead | -LL | if let Ok(y) = x . parse() { +LL | if let Ok(y) = x . parse() { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant diff --git a/tests/ui/missing_assert_message.rs b/tests/ui/missing_assert_message.rs new file mode 100644 index 00000000000..89404ca8827 --- /dev/null +++ b/tests/ui/missing_assert_message.rs @@ -0,0 +1,84 @@ +#![allow(unused)] +#![warn(clippy::missing_assert_message)] + +macro_rules! bar { + ($( $x:expr ),*) => { + foo() + }; +} + +fn main() {} + +// Should trigger warning +fn asserts_without_message() { + assert!(foo()); + assert_eq!(foo(), foo()); + assert_ne!(foo(), foo()); + debug_assert!(foo()); + debug_assert_eq!(foo(), foo()); + debug_assert_ne!(foo(), foo()); +} + +// Should trigger warning +fn asserts_without_message_but_with_macro_calls() { + assert!(bar!(true)); + assert!(bar!(true, false)); + assert_eq!(bar!(true), foo()); + assert_ne!(bar!(true, true), bar!(true)); +} + +// Should trigger warning +fn asserts_with_trailing_commas() { + assert!(foo(),); + assert_eq!(foo(), foo(),); + assert_ne!(foo(), foo(),); + debug_assert!(foo(),); + debug_assert_eq!(foo(), foo(),); + debug_assert_ne!(foo(), foo(),); +} + +// Should not trigger warning +fn asserts_with_message_and_with_macro_calls() { + assert!(bar!(true), "msg"); + assert!(bar!(true, false), "msg"); + assert_eq!(bar!(true), foo(), "msg"); + assert_ne!(bar!(true, true), bar!(true), "msg"); +} + +// Should not trigger warning +fn asserts_with_message() { + assert!(foo(), "msg"); + assert_eq!(foo(), foo(), "msg"); + assert_ne!(foo(), foo(), "msg"); + debug_assert!(foo(), "msg"); + debug_assert_eq!(foo(), foo(), "msg"); + debug_assert_ne!(foo(), foo(), "msg"); +} + +// Should not trigger warning +#[test] +fn asserts_without_message_but_inside_a_test_function() { + assert!(foo()); + assert_eq!(foo(), foo()); + assert_ne!(foo(), foo()); + debug_assert!(foo()); + debug_assert_eq!(foo(), foo()); + debug_assert_ne!(foo(), foo()); +} + +// Should not trigger warning +#[cfg(test)] +mod tests { + fn asserts_without_message_but_inside_a_test_module() { + assert!(foo()); + assert_eq!(foo(), foo()); + assert_ne!(foo(), foo()); + debug_assert!(foo()); + debug_assert_eq!(foo(), foo()); + debug_assert_ne!(foo(), foo()); + } +} + +fn foo() -> bool { + true +} diff --git a/tests/ui/missing_assert_message.stderr b/tests/ui/missing_assert_message.stderr new file mode 100644 index 00000000000..ecd03801277 --- /dev/null +++ b/tests/ui/missing_assert_message.stderr @@ -0,0 +1,131 @@ +error: assert without any message + --> $DIR/missing_assert_message.rs:14:5 + | +LL | assert!(foo()); + | ^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + = note: `-D clippy::missing-assert-message` implied by `-D warnings` + +error: assert without any message + --> $DIR/missing_assert_message.rs:15:5 + | +LL | assert_eq!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:16:5 + | +LL | assert_ne!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:17:5 + | +LL | debug_assert!(foo()); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:18:5 + | +LL | debug_assert_eq!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:19:5 + | +LL | debug_assert_ne!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:24:5 + | +LL | assert!(bar!(true)); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:25:5 + | +LL | assert!(bar!(true, false)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:26:5 + | +LL | assert_eq!(bar!(true), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:27:5 + | +LL | assert_ne!(bar!(true, true), bar!(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:32:5 + | +LL | assert!(foo(),); + | ^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:33:5 + | +LL | assert_eq!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:34:5 + | +LL | assert_ne!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:35:5 + | +LL | debug_assert!(foo(),); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:36:5 + | +LL | debug_assert_eq!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:37:5 + | +LL | debug_assert_ne!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: aborting due to 16 previous errors + diff --git a/tests/ui/missing_doc.stderr b/tests/ui/missing_doc.stderr index d3bef28bf64..4e8a49bf1cd 100644 --- a/tests/ui/missing_doc.stderr +++ b/tests/ui/missing_doc.stderr @@ -6,30 +6,12 @@ LL | type Typedef = String; | = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` -error: missing documentation for a type alias - --> $DIR/missing_doc.rs:17:1 - | -LL | pub type PubTypedef = String; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a module --> $DIR/missing_doc.rs:19:1 | LL | mod module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a module - --> $DIR/missing_doc.rs:20:1 - | -LL | pub mod pub_module_no_dox {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a function - --> $DIR/missing_doc.rs:24:1 - | -LL | pub fn foo2() {} - | ^^^^^^^^^^^^^^^^ - error: missing documentation for a function --> $DIR/missing_doc.rs:25:1 | @@ -69,50 +51,18 @@ error: missing documentation for a variant LL | BarB, | ^^^^ -error: missing documentation for an enum - --> $DIR/missing_doc.rs:44:1 - | -LL | / pub enum PubBaz { -LL | | PubBazA { a: isize }, -LL | | } - | |_^ - -error: missing documentation for a variant - --> $DIR/missing_doc.rs:45:5 - | -LL | PubBazA { a: isize }, - | ^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a struct field - --> $DIR/missing_doc.rs:45:15 - | -LL | PubBazA { a: isize }, - | ^^^^^^^^ - error: missing documentation for a constant --> $DIR/missing_doc.rs:65:1 | LL | const FOO: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a constant - --> $DIR/missing_doc.rs:72:1 - | -LL | pub const FOO4: u32 = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a static --> $DIR/missing_doc.rs:74:1 | LL | static BAR: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a static - --> $DIR/missing_doc.rs:81:1 - | -LL | pub static BAR4: u32 = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a module --> $DIR/missing_doc.rs:83:1 | @@ -125,35 +75,17 @@ LL | | } LL | | } | |_^ -error: missing documentation for a function - --> $DIR/missing_doc.rs:86:5 - | -LL | pub fn undocumented1() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a function - --> $DIR/missing_doc.rs:87:5 - | -LL | pub fn undocumented2() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a function --> $DIR/missing_doc.rs:88:5 | LL | fn undocumented3() {} | ^^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a function - --> $DIR/missing_doc.rs:93:9 - | -LL | pub fn also_undocumented1() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a function --> $DIR/missing_doc.rs:94:9 | LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/missing_doc_impl.stderr b/tests/ui/missing_doc_impl.stderr index b410f56e167..111d6546966 100644 --- a/tests/ui/missing_doc_impl.stderr +++ b/tests/ui/missing_doc_impl.stderr @@ -21,60 +21,12 @@ error: missing documentation for a struct field LL | b: isize, | ^^^^^^^^ -error: missing documentation for a struct - --> $DIR/missing_doc_impl.rs:18:1 - | -LL | / pub struct PubFoo { -LL | | pub a: isize, -LL | | b: isize, -LL | | } - | |_^ - -error: missing documentation for a struct field - --> $DIR/missing_doc_impl.rs:19:5 - | -LL | pub a: isize, - | ^^^^^^^^^^^^ - error: missing documentation for a struct field --> $DIR/missing_doc_impl.rs:20:5 | LL | b: isize, | ^^^^^^^^ -error: missing documentation for a trait - --> $DIR/missing_doc_impl.rs:43:1 - | -LL | / pub trait C { -LL | | fn foo(&self); -LL | | fn foo_with_impl(&self) {} -LL | | } - | |_^ - -error: missing documentation for a method - --> $DIR/missing_doc_impl.rs:44:5 - | -LL | fn foo(&self); - | ^^^^^^^^^^^^^^ - -error: missing documentation for a method - --> $DIR/missing_doc_impl.rs:45:5 - | -LL | fn foo_with_impl(&self) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for an associated type - --> $DIR/missing_doc_impl.rs:55:5 - | -LL | type AssociatedType; - | ^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for an associated type - --> $DIR/missing_doc_impl.rs:56:5 - | -LL | type AssociatedTypeDef = Self; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for an associated function --> $DIR/missing_doc_impl.rs:67:5 | @@ -89,12 +41,6 @@ error: missing documentation for an associated function LL | fn bar() {} | ^^^^^^^^^^^ -error: missing documentation for an associated function - --> $DIR/missing_doc_impl.rs:74:5 - | -LL | pub fn foo() {} - | ^^^^^^^^^^^^^^^ - error: missing documentation for an associated function --> $DIR/missing_doc_impl.rs:78:5 | @@ -103,5 +49,5 @@ LL | | 1 LL | | } | |_____^ -error: aborting due to 15 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 4511bc99c3c..5073685c9f0 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -116,4 +116,32 @@ fn issue10259() { unsafe_macro!(); } +fn _fn_ptr(x: unsafe fn()) { + unsafe { + x(); + x(); + } +} + +fn _assoc_const() { + trait X { + const X: unsafe fn(); + } + fn _f() { + unsafe { + T::X(); + T::X(); + } + } +} + +fn _field_fn_ptr(x: unsafe fn()) { + struct X(unsafe fn()); + let x = X(x); + unsafe { + x.0(); + x.0(); + } +} + fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index 303aeb7aee0..e0c1d3801f7 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -125,5 +125,65 @@ note: raw pointer dereference occurs here LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:120:5 + | +LL | / unsafe { +LL | | x(); +LL | | x(); +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:121:9 + | +LL | x(); + | ^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:122:9 + | +LL | x(); + | ^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:131:9 + | +LL | / unsafe { +LL | | T::X(); +LL | | T::X(); +LL | | } + | |_________^ + | +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:132:13 + | +LL | T::X(); + | ^^^^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:133:13 + | +LL | T::X(); + | ^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:141:5 + | +LL | / unsafe { +LL | | x.0(); +LL | | x.0(); +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:142:9 + | +LL | x.0(); + | ^^^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:143:9 + | +LL | x.0(); + | ^^^^^ + +error: aborting due to 8 previous errors diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index beec42f08bb..a2a30c8b931 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -406,7 +406,7 @@ mod issue10041 { struct Bomb; impl Bomb { - // Hidden default generic paramter. + // Hidden default generic parameter. pub fn new() -> impl PartialOrd { 0i32 } diff --git a/tests/ui/redundant_async_block.fixed b/tests/ui/redundant_async_block.fixed new file mode 100644 index 00000000000..5f9931df45e --- /dev/null +++ b/tests/ui/redundant_async_block.fixed @@ -0,0 +1,64 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::redundant_async_block)] + +async fn func1(n: usize) -> usize { + n + 1 +} + +async fn func2() -> String { + let s = String::from("some string"); + let f = async { (*s).to_owned() }; + let x = f; + x.await +} + +macro_rules! await_in_macro { + ($e:expr) => { + std::convert::identity($e).await + }; +} + +async fn func3(n: usize) -> usize { + // Do not lint (suggestion would be `std::convert::identity(func1(n))` + // which copies code from inside the macro) + async move { await_in_macro!(func1(n)) }.await +} + +// This macro should never be linted as `$e` might contain `.await` +macro_rules! async_await_parameter_in_macro { + ($e:expr) => { + async { $e.await } + }; +} + +// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not +// contain code coming from the parameters +macro_rules! async_await_in_macro { + ($f:expr) => { + ($f)(async { func2().await }) + }; +} + +fn main() { + let fut1 = async { 17 }; + let fut2 = fut1; + + let fut1 = async { 25 }; + let fut2 = fut1; + + let fut = async { 42 }; + + // Do not lint: not a single expression + let fut = async { + func1(10).await; + func2().await + }; + + // Do not lint: expression contains `.await` + let fut = async { func1(func2().await.len()).await }; + + let fut = async_await_parameter_in_macro!(func2()); + let fut = async_await_in_macro!(std::convert::identity); +} diff --git a/tests/ui/redundant_async_block.rs b/tests/ui/redundant_async_block.rs new file mode 100644 index 00000000000..de3c9970c65 --- /dev/null +++ b/tests/ui/redundant_async_block.rs @@ -0,0 +1,64 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::redundant_async_block)] + +async fn func1(n: usize) -> usize { + n + 1 +} + +async fn func2() -> String { + let s = String::from("some string"); + let f = async { (*s).to_owned() }; + let x = async { f.await }; + x.await +} + +macro_rules! await_in_macro { + ($e:expr) => { + std::convert::identity($e).await + }; +} + +async fn func3(n: usize) -> usize { + // Do not lint (suggestion would be `std::convert::identity(func1(n))` + // which copies code from inside the macro) + async move { await_in_macro!(func1(n)) }.await +} + +// This macro should never be linted as `$e` might contain `.await` +macro_rules! async_await_parameter_in_macro { + ($e:expr) => { + async { $e.await } + }; +} + +// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not +// contain code coming from the parameters +macro_rules! async_await_in_macro { + ($f:expr) => { + ($f)(async { func2().await }) + }; +} + +fn main() { + let fut1 = async { 17 }; + let fut2 = async { fut1.await }; + + let fut1 = async { 25 }; + let fut2 = async move { fut1.await }; + + let fut = async { async { 42 }.await }; + + // Do not lint: not a single expression + let fut = async { + func1(10).await; + func2().await + }; + + // Do not lint: expression contains `.await` + let fut = async { func1(func2().await.len()).await }; + + let fut = async_await_parameter_in_macro!(func2()); + let fut = async_await_in_macro!(std::convert::identity); +} diff --git a/tests/ui/redundant_async_block.stderr b/tests/ui/redundant_async_block.stderr new file mode 100644 index 00000000000..b16d96dce84 --- /dev/null +++ b/tests/ui/redundant_async_block.stderr @@ -0,0 +1,28 @@ +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:13:13 + | +LL | let x = async { f.await }; + | ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f` + | + = note: `-D clippy::redundant-async-block` implied by `-D warnings` + +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:46:16 + | +LL | let fut2 = async { fut1.await }; + | ^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1` + +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:49:16 + | +LL | let fut2 = async move { fut1.await }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1` + +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:51:15 + | +LL | let fut = async { async { 42 }.await }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index c0e49ff4caa..b987fd2ce6f 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed @@ -2,6 +2,7 @@ #![feature(async_closure)] #![warn(clippy::redundant_closure_call)] +#![allow(clippy::redundant_async_block)] #![allow(unused)] async fn something() -> u32 { diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index 9e6e54348a8..633a2979d5d 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -2,6 +2,7 @@ #![feature(async_closure)] #![warn(clippy::redundant_closure_call)] +#![allow(clippy::redundant_async_block)] #![allow(unused)] async fn something() -> u32 { diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index d71bcba2a82..8a1f0771659 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,5 +1,5 @@ error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:16:13 + --> $DIR/redundant_closure_call_fixable.rs:17:13 | LL | let a = (|| 42)(); | ^^^^^^^^^ help: try doing something like: `42` @@ -7,7 +7,7 @@ LL | let a = (|| 42)(); = note: `-D clippy::redundant-closure-call` implied by `-D warnings` error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:17:13 + --> $DIR/redundant_closure_call_fixable.rs:18:13 | LL | let b = (async || { | _____________^ @@ -27,7 +27,7 @@ LL ~ }; | error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:22:13 + --> $DIR/redundant_closure_call_fixable.rs:23:13 | LL | let c = (|| { | _____________^ @@ -47,13 +47,13 @@ LL ~ }; | error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:27:13 + --> $DIR/redundant_closure_call_fixable.rs:28:13 | LL | let d = (async || something().await)(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }` error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:36:13 + --> $DIR/redundant_closure_call_fixable.rs:37:13 | LL | (|| m!())() | ^^^^^^^^^^^ help: try doing something like: `m!()` @@ -64,7 +64,7 @@ LL | m2!(); = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:31:13 + --> $DIR/redundant_closure_call_fixable.rs:32:13 | LL | (|| 0)() | ^^^^^^^^ help: try doing something like: `0` diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index fa89706a815..04008c0d9b3 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -65,19 +65,19 @@ fn xor_swap_locals() { // This is an xor-based swap of local variables. let mut a = 0; let mut b = 1; - std::mem::swap(&mut a, &mut b) + std::mem::swap(&mut a, &mut b); } fn xor_field_swap() { // This is an xor-based swap of fields in a struct. let mut bar = Bar { a: 0, b: 1 }; - std::mem::swap(&mut bar.a, &mut bar.b) + std::mem::swap(&mut bar.a, &mut bar.b); } fn xor_slice_swap() { // This is an xor-based swap of a slice let foo = &mut [1, 2]; - foo.swap(0, 1) + foo.swap(0, 1); } fn xor_no_swap() { diff --git a/tests/ui/swap.stderr b/tests/ui/swap.stderr index f0acbfe253f..825c9261e19 100644 --- a/tests/ui/swap.stderr +++ b/tests/ui/swap.stderr @@ -4,7 +4,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually LL | / let temp = bar.a; LL | | bar.a = bar.b; LL | | bar.b = temp; - | |________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)` + | |_________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);` | = note: or maybe you should use `std::mem::replace`? = note: `-D clippy::manual-swap` implied by `-D warnings` @@ -15,7 +15,7 @@ error: this looks like you are swapping elements of `foo` manually LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; LL | | foo[1] = temp; - | |_________________^ help: try: `foo.swap(0, 1)` + | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually --> $DIR/swap.rs:46:5 @@ -23,7 +23,7 @@ error: this looks like you are swapping elements of `foo` manually LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; LL | | foo[1] = temp; - | |_________________^ help: try: `foo.swap(0, 1)` + | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually --> $DIR/swap.rs:65:5 @@ -31,7 +31,7 @@ error: this looks like you are swapping elements of `foo` manually LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; LL | | foo[1] = temp; - | |_________________^ help: try: `foo.swap(0, 1)` + | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `a` and `b` manually --> $DIR/swap.rs:76:5 @@ -39,7 +39,7 @@ error: this looks like you are swapping `a` and `b` manually LL | / a ^= b; LL | | b ^= a; LL | | a ^= b; - | |___________^ help: try: `std::mem::swap(&mut a, &mut b)` + | |___________^ help: try: `std::mem::swap(&mut a, &mut b);` error: this looks like you are swapping `bar.a` and `bar.b` manually --> $DIR/swap.rs:84:5 @@ -47,7 +47,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually LL | / bar.a ^= bar.b; LL | | bar.b ^= bar.a; LL | | bar.a ^= bar.b; - | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)` + | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);` error: this looks like you are swapping elements of `foo` manually --> $DIR/swap.rs:92:5 @@ -55,7 +55,7 @@ error: this looks like you are swapping elements of `foo` manually LL | / foo[0] ^= foo[1]; LL | | foo[1] ^= foo[0]; LL | | foo[0] ^= foo[1]; - | |_____________________^ help: try: `foo.swap(0, 1)` + | |_____________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually --> $DIR/swap.rs:121:5 @@ -63,7 +63,7 @@ error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually LL | / let temp = foo[0][1]; LL | | foo[0][1] = bar[1][0]; LL | | bar[1][0] = temp; - | |____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0])` + | |_____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0]);` | = note: or maybe you should use `std::mem::replace`? @@ -74,7 +74,7 @@ LL | ; let t = a; | _______^ LL | | a = b; LL | | b = t; - | |_________^ help: try: `std::mem::swap(&mut a, &mut b)` + | |__________^ help: try: `std::mem::swap(&mut a, &mut b);` | = note: or maybe you should use `std::mem::replace`? @@ -85,7 +85,7 @@ LL | ; let t = c.0; | _______^ LL | | c.0 = a; LL | | a = t; - | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)` + | |__________^ help: try: `std::mem::swap(&mut c.0, &mut a);` | = note: or maybe you should use `std::mem::replace`? @@ -95,7 +95,7 @@ error: this looks like you are swapping `b` and `a` manually LL | / let t = b; LL | | b = a; LL | | a = t; - | |_________^ help: try: `std::mem::swap(&mut b, &mut a)` + | |__________^ help: try: `std::mem::swap(&mut b, &mut a);` | = note: or maybe you should use `std::mem::replace`? @@ -151,7 +151,7 @@ error: this looks like you are swapping `s.0.x` and `s.0.y` manually LL | / let t = s.0.x; LL | | s.0.x = s.0.y; LL | | s.0.y = t; - | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)` + | |______________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y);` | = note: or maybe you should use `std::mem::replace`? diff --git a/tests/ui/trailing_empty_array.rs b/tests/ui/trailing_empty_array.rs index c39b0bcaf22..8e3749eef35 100644 --- a/tests/ui/trailing_empty_array.rs +++ b/tests/ui/trailing_empty_array.rs @@ -155,7 +155,6 @@ struct TupleStructReprC(i32, [usize; 0]); type NamedTuple = (i32, [usize; 0]); -#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995) struct ConstParamZeroDefault { field: i32, last: [usize; N], @@ -166,7 +165,6 @@ struct ConstParamNoDefault { last: [usize; N], } -#[rustfmt::skip] struct ConstParamNonZeroDefault { field: i32, last: [usize; N], diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 0a6166571eb..3ac6217312a 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -647,3 +647,13 @@ fn msrv_1_37() { } } } + +mod issue_10371 { + struct Val {} + + impl From> for i32 { + fn from(_: Val) -> Self { + todo!() + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 39c2b431f7f..9dc5d1e3f9b 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -647,3 +647,13 @@ fn msrv_1_37() { } } } + +mod issue_10371 { + struct Val {} + + impl From> for i32 { + fn from(_: Val) -> Self { + todo!() + } + } +} From c34d04a3ddce97a32c2024eb7cd1136e9d7fec95 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 10 Mar 2023 11:28:10 +0100 Subject: [PATCH 08/47] Update changelog for beta-accepted labels --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0abe234fc8f..da7042f4440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -152,6 +152,8 @@ Current stable, released 2023-03-09 * `SYSROOT` and `--sysroot` can now be set at the same time [#10149](https://github.com/rust-lang/rust-clippy/pull/10149) +* Fix error when providing an `array-size-threshold` in `clippy.toml` + [#10423](https://github.com/rust-lang/rust-clippy/pull/10423) ## Rust 1.67 @@ -186,8 +188,6 @@ Released 2023-01-26 ### Moves and Deprecations -* Moved [`uninlined_format_args`] to `style` (Now warn-by-default) - [#9865](https://github.com/rust-lang/rust-clippy/pull/9865) * Moved [`needless_collect`] to `nursery` (Now allow-by-default) [#9705](https://github.com/rust-lang/rust-clippy/pull/9705) * Moved [`or_fun_call`] to `nursery` (Now allow-by-default) @@ -423,7 +423,7 @@ Released 2022-12-15 [#9490](https://github.com/rust-lang/rust-clippy/pull/9490) * [`almost_complete_letter_range`]: No longer lints in external macros [#9467](https://github.com/rust-lang/rust-clippy/pull/9467) -* [`drop_copy`]: No longer lints on idiomatic cases in match arms +* [`drop_copy`]: No longer lints on idiomatic cases in match arms [#9491](https://github.com/rust-lang/rust-clippy/pull/9491) * [`question_mark`]: No longer lints in const context [#9487](https://github.com/rust-lang/rust-clippy/pull/9487) From 797d8bff0807acbd36d079401fc99dbf042f71e9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 10 Mar 2023 11:07:28 -0500 Subject: [PATCH 09/47] Don't lint `manual_clamp` in const contexts. --- clippy_lints/src/manual_clamp.rs | 7 ++++--- tests/ui/manual_clamp.rs | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index f239736d38a..440362b96b4 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -6,7 +6,8 @@ use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::MaybePath; use clippy_utils::{ - eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, + eq_expr_value, in_constant, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, + peel_blocks_with_stmt, }; use itertools::Itertools; use rustc_errors::Applicability; @@ -117,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { if !self.msrv.meets(msrvs::CLAMP) { return; } - if !expr.span.from_expansion() { + if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) { let suggestion = is_if_elseif_else_pattern(cx, expr) .or_else(|| is_max_min_pattern(cx, expr)) .or_else(|| is_call_max_min_pattern(cx, expr)) @@ -130,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if !self.msrv.meets(msrvs::CLAMP) { + if !self.msrv.meets(msrvs::CLAMP) || in_constant(cx, block.hir_id) { return; } for suggestion in is_two_if_pattern(cx, block) { diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index f7902e6fd53..cdfd8e4c3fe 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -326,3 +326,22 @@ fn msrv_1_50() { input }; } + +const fn _const() { + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; + + let mut x = input; + if max < x { + let x = max; + } + if min > x { + x = min; + } +} From 5c85cd9fee1ca177187b424a4328fa298c19a9aa Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 9 Mar 2023 19:53:59 +0000 Subject: [PATCH 10/47] Directly construct Inherited. --- .../src/methods/unnecessary_to_owned.rs | 8 +-- clippy_lints/src/transmute/utils.rs | 57 +++++++++---------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index df26b36b7b3..4c4c003ca46 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -369,10 +369,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< Node::Item(item) => { if let ItemKind::Fn(_, _, body_id) = &item.kind && let output_ty = return_ty(cx, item.owner_id) - && Inherited::build(cx.tcx, item.owner_id.def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.owner_id.def_id); - fn_ctxt.can_coerce(ty, output_ty) - }) { + && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id) + && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id) + && fn_ctxt.can_coerce(ty, output_ty) + { if has_lifetime(output_ty) && has_lifetime(ty) { return false; } diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index cddaf9450ea..62efd13b8d9 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -33,38 +33,37 @@ pub(super) fn check_cast<'tcx>( let hir_id = e.hir_id; let local_def_id = hir_id.owner.def_id; - Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id); + let inherited = Inherited::new(cx.tcx, local_def_id); + let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, local_def_id); - // If we already have errors, we can't be sure we can pointer cast. + // If we already have errors, we can't be sure we can pointer cast. + assert!( + !fn_ctxt.errors_reported_since_creation(), + "Newly created FnCtxt contained errors" + ); + + if let Ok(check) = cast::CastCheck::new( + &fn_ctxt, + e, + from_ty, + to_ty, + // We won't show any error to the user, so we don't care what the span is here. + DUMMY_SP, + DUMMY_SP, + hir::Constness::NotConst, + ) { + let res = check.do_check(&fn_ctxt); + + // do_check's documentation says that it might return Ok and create + // errors in the fcx instead of returning Err in some cases. Those cases + // should be filtered out before getting here. assert!( !fn_ctxt.errors_reported_since_creation(), - "Newly created FnCtxt contained errors" + "`fn_ctxt` contained errors after cast check!" ); - if let Ok(check) = cast::CastCheck::new( - &fn_ctxt, - e, - from_ty, - to_ty, - // We won't show any error to the user, so we don't care what the span is here. - DUMMY_SP, - DUMMY_SP, - hir::Constness::NotConst, - ) { - let res = check.do_check(&fn_ctxt); - - // do_check's documentation says that it might return Ok and create - // errors in the fcx instead of returning Err in some cases. Those cases - // should be filtered out before getting here. - assert!( - !fn_ctxt.errors_reported_since_creation(), - "`fn_ctxt` contained errors after cast check!" - ); - - res.ok() - } else { - None - } - }) + res.ok() + } else { + None + } } From 0f1474ea270fb3b5f7c0db295b2817028acdb97a Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sat, 11 Mar 2023 00:23:58 +0100 Subject: [PATCH 11/47] Add `allow_attribute` lint --- CHANGELOG.md | 1 + clippy_lints/src/allow_attribute.rs | 91 +++++++++++++++++++++++++++++ clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 6 ++ tests/ui/allow_attribute.fixed | 18 ++++++ tests/ui/allow_attribute.rs | 18 ++++++ tests/ui/allow_attribute.stderr | 10 ++++ 7 files changed, 145 insertions(+) create mode 100644 clippy_lints/src/allow_attribute.rs create mode 100644 tests/ui/allow_attribute.fixed create mode 100644 tests/ui/allow_attribute.rs create mode 100644 tests/ui/allow_attribute.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index da7042f4440..2b00fd962ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4382,6 +4382,7 @@ Released 2018-09-13 [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core +[`allow_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attribute [`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason [`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range [`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range diff --git a/clippy_lints/src/allow_attribute.rs b/clippy_lints/src/allow_attribute.rs new file mode 100644 index 00000000000..2970d467f6f --- /dev/null +++ b/clippy_lints/src/allow_attribute.rs @@ -0,0 +1,91 @@ +use ast::{AttrStyle, MetaItemKind}; +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet}; +use rustc_ast as ast; +use rustc_errors::Applicability; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::Ident, BytePos}; + +declare_clippy_lint! { + /// ### What it does + /// Detects uses of the `#[allow]` attribute and suggests to replace it with the new `#[expect]` attribute implemented by `#![feature(lint_reasons)]` ([RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) + /// ### Why is this bad? + /// Using `#[allow]` isn't bad, but `#[expect]` may be preferred as it lints if the code **doesn't** produce a warning. + /// ### Example + /// ```rust + /// #[allow(unused_mut)] + /// fn foo() -> usize { + /// let mut a = Vec::new(); + /// a.len() + ///} + /// ``` + /// Use instead: + /// ```rust + /// #[expect(unused_mut)] + /// fn foo() -> usize { + /// let mut a = Vec::new(); + /// a.len() + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub ALLOW_ATTRIBUTE, + restriction, + "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." +} + +pub struct AllowAttribute { + pub lint_reasons_active: bool, +} + +impl_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTE]); + +impl LateLintPass<'_> for AllowAttribute { + // Separate each crate's features. + fn check_crate_post(&mut self, _: &LateContext<'_>) { + self.lint_reasons_active = false; + } + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { + // Check inner attributes + + if_chain! { + if let AttrStyle::Inner = attr.style; + if attr.ident() + .unwrap_or(Ident::with_dummy_span(sym!(empty))) // Will not trigger if doesn't have an ident. + .name == sym!(feature); + if let ast::AttrKind::Normal(normal) = &attr.kind; + if let Some(MetaItemKind::List(list)) = normal.item.meta_kind(); + if list[0].ident().unwrap().name == sym!(lint_reasons); + then { + self.lint_reasons_active = true; + } + } + + // Check outer attributes + + if_chain! { + if let AttrStyle::Outer = attr.style; + if attr.ident() + .unwrap_or(Ident::with_dummy_span(sym!(empty))) // Will not trigger if doesn't have an ident. + .name == sym!(allow); + if self.lint_reasons_active; + then { + span_lint_and_sugg( + cx, + ALLOW_ATTRIBUTE, + attr.span, + "#[allow] attribute found", + "replace it with", + format!("#[expect{})]", snippet( + cx, + attr.ident().unwrap().span + .with_lo( + attr.ident().unwrap().span.hi() + BytePos(2) // Cut [( + ) + .with_hi( + attr.meta().unwrap().span.hi() - BytePos(2) // Cut )] + ) + , "...")), Applicability::MachineApplicable); + } + } + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index cc6024b87cd..73f74d59f4a 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -35,6 +35,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, + crate::allow_attribute::ALLOW_ATTRIBUTE_INFO, crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, crate::as_conversions::AS_CONVERSIONS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 491732be208..51da5d1ba49 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -67,6 +67,7 @@ mod declared_lints; mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` +mod allow_attribute; mod almost_complete_range; mod approx_const; mod as_conversions; @@ -933,6 +934,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock)); store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); + store.register_late_pass(|_| { + Box::new(allow_attribute::AllowAttribute { + lint_reasons_active: false, + }) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/allow_attribute.fixed b/tests/ui/allow_attribute.fixed new file mode 100644 index 00000000000..5f445a0bd96 --- /dev/null +++ b/tests/ui/allow_attribute.fixed @@ -0,0 +1,18 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::allow_attribute)] +#![feature(lint_reasons)] + +fn main() {} + +// Using clippy::needless_borrow just as a placeholder, it isn't relevant. + +// Should lint +#[expect(dead_code)] +struct T1; + +struct T2; // Should not lint +#[deny(clippy::needless_borrow)] // Should not lint +struct T3; +#[warn(clippy::needless_borrow)] // Should not lint +struct T4; diff --git a/tests/ui/allow_attribute.rs b/tests/ui/allow_attribute.rs new file mode 100644 index 00000000000..36d2e3e27ab --- /dev/null +++ b/tests/ui/allow_attribute.rs @@ -0,0 +1,18 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::allow_attribute)] +#![feature(lint_reasons)] + +fn main() {} + +// Using clippy::needless_borrow just as a placeholder, it isn't relevant. + +// Should lint +#[allow(dead_code)] +struct T1; + +struct T2; // Should not lint +#[deny(clippy::needless_borrow)] // Should not lint +struct T3; +#[warn(clippy::needless_borrow)] // Should not lint +struct T4; diff --git a/tests/ui/allow_attribute.stderr b/tests/ui/allow_attribute.stderr new file mode 100644 index 00000000000..6f90661577c --- /dev/null +++ b/tests/ui/allow_attribute.stderr @@ -0,0 +1,10 @@ +error: #[allow] attribute found + --> $DIR/allow_attribute.rs:11:1 + | +LL | #[allow(dead_code)] + | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `#[expect(dead_code)]` + | + = note: `-D clippy::allow-attribute` implied by `-D warnings` + +error: aborting due to previous error + From 59568962aed2c4aff72360e4fbd3b9c04371dbec Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sat, 11 Mar 2023 00:48:50 +0100 Subject: [PATCH 12/47] Fix code fragment --- clippy_lints/src/allow_attribute.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_lints/src/allow_attribute.rs b/clippy_lints/src/allow_attribute.rs index 2970d467f6f..805c804119a 100644 --- a/clippy_lints/src/allow_attribute.rs +++ b/clippy_lints/src/allow_attribute.rs @@ -21,6 +21,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust + /// # #![feature(lint_reasons)] /// #[expect(unused_mut)] /// fn foo() -> usize { /// let mut a = Vec::new(); From 1cf72183cfe0a29708130f59c79176128b06fcae Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sat, 11 Mar 2023 01:08:03 +0100 Subject: [PATCH 13/47] Add ignore flag to code fragments --- clippy_lints/src/allow_attribute.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/allow_attribute.rs b/clippy_lints/src/allow_attribute.rs index 805c804119a..2258b973307 100644 --- a/clippy_lints/src/allow_attribute.rs +++ b/clippy_lints/src/allow_attribute.rs @@ -12,7 +12,7 @@ declare_clippy_lint! { /// ### Why is this bad? /// Using `#[allow]` isn't bad, but `#[expect]` may be preferred as it lints if the code **doesn't** produce a warning. /// ### Example - /// ```rust + /// ```rust,ignore /// #[allow(unused_mut)] /// fn foo() -> usize { /// let mut a = Vec::new(); @@ -20,7 +20,7 @@ declare_clippy_lint! { ///} /// ``` /// Use instead: - /// ```rust + /// ```rust,ignore /// # #![feature(lint_reasons)] /// #[expect(unused_mut)] /// fn foo() -> usize { From 2d572d4a9c20a11a3aa75a7fdd9168cc95c60d4c Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sat, 11 Mar 2023 13:11:27 +0100 Subject: [PATCH 14/47] Replace list indexing for `.get` (fail-safe) --- clippy_lints/src/allow_attribute.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/allow_attribute.rs b/clippy_lints/src/allow_attribute.rs index 2258b973307..defd14da9ef 100644 --- a/clippy_lints/src/allow_attribute.rs +++ b/clippy_lints/src/allow_attribute.rs @@ -55,7 +55,8 @@ impl LateLintPass<'_> for AllowAttribute { .name == sym!(feature); if let ast::AttrKind::Normal(normal) = &attr.kind; if let Some(MetaItemKind::List(list)) = normal.item.meta_kind(); - if list[0].ident().unwrap().name == sym!(lint_reasons); + if let Some(symbol) = list.get(0); + if symbol.ident().unwrap().name == sym!(lint_reasons); then { self.lint_reasons_active = true; } From d65c9a5700454ab2a3bd81dccff610ad13855d14 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sat, 11 Mar 2023 21:30:34 +0100 Subject: [PATCH 15/47] Extend tests + improve description + general improvement --- clippy_lints/src/allow_attribute.rs | 84 ++++++++++++----------------- clippy_lints/src/lib.rs | 6 +-- tests/ui/allow_attribute.fixed | 7 +++ tests/ui/allow_attribute.rs | 7 +++ tests/ui/allow_attribute.stderr | 12 +++-- 5 files changed, 59 insertions(+), 57 deletions(-) diff --git a/clippy_lints/src/allow_attribute.rs b/clippy_lints/src/allow_attribute.rs index defd14da9ef..28fc45d0554 100644 --- a/clippy_lints/src/allow_attribute.rs +++ b/clippy_lints/src/allow_attribute.rs @@ -1,16 +1,27 @@ -use ast::{AttrStyle, MetaItemKind}; -use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet}; +use ast::AttrStyle; +use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{symbol::Ident, BytePos}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// ### What it does - /// Detects uses of the `#[allow]` attribute and suggests to replace it with the new `#[expect]` attribute implemented by `#![feature(lint_reasons)]` ([RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) + /// Detects uses of the `#[allow]` attribute and suggests replacing it with + /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) + /// + /// The expect attribute is still unstable and requires the `lint_reasons` + /// on nightly. It can be enabled by adding `#![feature(lint_reasons)]` to + /// the crate root. + /// + /// This lint only warns outer attributes (`#[allow]`), as inner attributes + /// (`#![allow]`) are usually used to enable or disable lints on a global scale. + /// /// ### Why is this bad? - /// Using `#[allow]` isn't bad, but `#[expect]` may be preferred as it lints if the code **doesn't** produce a warning. + /// + /// `#[expect]` attributes suppress the lint emission, but emit a warning, if + /// the expectation is unfulfilled. This can be useful to be notified when the + /// lint is no longer triggered. + /// /// ### Example /// ```rust,ignore /// #[allow(unused_mut)] @@ -34,59 +45,34 @@ declare_clippy_lint! { "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." } -pub struct AllowAttribute { - pub lint_reasons_active: bool, -} - -impl_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTE]); +declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTE]); impl LateLintPass<'_> for AllowAttribute { // Separate each crate's features. - fn check_crate_post(&mut self, _: &LateContext<'_>) { - self.lint_reasons_active = false; - } fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { - // Check inner attributes - - if_chain! { - if let AttrStyle::Inner = attr.style; - if attr.ident() - .unwrap_or(Ident::with_dummy_span(sym!(empty))) // Will not trigger if doesn't have an ident. - .name == sym!(feature); - if let ast::AttrKind::Normal(normal) = &attr.kind; - if let Some(MetaItemKind::List(list)) = normal.item.meta_kind(); - if let Some(symbol) = list.get(0); - if symbol.ident().unwrap().name == sym!(lint_reasons); - then { - self.lint_reasons_active = true; - } - } - - // Check outer attributes - if_chain! { + if cx.tcx.features().lint_reasons; if let AttrStyle::Outer = attr.style; - if attr.ident() - .unwrap_or(Ident::with_dummy_span(sym!(empty))) // Will not trigger if doesn't have an ident. - .name == sym!(allow); - if self.lint_reasons_active; + if let Some(ident) = attr.ident(); + if ident.name == rustc_span::symbol::sym::allow; then { span_lint_and_sugg( cx, ALLOW_ATTRIBUTE, - attr.span, + ident.span, "#[allow] attribute found", - "replace it with", - format!("#[expect{})]", snippet( - cx, - attr.ident().unwrap().span - .with_lo( - attr.ident().unwrap().span.hi() + BytePos(2) // Cut [( - ) - .with_hi( - attr.meta().unwrap().span.hi() - BytePos(2) // Cut )] - ) - , "...")), Applicability::MachineApplicable); + "replace it with", "expect".into() + // format!("expect{}", snippet( + // cx, + // ident.span + // .with_lo( + // ident.span.hi() + BytePos(2) // Cut *( + // ) + // .with_hi( + // attr.meta().unwrap().span.hi() - BytePos(1) // Cut ) + // ) + // , "...")) + , Applicability::MachineApplicable); } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51da5d1ba49..a3c65a69c99 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -934,11 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock)); store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); - store.register_late_pass(|_| { - Box::new(allow_attribute::AllowAttribute { - lint_reasons_active: false, - }) - }); + store.register_late_pass(|_| Box::new(allow_attribute::AllowAttribute)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/allow_attribute.fixed b/tests/ui/allow_attribute.fixed index 5f445a0bd96..01759ced09d 100644 --- a/tests/ui/allow_attribute.fixed +++ b/tests/ui/allow_attribute.fixed @@ -16,3 +16,10 @@ struct T2; // Should not lint struct T3; #[warn(clippy::needless_borrow)] // Should not lint struct T4; +// `panic = "unwind"` should always be true +#[cfg_attr(panic = "unwind", expect(dead_code))] +struct CfgT; + +fn ignore_inner_attr() { + #![allow(unused)] // Should not lint +} diff --git a/tests/ui/allow_attribute.rs b/tests/ui/allow_attribute.rs index 36d2e3e27ab..b8e341fe9e4 100644 --- a/tests/ui/allow_attribute.rs +++ b/tests/ui/allow_attribute.rs @@ -16,3 +16,10 @@ struct T2; // Should not lint struct T3; #[warn(clippy::needless_borrow)] // Should not lint struct T4; +// `panic = "unwind"` should always be true +#[cfg_attr(panic = "unwind", allow(dead_code))] +struct CfgT; + +fn ignore_inner_attr() { + #![allow(unused)] // Should not lint +} diff --git a/tests/ui/allow_attribute.stderr b/tests/ui/allow_attribute.stderr index 6f90661577c..58b7323b068 100644 --- a/tests/ui/allow_attribute.stderr +++ b/tests/ui/allow_attribute.stderr @@ -1,10 +1,16 @@ error: #[allow] attribute found - --> $DIR/allow_attribute.rs:11:1 + --> $DIR/allow_attribute.rs:11:3 | LL | #[allow(dead_code)] - | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `#[expect(dead_code)]` + | ^^^^^ help: replace it with: `expect` | = note: `-D clippy::allow-attribute` implied by `-D warnings` -error: aborting due to previous error +error: #[allow] attribute found + --> $DIR/allow_attribute.rs:20:30 + | +LL | #[cfg_attr(panic = "unwind", allow(dead_code))] + | ^^^^^ help: replace it with: `expect` + +error: aborting due to 2 previous errors From 213a22efb94ab62d7458501c5ab9e303b222e6a2 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 27 Feb 2023 13:07:44 +0000 Subject: [PATCH 16/47] Remove uses of `box_syntax` in rustc and tools --- tests/ui/boxed_local.rs | 35 +++++++------------- tests/ui/boxed_local.stderr | 8 ++--- tests/ui/no_effect.rs | 3 +- tests/ui/no_effect.stderr | 38 ++++++++++------------ tests/ui/unnecessary_operation.fixed | 2 -- tests/ui/unnecessary_operation.rs | 2 -- tests/ui/unnecessary_operation.stderr | 46 ++++++++++++--------------- 7 files changed, 53 insertions(+), 81 deletions(-) diff --git a/tests/ui/boxed_local.rs b/tests/ui/boxed_local.rs index 4639f00a8d8..79b6d33fc77 100644 --- a/tests/ui/boxed_local.rs +++ b/tests/ui/boxed_local.rs @@ -1,4 +1,3 @@ -#![feature(box_syntax)] #![feature(lint_reasons)] #![allow( clippy::borrowed_box, @@ -34,7 +33,7 @@ fn ok_box_trait(boxed_trait: &Box) { } fn warn_call() { - let x = box A; + let x = Box::new(A); x.foo(); } @@ -43,41 +42,41 @@ fn warn_arg(x: Box) { } fn nowarn_closure_arg() { - let x = Some(box A); + let x = Some(Box::new(A)); x.map_or((), |x| take_ref(&x)); } fn warn_rename_call() { - let x = box A; + let x = Box::new(A); let y = x; y.foo(); // via autoderef } fn warn_notuse() { - let bz = box A; + let bz = Box::new(A); } fn warn_pass() { - let bz = box A; + let bz = Box::new(A); take_ref(&bz); // via deref coercion } fn nowarn_return() -> Box { - box A // moved out, "escapes" + Box::new(A) // moved out, "escapes" } fn nowarn_move() { - let bx = box A; + let bx = Box::new(A); drop(bx) // moved in, "escapes" } fn nowarn_call() { - let bx = box A; + let bx = Box::new(A); bx.clone(); // method only available to Box, not via autoderef } fn nowarn_pass() { - let bx = box A; + let bx = Box::new(A); take_box(&bx); // fn needs &Box } @@ -86,30 +85,20 @@ fn take_ref(x: &A) {} fn nowarn_ref_take() { // false positive, should actually warn - let x = box A; + let x = Box::new(A); let y = &x; take_box(y); } fn nowarn_match() { - let x = box A; // moved into a match + let x = Box::new(A); // moved into a match match x { y => drop(y), } } fn warn_match() { - let x = box A; - match &x { - // not moved - y => (), - } -} - -fn nowarn_large_array() { - // should not warn, is large array - // and should not be on stack - let x = box [1; 10000]; + let x = Box::new(A); match &x { // not moved y => (), diff --git a/tests/ui/boxed_local.stderr b/tests/ui/boxed_local.stderr index 9036529f39c..10d78fbc0ab 100644 --- a/tests/ui/boxed_local.stderr +++ b/tests/ui/boxed_local.stderr @@ -1,5 +1,5 @@ error: local variable doesn't need to be boxed here - --> $DIR/boxed_local.rs:41:13 + --> $DIR/boxed_local.rs:40:13 | LL | fn warn_arg(x: Box) { | ^ @@ -7,19 +7,19 @@ LL | fn warn_arg(x: Box) { = note: `-D clippy::boxed-local` implied by `-D warnings` error: local variable doesn't need to be boxed here - --> $DIR/boxed_local.rs:132:12 + --> $DIR/boxed_local.rs:121:12 | LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ error: local variable doesn't need to be boxed here - --> $DIR/boxed_local.rs:196:44 + --> $DIR/boxed_local.rs:185:44 | LL | fn default_impl_x(self: Box, x: Box) -> u32 { | ^ error: local variable doesn't need to be boxed here - --> $DIR/boxed_local.rs:203:16 + --> $DIR/boxed_local.rs:192:16 | LL | fn foo(x: Box) {} | ^ diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index f08eb092e6b..ec8a5aa28c5 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -1,4 +1,4 @@ -#![feature(box_syntax, fn_traits, unboxed_closures)] +#![feature(fn_traits, unboxed_closures)] #![warn(clippy::no_effect_underscore_binding)] #![allow(dead_code, path_statements)] #![allow(clippy::deref_addrof, clippy::redundant_field_names, clippy::uninlined_format_args)] @@ -102,7 +102,6 @@ fn main() { *&42; &6; (5, 6, 7); - box 42; ..; 5..; ..5; diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index 6a1e636f9a6..92f6dbfbdba 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -81,83 +81,77 @@ LL | (5, 6, 7); error: statement with no effect --> $DIR/no_effect.rs:105:5 | -LL | box 42; - | ^^^^^^^ - -error: statement with no effect - --> $DIR/no_effect.rs:106:5 - | LL | ..; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:107:5 + --> $DIR/no_effect.rs:106:5 | LL | 5..; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:108:5 + --> $DIR/no_effect.rs:107:5 | LL | ..5; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:109:5 + --> $DIR/no_effect.rs:108:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:110:5 + --> $DIR/no_effect.rs:109:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:111:5 + --> $DIR/no_effect.rs:110:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:112:5 + --> $DIR/no_effect.rs:111:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:113:5 + --> $DIR/no_effect.rs:112:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:114:5 + --> $DIR/no_effect.rs:113:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:115:5 + --> $DIR/no_effect.rs:114:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:117:5 + --> $DIR/no_effect.rs:116:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:119:5 + --> $DIR/no_effect.rs:118:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:120:5 + --> $DIR/no_effect.rs:119:5 | LL | let _unused = 1; | ^^^^^^^^^^^^^^^^ @@ -165,22 +159,22 @@ LL | let _unused = 1; = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:121:5 + --> $DIR/no_effect.rs:120:5 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:122:5 + --> $DIR/no_effect.rs:121:5 | LL | let _duck = Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:123:5 + --> $DIR/no_effect.rs:122:5 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 30 previous errors +error: aborting due to 29 previous errors diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index d37163570ab..65d9c910b82 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(box_syntax)] #![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] #![warn(clippy::unnecessary_operation)] @@ -59,7 +58,6 @@ fn main() { 5;6;get_number(); get_number(); get_number(); - get_number(); 5;get_number(); 42;get_number(); assert!([42, 55].len() > get_usize()); diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index a14fd4bca0e..4e2acd59f04 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(box_syntax)] #![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] #![warn(clippy::unnecessary_operation)] @@ -57,7 +56,6 @@ fn main() { *&get_number(); &get_number(); (5, 6, get_number()); - box get_number(); get_number()..; ..get_number(); 5..get_number(); diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index f66d08ecb82..44cf2e01ff7 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -1,5 +1,5 @@ error: unnecessary operation - --> $DIR/unnecessary_operation.rs:51:5 + --> $DIR/unnecessary_operation.rs:50:5 | LL | Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` @@ -7,109 +7,103 @@ LL | Tuple(get_number()); = note: `-D clippy::unnecessary-operation` implied by `-D warnings` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:52:5 + --> $DIR/unnecessary_operation.rs:51:5 | LL | Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:53:5 + --> $DIR/unnecessary_operation.rs:52:5 | LL | Struct { ..get_struct() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:54:5 + --> $DIR/unnecessary_operation.rs:53:5 | LL | Enum::Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:55:5 + --> $DIR/unnecessary_operation.rs:54:5 | LL | Enum::Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:56:5 + --> $DIR/unnecessary_operation.rs:55:5 | LL | 5 + get_number(); | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:57:5 + --> $DIR/unnecessary_operation.rs:56:5 | LL | *&get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:58:5 + --> $DIR/unnecessary_operation.rs:57:5 | LL | &get_number(); | ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:59:5 + --> $DIR/unnecessary_operation.rs:58:5 | LL | (5, 6, get_number()); | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:60:5 - | -LL | box get_number(); - | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` - -error: unnecessary operation - --> $DIR/unnecessary_operation.rs:61:5 + --> $DIR/unnecessary_operation.rs:59:5 | LL | get_number()..; | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:62:5 + --> $DIR/unnecessary_operation.rs:60:5 | LL | ..get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:63:5 + --> $DIR/unnecessary_operation.rs:61:5 | LL | 5..get_number(); | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:64:5 + --> $DIR/unnecessary_operation.rs:62:5 | LL | [42, get_number()]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:65:5 + --> $DIR/unnecessary_operation.rs:63:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:66:5 + --> $DIR/unnecessary_operation.rs:64:5 | LL | (42, get_number()).1; | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:67:5 + --> $DIR/unnecessary_operation.rs:65:5 | LL | [get_number(); 55]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:68:5 + --> $DIR/unnecessary_operation.rs:66:5 | LL | [42; 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:69:5 + --> $DIR/unnecessary_operation.rs:67:5 | LL | / { LL | | get_number() @@ -117,12 +111,12 @@ LL | | }; | |______^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:72:5 + --> $DIR/unnecessary_operation.rs:70:5 | LL | / FooString { LL | | s: String::from("blah"), LL | | }; | |______^ help: statement can be reduced to: `String::from("blah");` -error: aborting due to 20 previous errors +error: aborting due to 19 previous errors From 15f24234c874df0f5225229a6fd5c0514f2c7ceb Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 27 Feb 2023 13:17:29 +0000 Subject: [PATCH 17/47] Remove `box_syntax` from AST and use in tools --- clippy_lints/src/suspicious_operation_groupings.rs | 3 +-- clippy_utils/src/ast_utils.rs | 2 +- clippy_utils/src/sugg.rs | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index e111c7d2291..8aa47b62ebf 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -596,8 +596,7 @@ fn ident_difference_expr_with_base_location( | (MethodCall(_), MethodCall(_)) | (Call(_, _), Call(_, _)) | (ConstBlock(_), ConstBlock(_)) - | (Array(_), Array(_)) - | (Box(_), Box(_)) => { + | (Array(_), Array(_)) => { // keep going }, _ => { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index d82098523e3..809d654603a 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -143,7 +143,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Paren(l), _) => eq_expr(l, r), (_, Paren(r)) => eq_expr(l, r), (Err, Err) => true, - (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r), + (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r), (Array(l), Array(r)) => over(l, r, |l, r| eq_expr(l, r)), (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 07feadca2b0..85bf28b708b 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -188,7 +188,6 @@ impl<'a> Sugg<'a> { match expr.kind { _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::AddrOf(..) - | ast::ExprKind::Box(..) | ast::ExprKind::Closure { .. } | ast::ExprKind::If(..) | ast::ExprKind::Let(..) From 1c7048d7852937099320091900e25061fde5479f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 7 Mar 2023 09:40:55 -0500 Subject: [PATCH 18/47] Add utility macros to help with writing tests. --- tests/ui/almost_complete_range.fixed | 49 +- tests/ui/almost_complete_range.rs | 49 +- tests/ui/almost_complete_range.stderr | 93 ++-- tests/ui/as_conversions.rs | 13 +- tests/ui/as_conversions.stderr | 6 +- tests/ui/auxiliary/doc_unsafe_macros.rs | 16 - tests/ui/auxiliary/implicit_hasher_macros.rs | 6 - tests/ui/auxiliary/macro_rules.rs | 141 ------ tests/ui/auxiliary/macro_use_helper.rs | 2 +- tests/ui/auxiliary/proc_macro_with_span.rs | 32 -- tests/ui/auxiliary/proc_macros.rs | 474 ++++++++++++++++++ .../auxiliary/helper.rs | 2 +- tests/ui/crashes/ice-10148.rs | 6 +- tests/ui/default_numeric_fallback_f64.fixed | 17 +- tests/ui/default_numeric_fallback_f64.rs | 17 +- tests/ui/default_numeric_fallback_f64.stderr | 11 +- tests/ui/default_numeric_fallback_i32.fixed | 17 +- tests/ui/default_numeric_fallback_i32.rs | 17 +- tests/ui/default_numeric_fallback_i32.stderr | 11 +- tests/ui/default_trait_access.fixed | 6 +- tests/ui/default_trait_access.rs | 6 +- tests/ui/deref_addrof.fixed | 24 +- tests/ui/deref_addrof.rs | 24 +- tests/ui/deref_addrof.stderr | 38 +- tests/ui/deref_addrof_macro.rs | 15 +- tests/ui/doc_unsafe.rs | 12 +- tests/ui/empty_loop.rs | 15 +- tests/ui/equatable_if_let.fixed | 16 +- tests/ui/equatable_if_let.rs | 16 +- tests/ui/equatable_if_let.stderr | 32 +- tests/ui/field_reassign_with_default.rs | 36 +- tests/ui/field_reassign_with_default.stderr | 44 +- tests/ui/implicit_hasher.rs | 25 +- tests/ui/implicit_hasher.stderr | 55 +- .../ui/inconsistent_struct_constructor.fixed | 21 +- tests/ui/inconsistent_struct_constructor.rs | 21 +- .../ui/inconsistent_struct_constructor.stderr | 4 +- tests/ui/large_enum_variant.rs | 13 +- tests/ui/macro_use_imports.fixed | 4 +- tests/ui/macro_use_imports.rs | 2 +- tests/ui/macro_use_imports.stderr | 2 +- tests/ui/macro_use_imports_expect.rs | 2 +- tests/ui/manual_rem_euclid.fixed | 24 +- tests/ui/manual_rem_euclid.rs | 24 +- tests/ui/manual_rem_euclid.stderr | 17 +- tests/ui/mem_replace_macro.rs | 23 +- tests/ui/mem_replace_macro.stderr | 11 +- .../ui/missing_const_for_fn/cant_be_const.rs | 6 +- tests/ui/missing_doc.rs | 6 +- tests/ui/missing_doc_impl.rs | 6 +- tests/ui/mistyped_literal_suffix.fixed | 6 +- tests/ui/mistyped_literal_suffix.rs | 6 +- tests/ui/multiple_unsafe_ops_per_block.rs | 11 +- tests/ui/multiple_unsafe_ops_per_block.stderr | 18 +- tests/ui/must_use_unit.fixed | 11 +- tests/ui/must_use_unit.rs | 11 +- tests/ui/mut_mut.rs | 11 +- tests/ui/mut_mut.stderr | 25 +- tests/ui/needless_late_init.fixed | 36 +- tests/ui/needless_late_init.rs | 36 +- tests/ui/needless_late_init.stderr | 32 +- tests/ui/needless_lifetimes.fixed | 41 +- tests/ui/needless_lifetimes.rs | 41 +- tests/ui/needless_lifetimes.stderr | 15 +- tests/ui/option_env_unwrap.rs | 24 +- tests/ui/option_env_unwrap.stderr | 42 +- tests/ui/ptr_as_ptr.fixed | 16 +- tests/ui/ptr_as_ptr.rs | 16 +- tests/ui/ptr_as_ptr.stderr | 25 +- tests/ui/single_match_else.rs | 6 +- tests/ui/string_add.rs | 11 +- tests/ui/toplevel_ref_arg.fixed | 24 +- tests/ui/toplevel_ref_arg.rs | 24 +- tests/ui/toplevel_ref_arg.stderr | 21 +- tests/ui/toplevel_ref_arg_non_rustfix.rs | 22 +- tests/ui/toplevel_ref_arg_non_rustfix.stderr | 7 +- tests/ui/try_err.fixed | 67 +-- tests/ui/try_err.rs | 67 +-- tests/ui/try_err.stderr | 32 +- tests/ui/uninlined_format_args.fixed | 6 +- tests/ui/uninlined_format_args.rs | 6 +- tests/ui/unit_arg.rs | 6 +- tests/ui/unnecessary_lazy_eval.fixed | 6 +- tests/ui/unnecessary_lazy_eval.rs | 6 +- tests/ui/unnecessary_unsafety_doc.rs | 12 +- tests/ui/unnecessary_unsafety_doc.stderr | 2 +- 86 files changed, 1179 insertions(+), 1098 deletions(-) delete mode 100644 tests/ui/auxiliary/doc_unsafe_macros.rs delete mode 100644 tests/ui/auxiliary/implicit_hasher_macros.rs delete mode 100644 tests/ui/auxiliary/proc_macro_with_span.rs create mode 100644 tests/ui/auxiliary/proc_macros.rs diff --git a/tests/ui/almost_complete_range.fixed b/tests/ui/almost_complete_range.fixed index 6046addf719..a4bf7fe18d5 100644 --- a/tests/ui/almost_complete_range.fixed +++ b/tests/ui/almost_complete_range.fixed @@ -1,6 +1,6 @@ // run-rustfix // edition:2018 -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] @@ -9,33 +9,10 @@ #![allow(clippy::needless_parens_on_range_literals)] #![allow(clippy::double_parens)] -#[macro_use] -extern crate macro_rules; - -macro_rules! a { - () => { - 'a' - }; -} -macro_rules! A { - () => { - 'A' - }; -} -macro_rules! zero { - () => { - '0' - }; -} - -macro_rules! b { - () => { - let _ = 'a'..='z'; - let _ = 'A'..='Z'; - let _ = '0'..='9'; - }; -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { #[rustfmt::skip] { @@ -56,9 +33,9 @@ fn main() { let _ = b'B'..b'Z'; let _ = b'1'..b'9'; - let _ = a!()..='z'; - let _ = A!()..='Z'; - let _ = zero!()..='9'; + let _ = inline!('a')..='z'; + let _ = inline!('A')..='Z'; + let _ = inline!('0')..='9'; let _ = match 0u8 { b'a'..=b'z' if true => 1, @@ -80,8 +57,16 @@ fn main() { _ => 7, }; - almost_complete_range!(); - b!(); + external!( + let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; + ); + inline!( + let _ = 'a'..='z'; + let _ = 'A'..='Z'; + let _ = '0'..='9'; + ); } #[clippy::msrv = "1.25"] diff --git a/tests/ui/almost_complete_range.rs b/tests/ui/almost_complete_range.rs index ae7e07ab872..8237c3a1361 100644 --- a/tests/ui/almost_complete_range.rs +++ b/tests/ui/almost_complete_range.rs @@ -1,6 +1,6 @@ // run-rustfix // edition:2018 -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] @@ -9,33 +9,10 @@ #![allow(clippy::needless_parens_on_range_literals)] #![allow(clippy::double_parens)] -#[macro_use] -extern crate macro_rules; - -macro_rules! a { - () => { - 'a' - }; -} -macro_rules! A { - () => { - 'A' - }; -} -macro_rules! zero { - () => { - '0' - }; -} - -macro_rules! b { - () => { - let _ = 'a'..'z'; - let _ = 'A'..'Z'; - let _ = '0'..'9'; - }; -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { #[rustfmt::skip] { @@ -56,9 +33,9 @@ fn main() { let _ = b'B'..b'Z'; let _ = b'1'..b'9'; - let _ = a!()..'z'; - let _ = A!()..'Z'; - let _ = zero!()..'9'; + let _ = inline!('a')..'z'; + let _ = inline!('A')..'Z'; + let _ = inline!('0')..'9'; let _ = match 0u8 { b'a'..b'z' if true => 1, @@ -80,8 +57,16 @@ fn main() { _ => 7, }; - almost_complete_range!(); - b!(); + external!( + let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; + ); + inline!( + let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; + ); } #[clippy::msrv = "1.25"] diff --git a/tests/ui/almost_complete_range.stderr b/tests/ui/almost_complete_range.stderr index a7a53287850..34521c13ab3 100644 --- a/tests/ui/almost_complete_range.stderr +++ b/tests/ui/almost_complete_range.stderr @@ -1,5 +1,5 @@ error: almost complete ascii range - --> $DIR/almost_complete_range.rs:42:17 + --> $DIR/almost_complete_range.rs:19:17 | LL | let _ = ('a') ..'z'; | ^^^^^^--^^^ @@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z'; = note: `-D clippy::almost-complete-range` implied by `-D warnings` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:43:17 + --> $DIR/almost_complete_range.rs:20:17 | LL | let _ = 'A' .. ('Z'); | ^^^^--^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z'); | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:44:17 + --> $DIR/almost_complete_range.rs:21:17 | LL | let _ = ((('0'))) .. ('9'); | ^^^^^^^^^^--^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = ((('0'))) .. ('9'); | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:51:13 + --> $DIR/almost_complete_range.rs:28:13 | LL | let _ = (b'a')..(b'z'); | ^^^^^^--^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = (b'a')..(b'z'); | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:52:13 + --> $DIR/almost_complete_range.rs:29:13 | LL | let _ = b'A'..b'Z'; | ^^^^--^^^^ @@ -41,7 +41,7 @@ LL | let _ = b'A'..b'Z'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:53:13 + --> $DIR/almost_complete_range.rs:30:13 | LL | let _ = b'0'..b'9'; | ^^^^--^^^^ @@ -49,31 +49,31 @@ LL | let _ = b'0'..b'9'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:59:13 + --> $DIR/almost_complete_range.rs:36:13 | -LL | let _ = a!()..'z'; - | ^^^^--^^^ - | | - | help: use an inclusive range: `..=` +LL | let _ = inline!('a')..'z'; + | ^^^^^^^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:60:13 + --> $DIR/almost_complete_range.rs:37:13 | -LL | let _ = A!()..'Z'; - | ^^^^--^^^ - | | - | help: use an inclusive range: `..=` +LL | let _ = inline!('A')..'Z'; + | ^^^^^^^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:61:13 + --> $DIR/almost_complete_range.rs:38:13 | -LL | let _ = zero!()..'9'; - | ^^^^^^^--^^^ - | | - | help: use an inclusive range: `..=` +LL | let _ = inline!('0')..'9'; + | ^^^^^^^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:64:9 + --> $DIR/almost_complete_range.rs:41:9 | LL | b'a'..b'z' if true => 1, | ^^^^--^^^^ @@ -81,7 +81,7 @@ LL | b'a'..b'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:65:9 + --> $DIR/almost_complete_range.rs:42:9 | LL | b'A'..b'Z' if true => 2, | ^^^^--^^^^ @@ -89,7 +89,7 @@ LL | b'A'..b'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:66:9 + --> $DIR/almost_complete_range.rs:43:9 | LL | b'0'..b'9' if true => 3, | ^^^^--^^^^ @@ -97,7 +97,7 @@ LL | b'0'..b'9' if true => 3, | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:74:9 + --> $DIR/almost_complete_range.rs:51:9 | LL | 'a'..'z' if true => 1, | ^^^--^^^ @@ -105,7 +105,7 @@ LL | 'a'..'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:75:9 + --> $DIR/almost_complete_range.rs:52:9 | LL | 'A'..'Z' if true => 2, | ^^^--^^^ @@ -113,7 +113,7 @@ LL | 'A'..'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:76:9 + --> $DIR/almost_complete_range.rs:53:9 | LL | '0'..'9' if true => 3, | ^^^--^^^ @@ -121,46 +121,37 @@ LL | '0'..'9' if true => 3, | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:33:17 + --> $DIR/almost_complete_range.rs:66:17 | LL | let _ = 'a'..'z'; | ^^^--^^^ | | | help: use an inclusive range: `..=` -... -LL | b!(); - | ---- in this macro invocation | - = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: almost complete ascii range - --> $DIR/almost_complete_range.rs:34:17 + --> $DIR/almost_complete_range.rs:67:17 | LL | let _ = 'A'..'Z'; | ^^^--^^^ | | | help: use an inclusive range: `..=` -... -LL | b!(); - | ---- in this macro invocation | - = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: almost complete ascii range - --> $DIR/almost_complete_range.rs:35:17 + --> $DIR/almost_complete_range.rs:68:17 | LL | let _ = '0'..'9'; | ^^^--^^^ | | | help: use an inclusive range: `..=` -... -LL | b!(); - | ---- in this macro invocation | - = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: almost complete ascii range - --> $DIR/almost_complete_range.rs:90:9 + --> $DIR/almost_complete_range.rs:75:9 | LL | 'a'..'z' => 1, | ^^^--^^^ @@ -168,7 +159,7 @@ LL | 'a'..'z' => 1, | help: use an inclusive range: `...` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:91:9 + --> $DIR/almost_complete_range.rs:76:9 | LL | 'A'..'Z' => 2, | ^^^--^^^ @@ -176,7 +167,7 @@ LL | 'A'..'Z' => 2, | help: use an inclusive range: `...` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:92:9 + --> $DIR/almost_complete_range.rs:77:9 | LL | '0'..'9' => 3, | ^^^--^^^ @@ -184,7 +175,7 @@ LL | '0'..'9' => 3, | help: use an inclusive range: `...` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:99:13 + --> $DIR/almost_complete_range.rs:84:13 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -192,7 +183,7 @@ LL | let _ = 'a'..'z'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:100:13 + --> $DIR/almost_complete_range.rs:85:13 | LL | let _ = 'A'..'Z'; | ^^^--^^^ @@ -200,7 +191,7 @@ LL | let _ = 'A'..'Z'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:101:13 + --> $DIR/almost_complete_range.rs:86:13 | LL | let _ = '0'..'9'; | ^^^--^^^ @@ -208,7 +199,7 @@ LL | let _ = '0'..'9'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:103:9 + --> $DIR/almost_complete_range.rs:88:9 | LL | 'a'..'z' => 1, | ^^^--^^^ @@ -216,7 +207,7 @@ LL | 'a'..'z' => 1, | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:104:9 + --> $DIR/almost_complete_range.rs:89:9 | LL | 'A'..'Z' => 1, | ^^^--^^^ @@ -224,7 +215,7 @@ LL | 'A'..'Z' => 1, | help: use an inclusive range: `..=` error: almost complete ascii range - --> $DIR/almost_complete_range.rs:105:9 + --> $DIR/almost_complete_range.rs:90:9 | LL | '0'..'9' => 3, | ^^^--^^^ diff --git a/tests/ui/as_conversions.rs b/tests/ui/as_conversions.rs index ba4394defbf..c50d4088b5e 100644 --- a/tests/ui/as_conversions.rs +++ b/tests/ui/as_conversions.rs @@ -1,20 +1,15 @@ -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::as_conversions)] #![allow(clippy::borrow_as_ptr)] -#[macro_use] -extern crate macro_rules; - -fn with_external_macro() { - as_conv_with_arg!(0u32 as u64); - as_conv!(); -} +extern crate proc_macros; +use proc_macros::external; fn main() { let i = 0u32 as u64; let j = &i as *const u64 as *mut u64; - with_external_macro(); + external!(0u32 as u64); } diff --git a/tests/ui/as_conversions.stderr b/tests/ui/as_conversions.stderr index f5d59e1e5d8..54037a64997 100644 --- a/tests/ui/as_conversions.stderr +++ b/tests/ui/as_conversions.stderr @@ -1,5 +1,5 @@ error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:15:13 + --> $DIR/as_conversions.rs:10:13 | LL | let i = 0u32 as u64; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = 0u32 as u64; = note: `-D clippy::as-conversions` implied by `-D warnings` error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:17:13 + --> $DIR/as_conversions.rs:12:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:17:13 + --> $DIR/as_conversions.rs:12:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/auxiliary/doc_unsafe_macros.rs b/tests/ui/auxiliary/doc_unsafe_macros.rs deleted file mode 100644 index 3d917e3dc75..00000000000 --- a/tests/ui/auxiliary/doc_unsafe_macros.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[macro_export] -macro_rules! undocd_unsafe { - () => { - pub unsafe fn oy_vey() { - unimplemented!(); - } - }; -} -#[macro_export] -macro_rules! undocd_safe { - () => { - pub fn vey_oy() { - unimplemented!(); - } - }; -} diff --git a/tests/ui/auxiliary/implicit_hasher_macros.rs b/tests/ui/auxiliary/implicit_hasher_macros.rs deleted file mode 100644 index 1eb77c53183..00000000000 --- a/tests/ui/auxiliary/implicit_hasher_macros.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[macro_export] -macro_rules! implicit_hasher_fn { - () => { - pub fn f(input: &HashMap) {} - }; -} diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index a13af565203..1dc92c1b92b 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -2,21 +2,6 @@ //! Used to test that certain lints don't trigger in imported external macros -#[macro_export] -macro_rules! foofoo { - () => { - loop {} - }; -} - -#[macro_export] -macro_rules! must_use_unit { - () => { - #[must_use] - fn foo() {} - }; -} - #[macro_export] macro_rules! try_err { () => { @@ -36,135 +21,9 @@ macro_rules! string_add { }; } -#[macro_export] -macro_rules! take_external { - ($s:expr) => { - std::mem::replace($s, Default::default()) - }; -} - -#[macro_export] -macro_rules! option_env_unwrap_external { - ($env: expr) => { - option_env!($env).unwrap() - }; - ($env: expr, $message: expr) => { - option_env!($env).expect($message) - }; -} - -#[macro_export] -macro_rules! ref_arg_binding { - () => { - let ref _y = 42; - }; -} - -#[macro_export] -macro_rules! ref_arg_function { - () => { - fn fun_example(ref _x: usize) {} - }; -} - -#[macro_export] -macro_rules! as_conv_with_arg { - (0u32 as u64) => { - () - }; -} - -#[macro_export] -macro_rules! as_conv { - () => { - 0u32 as u64 - }; -} - -#[macro_export] -macro_rules! large_enum_variant { - () => { - enum LargeEnumInMacro { - A(i32), - B([i32; 8000]), - } - }; -} - -#[macro_export] -macro_rules! field_reassign_with_default { - () => { - #[derive(Default)] - struct A { - pub i: i32, - pub j: i64, - } - fn lint() { - let mut a: A = Default::default(); - a.i = 42; - a; - } - }; -} - -#[macro_export] -macro_rules! default_numeric_fallback { - () => { - let x = 22; - }; -} - #[macro_export] macro_rules! mut_mut { () => { let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32; }; } - -#[macro_export] -macro_rules! ptr_as_ptr_cast { - ($ptr: ident) => { - $ptr as *const i32 - }; -} - -#[macro_export] -macro_rules! manual_rem_euclid { - () => { - let value: i32 = 5; - let _: i32 = ((value % 4) + 4) % 4; - }; -} - -#[macro_export] -macro_rules! equatable_if_let { - ($a:ident) => {{ if let 2 = $a {} }}; -} - -#[macro_export] -macro_rules! almost_complete_range { - () => { - let _ = 'a'..'z'; - let _ = 'A'..'Z'; - let _ = '0'..'9'; - }; -} - -#[macro_export] -macro_rules! unsafe_macro { - () => { - unsafe { - *core::ptr::null::<()>(); - *core::ptr::null::<()>(); - } - }; -} - -#[macro_export] -macro_rules! needless_lifetime { - () => { - fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { - unimplemented!() - } - }; -} diff --git a/tests/ui/auxiliary/macro_use_helper.rs b/tests/ui/auxiliary/macro_use_helper.rs index ecb55d8cb48..7ed8a28dbd9 100644 --- a/tests/ui/auxiliary/macro_use_helper.rs +++ b/tests/ui/auxiliary/macro_use_helper.rs @@ -13,7 +13,7 @@ pub mod inner { // RE-EXPORT // this will stick in `inner` module - pub use macro_rules::foofoo; + pub use macro_rules::mut_mut; pub use macro_rules::try_err; pub mod nested { diff --git a/tests/ui/auxiliary/proc_macro_with_span.rs b/tests/ui/auxiliary/proc_macro_with_span.rs deleted file mode 100644 index 8ea631f2bbd..00000000000 --- a/tests/ui/auxiliary/proc_macro_with_span.rs +++ /dev/null @@ -1,32 +0,0 @@ -// compile-flags: --emit=link -// no-prefer-dynamic - -#![crate_type = "proc-macro"] - -extern crate proc_macro; - -use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree}; - -#[proc_macro] -pub fn with_span(input: TokenStream) -> TokenStream { - let mut iter = input.into_iter(); - let span = iter.next().unwrap().span(); - let mut res = TokenStream::new(); - write_with_span(span, iter, &mut res); - res -} - -fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) { - for mut tt in input { - if let TokenTree::Group(g) = tt { - let mut stream = TokenStream::new(); - write_with_span(s, g.stream().into_iter(), &mut stream); - let mut group = Group::new(g.delimiter(), stream); - group.set_span(s); - out.extend([TokenTree::Group(group)]); - } else { - tt.set_span(s); - out.extend([tt]); - } - } -} diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs new file mode 100644 index 00000000000..325be83a0d7 --- /dev/null +++ b/tests/ui/auxiliary/proc_macros.rs @@ -0,0 +1,474 @@ +// compile-flags: --emit=link +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(let_chains)] +#![feature(proc_macro_span)] +#![allow(dead_code)] + +extern crate proc_macro; + +use core::mem; +use proc_macro::{ + token_stream::IntoIter, + Delimiter::{self, Brace, Parenthesis}, + Group, Ident, Literal, Punct, + Spacing::{self, Alone, Joint}, + Span, TokenStream, TokenTree as TT, +}; + +type Result = core::result::Result; + +/// Make a `compile_error!` pointing to the given span. +fn make_error(msg: &str, span: Span) -> TokenStream { + TokenStream::from_iter([ + TT::Ident(Ident::new("compile_error", span)), + TT::Punct(punct_with_span('!', Alone, span)), + TT::Group({ + let mut msg = Literal::string(msg); + msg.set_span(span); + group_with_span(Parenthesis, TokenStream::from_iter([TT::Literal(msg)]), span) + }), + ]) +} + +fn expect_tt(tt: Option, f: impl FnOnce(TT) -> Option, expected: &str, span: Span) -> Result { + match tt { + None => Err(make_error( + &format!("unexpected end of input, expected {expected}"), + span, + )), + Some(tt) => { + let span = tt.span(); + match f(tt) { + Some(x) => Ok(x), + None => Err(make_error(&format!("unexpected token, expected {expected}"), span)), + } + }, + } +} + +fn punct_with_span(c: char, spacing: Spacing, span: Span) -> Punct { + let mut p = Punct::new(c, spacing); + p.set_span(span); + p +} + +fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Group { + let mut g = Group::new(delimiter, stream); + g.set_span(span); + g +} + +/// Token used to escape the following token from the macro's span rules. +const ESCAPE_CHAR: char = '$'; + +/// Takes a single token followed by a sequence tokens. Returns the sequence of tokens with their +/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`. +#[proc_macro] +pub fn with_span(input: TokenStream) -> TokenStream { + let mut iter = input.into_iter(); + let span = iter.next().unwrap().span(); + let mut res = TokenStream::new(); + if let Err(e) = write_with_span(span, iter, &mut res) { + e + } else { + res + } +} + +/// Takes a sequence of tokens and return the tokens with the span set such that they appear to be +/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`. +#[proc_macro] +pub fn external(input: TokenStream) -> TokenStream { + let mut res = TokenStream::new(); + if let Err(e) = write_with_span(Span::mixed_site(), input.into_iter(), &mut res) { + e + } else { + res + } +} + +/// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped +/// either by `#ident` or `#(tokens)`. +fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> { + while let Some(tt) = input.next() { + match tt { + TT::Punct(p) if p.as_char() == ESCAPE_CHAR => { + expect_tt( + input.next(), + |tt| match tt { + tt @ (TT::Ident(_) | TT::Literal(_)) => { + out.extend([tt]); + Some(()) + }, + TT::Punct(mut p) if p.as_char() == ESCAPE_CHAR => { + p.set_span(s); + out.extend([TT::Punct(p)]); + Some(()) + }, + TT::Group(g) if g.delimiter() == Parenthesis => { + out.extend([TT::Group(group_with_span(Delimiter::None, g.stream(), g.span()))]); + Some(()) + }, + _ => None, + }, + "an ident, a literal, or parenthesized tokens", + p.span(), + )?; + }, + TT::Group(g) => { + let mut stream = TokenStream::new(); + write_with_span(s, g.stream().into_iter(), &mut stream)?; + out.extend([TT::Group(group_with_span(g.delimiter(), stream, s))]); + }, + mut tt => { + tt.set_span(s); + out.extend([tt]); + }, + } + } + Ok(()) +} + +/// Within the item this attribute is attached to, an `inline!` macro is available which expands the +/// contained tokens as though they came from a macro expansion. +/// +/// Within the `inline!` macro, any token preceded by `$` is passed as though it were an argument +/// with an automatically chosen fragment specifier. `$ident` will be passed as `ident`, `$1` or +/// `$"literal"` will be passed as `literal`, `$'lt` will be passed as `lifetime`, and `$(...)` will +/// pass the contained tokens as a `tt` sequence (the wrapping parenthesis are removed). If another +/// specifier is required it can be specified within parenthesis like `$(@expr ...)`. This will +/// expand the remaining tokens as a single argument. +/// +/// Multiple `inline!` macros may be nested within each other. This will expand as nested macro +/// calls. However, any arguments will be passed as though they came from the outermost context. +#[proc_macro_attribute] +pub fn inline_macros(args: TokenStream, input: TokenStream) -> TokenStream { + let mut args = args.into_iter(); + let mac_name = match args.next() { + Some(TT::Ident(name)) => Some(name), + Some(tt) => { + return make_error( + "unexpected argument, expected either an ident or no arguments", + tt.span(), + ); + }, + None => None, + }; + if let Some(tt) = args.next() { + return make_error( + "unexpected argument, expected either an ident or no arguments", + tt.span(), + ); + }; + + let mac_name = if let Some(mac_name) = mac_name { + Ident::new(&format!("__inline_mac_{mac_name}"), Span::call_site()) + } else { + let mut input = match LookaheadIter::new(input.clone().into_iter()) { + Some(x) => x, + None => return input, + }; + loop { + match input.next() { + None => break Ident::new("__inline_mac", Span::call_site()), + Some(TT::Ident(kind)) => match &*kind.to_string() { + "impl" => break Ident::new("__inline_mac_impl", Span::call_site()), + kind @ ("struct" | "enum" | "union" | "fn" | "mod" | "trait" | "type" | "const" | "static") => { + if let TT::Ident(name) = &input.tt { + break Ident::new(&format!("__inline_mac_{kind}_{name}"), Span::call_site()); + } else { + break Ident::new(&format!("__inline_mac_{kind}"), Span::call_site()); + } + }, + _ => {}, + }, + _ => {}, + } + } + }; + + let mut expander = Expander::default(); + let mut mac = MacWriter::new(mac_name); + if let Err(e) = expander.expand(input.into_iter(), &mut mac) { + return e; + } + let mut out = TokenStream::new(); + mac.finish(&mut out); + out.extend(expander.expn); + out +} + +/// Wraps a `TokenStream` iterator with a single token lookahead. +struct LookaheadIter { + tt: TT, + iter: IntoIter, +} +impl LookaheadIter { + fn new(mut iter: IntoIter) -> Option { + iter.next().map(|tt| Self { tt, iter }) + } + + /// Get's the lookahead token, replacing it with the next token in the stream. + /// Note: If there isn't a next token, this will not return the lookahead token. + fn next(&mut self) -> Option { + self.iter.next().map(|tt| mem::replace(&mut self.tt, tt)) + } +} + +/// Builds the macro used to implement all the `inline!` macro calls. +struct MacWriter { + name: Ident, + macros: TokenStream, + next_idx: usize, +} +impl MacWriter { + fn new(name: Ident) -> Self { + Self { + name, + macros: TokenStream::new(), + next_idx: 0, + } + } + + /// Inserts a new `inline!` call. + fn insert(&mut self, name_span: Span, bang_span: Span, body: Group, expander: &mut Expander) -> Result<()> { + let idx = self.next_idx; + self.next_idx += 1; + + let mut inner = Expander::for_arm(idx); + inner.expand(body.stream().into_iter(), self)?; + let new_arm = inner.arm.unwrap(); + + self.macros.extend([ + TT::Group(Group::new(Parenthesis, new_arm.args_def)), + TT::Punct(Punct::new('=', Joint)), + TT::Punct(Punct::new('>', Alone)), + TT::Group(Group::new(Parenthesis, inner.expn)), + TT::Punct(Punct::new(';', Alone)), + ]); + + expander.expn.extend([ + TT::Ident({ + let mut name = self.name.clone(); + name.set_span(name_span); + name + }), + TT::Punct(punct_with_span('!', Alone, bang_span)), + ]); + let mut call_body = TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]); + if let Some(arm) = expander.arm.as_mut() { + if !new_arm.args.is_empty() { + arm.add_sub_args(new_arm.args, &mut call_body); + } + } else { + call_body.extend(new_arm.args); + } + let mut g = Group::new(body.delimiter(), call_body); + g.set_span(body.span()); + expander.expn.extend([TT::Group(g)]); + Ok(()) + } + + /// Creates the macro definition. + fn finish(self, out: &mut TokenStream) { + if self.next_idx != 0 { + out.extend([ + TT::Ident(Ident::new("macro_rules", Span::call_site())), + TT::Punct(Punct::new('!', Alone)), + TT::Ident(self.name), + TT::Group(Group::new(Brace, self.macros)), + ]) + } + } +} + +struct MacroArm { + args_def: TokenStream, + args: Vec, +} +impl MacroArm { + fn add_single_arg_def(&mut self, kind: &str, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { + let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); + self.args_def.extend([ + TT::Punct(Punct::new('$', Alone)), + TT::Ident(name.clone()), + TT::Punct(Punct::new(':', Alone)), + TT::Ident(Ident::new(kind, Span::call_site())), + ]); + name.set_span(arg_span); + out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]); + } + + fn add_parenthesized_arg_def(&mut self, kind: Ident, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { + let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); + self.args_def.extend([TT::Group(Group::new( + Parenthesis, + TokenStream::from_iter([ + TT::Punct(Punct::new('$', Alone)), + TT::Ident(name.clone()), + TT::Punct(Punct::new(':', Alone)), + TT::Ident(kind), + ]), + ))]); + name.set_span(arg_span); + out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]); + } + + fn add_multi_arg_def(&mut self, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { + let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); + self.args_def.extend([TT::Group(Group::new( + Parenthesis, + TokenStream::from_iter([ + TT::Punct(Punct::new('$', Alone)), + TT::Group(Group::new( + Parenthesis, + TokenStream::from_iter([ + TT::Punct(Punct::new('$', Alone)), + TT::Ident(name.clone()), + TT::Punct(Punct::new(':', Alone)), + TT::Ident(Ident::new("tt", Span::call_site())), + ]), + )), + TT::Punct(Punct::new('*', Alone)), + ]), + ))]); + name.set_span(arg_span); + out.extend([ + TT::Punct(punct_with_span('$', Alone, dollar_span)), + TT::Group(group_with_span( + Parenthesis, + TokenStream::from_iter([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]), + dollar_span, + )), + TT::Punct(punct_with_span('*', Alone, dollar_span)), + ]); + } + + fn add_arg(&mut self, dollar_span: Span, tt: TT, input: &mut IntoIter, out: &mut TokenStream) -> Result<()> { + match tt { + TT::Punct(p) if p.as_char() == ESCAPE_CHAR => out.extend([TT::Punct(p)]), + TT::Punct(p) if p.as_char() == '\'' && p.spacing() == Joint => { + let lt_name = expect_tt( + input.next(), + |tt| match tt { + TT::Ident(x) => Some(x), + _ => None, + }, + "lifetime name", + p.span(), + )?; + let arg_span = p.span().join(lt_name.span()).unwrap_or(p.span()); + self.add_single_arg_def("lifetime", dollar_span, arg_span, out); + self.args.extend([TT::Punct(p), TT::Ident(lt_name)]); + }, + TT::Ident(x) => { + self.add_single_arg_def("ident", dollar_span, x.span(), out); + self.args.push(TT::Ident(x)); + }, + TT::Literal(x) => { + self.add_single_arg_def("literal", dollar_span, x.span(), out); + self.args.push(TT::Literal(x)); + }, + TT::Group(g) if g.delimiter() == Parenthesis => { + let mut inner = g.stream().into_iter(); + if let Some(TT::Punct(p)) = inner.next() + && p.as_char() == '@' + { + let kind = expect_tt( + inner.next(), + |tt| match tt { + TT::Ident(kind) => Some(kind), + _ => None, + }, + "a macro fragment specifier", + p.span(), + )?; + self.add_parenthesized_arg_def(kind, dollar_span, g.span(), out); + self.args.push(TT::Group(group_with_span(Parenthesis, inner.collect(), g.span()))) + } else { + self.add_multi_arg_def(dollar_span, g.span(), out); + self.args.push(TT::Group(g)); + } + }, + tt => return Err(make_error("unsupported escape", tt.span())), + }; + Ok(()) + } + + fn add_sub_args(&mut self, args: Vec, out: &mut TokenStream) { + self.add_multi_arg_def(Span::call_site(), Span::call_site(), out); + self.args + .extend([TT::Group(Group::new(Parenthesis, TokenStream::from_iter(args)))]); + } +} + +#[derive(Default)] +struct Expander { + arm: Option, + expn: TokenStream, +} +impl Expander { + fn for_arm(idx: usize) -> Self { + Self { + arm: Some(MacroArm { + args_def: TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]), + args: Vec::new(), + }), + expn: TokenStream::new(), + } + } + + fn write_tt(&mut self, tt: TT, mac: &mut MacWriter) -> Result<()> { + match tt { + TT::Group(g) => { + let outer = mem::take(&mut self.expn); + self.expand(g.stream().into_iter(), mac)?; + let inner = mem::replace(&mut self.expn, outer); + self.expn + .extend([TT::Group(group_with_span(g.delimiter(), inner, g.span()))]); + }, + tt => self.expn.extend([tt]), + } + Ok(()) + } + + fn expand(&mut self, input: IntoIter, mac: &mut MacWriter) -> Result<()> { + let Some(mut input) = LookaheadIter::new(input) else { + return Ok(()); + }; + while let Some(tt) = input.next() { + if let TT::Punct(p) = &tt + && p.as_char() == ESCAPE_CHAR + && let Some(arm) = self.arm.as_mut() + { + arm.add_arg(p.span(), mem::replace(&mut input.tt, tt), &mut input.iter, &mut self.expn)?; + if input.next().is_none() { + return Ok(()); + } + } else if let TT::Punct(p) = &input.tt + && p.as_char() == '!' + && let TT::Ident(name) = &tt + && name.to_string() == "inline" + { + let g = expect_tt( + input.iter.next(), + |tt| match tt { + TT::Group(g) => Some(g), + _ => None, + }, + "macro arguments", + p.span(), + )?; + mac.insert(name.span(), p.span(), g, self)?; + if input.next().is_none() { + return Ok(()); + } + } else { + self.write_tt(tt, mac)?; + } + } + self.write_tt(input.tt, mac) + } +} diff --git a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs index f13733af3d0..b03c21262c3 100644 --- a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs +++ b/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs @@ -1,5 +1,5 @@ // this file solely exists to test constants defined in foreign crates. -// As the most common case is the `http` crate, it replicates `http::HeadewrName`'s structure. +// As the most common case is the `http` crate, it replicates `http::HeaderName`'s structure. #![allow(clippy::declare_interior_mutable_const)] #![allow(unused_tuple_struct_fields)] diff --git a/tests/ui/crashes/ice-10148.rs b/tests/ui/crashes/ice-10148.rs index af33b10c693..1ab3570c907 100644 --- a/tests/ui/crashes/ice-10148.rs +++ b/tests/ui/crashes/ice-10148.rs @@ -1,8 +1,8 @@ -// aux-build:../../auxiliary/proc_macro_with_span.rs +// aux-build:../../auxiliary/proc_macros.rs -extern crate proc_macro_with_span; +extern crate proc_macros; -use proc_macro_with_span::with_span; +use proc_macros::with_span; fn main() { println!(with_span!(""something "")); diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index a9e5fd159af..42c15d6a70b 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -1,5 +1,5 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::default_numeric_fallback)] #![allow( @@ -13,8 +13,8 @@ clippy::let_with_type_underscore )] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; mod basic_expr { fn test() { @@ -167,20 +167,17 @@ mod method_calls { } mod in_macro { - macro_rules! internal_macro { - () => { - let x = 22.0_f64; - }; - } + use super::*; // Should lint in internal macro. + #[inline_macros] fn internal() { - internal_macro!(); + inline!(let x = 22.0_f64;); } // Should NOT lint in external macro. fn external() { - default_numeric_fallback!(); + external!(let x = 22.;); } } diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index 085f8f452b2..7da7ea254e9 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -1,5 +1,5 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::default_numeric_fallback)] #![allow( @@ -13,8 +13,8 @@ clippy::let_with_type_underscore )] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; mod basic_expr { fn test() { @@ -167,20 +167,17 @@ mod method_calls { } mod in_macro { - macro_rules! internal_macro { - () => { - let x = 22.; - }; - } + use super::*; // Should lint in internal macro. + #[inline_macros] fn internal() { - internal_macro!(); + inline!(let x = 22.;); } // Should NOT lint in external macro. fn external() { - default_numeric_fallback!(); + external!(let x = 22.;); } } diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index 44c6f1a9bea..b949cd1d50b 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -139,15 +139,12 @@ LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:172:21 + --> $DIR/default_numeric_fallback_f64.rs:175:25 | -LL | let x = 22.; - | ^^^ help: consider adding suffix: `22.0_f64` -... -LL | internal_macro!(); - | ----------------- in this macro invocation +LL | inline!(let x = 22.;); + | ^^^ help: consider adding suffix: `22.0_f64` | - = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 24 previous errors diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index 63ac4d5aeb6..b7485b73dcd 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -1,5 +1,5 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![feature(lint_reasons)] #![warn(clippy::default_numeric_fallback)] @@ -13,8 +13,8 @@ clippy::let_with_type_underscore )] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; mod basic_expr { fn test() { @@ -168,20 +168,17 @@ mod method_calls { } mod in_macro { - macro_rules! internal_macro { - () => { - let x = 22_i32; - }; - } + use super::*; // Should lint in internal macro. + #[inline_macros] fn internal() { - internal_macro!(); + inline!(let x = 22_i32;); } // Should NOT lint in external macro. fn external() { - default_numeric_fallback!(); + external!(let x = 22;); } } diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 28e6eceb80e..7307d31354e 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -1,5 +1,5 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![feature(lint_reasons)] #![warn(clippy::default_numeric_fallback)] @@ -13,8 +13,8 @@ clippy::let_with_type_underscore )] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; mod basic_expr { fn test() { @@ -168,20 +168,17 @@ mod method_calls { } mod in_macro { - macro_rules! internal_macro { - () => { - let x = 22; - }; - } + use super::*; // Should lint in internal macro. + #[inline_macros] fn internal() { - internal_macro!(); + inline!(let x = 22;); } // Should NOT lint in external macro. fn external() { - default_numeric_fallback!(); + external!(let x = 22;); } } diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index dd91574d5b3..48cd28102ce 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -151,15 +151,12 @@ LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:173:21 + --> $DIR/default_numeric_fallback_i32.rs:176:25 | -LL | let x = 22; - | ^^ help: consider adding suffix: `22_i32` -... -LL | internal_macro!(); - | ----------------- in this macro invocation +LL | inline!(let x = 22;); + | ^^ help: consider adding suffix: `22_i32` | - = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 26 previous errors diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index 5640599d48a..7842ef3ec40 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -1,12 +1,12 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] #![allow(clippy::uninlined_format_args)] -extern crate proc_macro_with_span; +extern crate proc_macros; -use proc_macro_with_span::with_span; +use proc_macros::with_span; use std::default; use std::default::Default as D2; use std::string; diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index 11d4bc5c5f0..cbb3e59c970 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,12 +1,12 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] #![allow(clippy::uninlined_format_args)] -extern crate proc_macro_with_span; +extern crate proc_macros; -use proc_macro_with_span::with_span; +use proc_macros::with_span; use std::default; use std::default::Default as D2; use std::string; diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 2f489deb1ee..ca5c03304c7 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -1,7 +1,12 @@ // run-rustfix +// aux-build:proc_macros.rs + #![allow(clippy::return_self_not_must_use)] #![warn(clippy::deref_addrof)] +extern crate proc_macros; +use proc_macros::inline_macros; + fn get_number() -> usize { 10 } @@ -41,28 +46,15 @@ fn main() { let _ = unsafe { *core::ptr::addr_of!(a) }; } -#[rustfmt::skip] -macro_rules! m { - ($visitor: expr) => { - $visitor - }; -} - -#[rustfmt::skip] -macro_rules! m_mut { - ($visitor: expr) => { - $visitor - }; -} - #[derive(Copy, Clone)] pub struct S; +#[inline_macros] impl S { pub fn f(&self) -> &Self { - m!(self) + inline!($(@expr self)) } #[allow(unused_mut)] // mut will be unused, once the macro is fixed pub fn f_mut(mut self) -> Self { - m_mut!(self) + inline!($(@expr self)) } } diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 49f360b9a7f..3db5fafe944 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -1,7 +1,12 @@ // run-rustfix +// aux-build:proc_macros.rs + #![allow(clippy::return_self_not_must_use)] #![warn(clippy::deref_addrof)] +extern crate proc_macros; +use proc_macros::inline_macros; + fn get_number() -> usize { 10 } @@ -41,28 +46,15 @@ fn main() { let _ = unsafe { *core::ptr::addr_of!(a) }; } -#[rustfmt::skip] -macro_rules! m { - ($visitor: expr) => { - *& $visitor - }; -} - -#[rustfmt::skip] -macro_rules! m_mut { - ($visitor: expr) => { - *& mut $visitor - }; -} - #[derive(Copy, Clone)] pub struct S; +#[inline_macros] impl S { pub fn f(&self) -> &Self { - m!(self) + inline!(*& $(@expr self)) } #[allow(unused_mut)] // mut will be unused, once the macro is fixed pub fn f_mut(mut self) -> Self { - m_mut!(self) + inline!(*&mut $(@expr self)) } } diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 75371fcdb96..e0287522fc5 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -1,5 +1,5 @@ error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:19:13 + --> $DIR/deref_addrof.rs:24:13 | LL | let b = *&a; | ^^^ help: try this: `a` @@ -7,68 +7,62 @@ LL | let b = *&a; = note: `-D clippy::deref-addrof` implied by `-D warnings` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:21:13 + --> $DIR/deref_addrof.rs:26:13 | LL | let b = *&get_number(); | ^^^^^^^^^^^^^^ help: try this: `get_number()` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:26:13 + --> $DIR/deref_addrof.rs:31:13 | LL | let b = *&bytes[1..2][0]; | ^^^^^^^^^^^^^^^^ help: try this: `bytes[1..2][0]` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:30:13 + --> $DIR/deref_addrof.rs:35:13 | LL | let b = *&(a); | ^^^^^ help: try this: `(a)` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:32:13 + --> $DIR/deref_addrof.rs:37:13 | LL | let b = *(&a); | ^^^^^ help: try this: `a` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:35:13 + --> $DIR/deref_addrof.rs:40:13 | LL | let b = *((&a)); | ^^^^^^^ help: try this: `a` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:37:13 + --> $DIR/deref_addrof.rs:42:13 | LL | let b = *&&a; | ^^^^ help: try this: `&a` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:39:14 + --> $DIR/deref_addrof.rs:44:14 | LL | let b = **&aref; | ^^^^^^ help: try this: `aref` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:47:9 + --> $DIR/deref_addrof.rs:54:17 | -LL | *& $visitor - | ^^^^^^^^^^^ help: try this: `$visitor` -... -LL | m!(self) - | -------- in this macro invocation +LL | inline!(*& $(@expr self)) + | ^^^^^^^^^^^^^^^^ help: try this: `$(@expr self)` | - = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:54:9 + --> $DIR/deref_addrof.rs:58:17 | -LL | *& mut $visitor - | ^^^^^^^^^^^^^^^ help: try this: `$visitor` -... -LL | m_mut!(self) - | ------------ in this macro invocation +LL | inline!(*&mut $(@expr self)) + | ^^^^^^^^^^^^^^^^^^^ help: try this: `$(@expr self)` | - = note: this error originates in the macro `m_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 10 previous errors diff --git a/tests/ui/deref_addrof_macro.rs b/tests/ui/deref_addrof_macro.rs index dcebd6c6e29..57c0be3f51e 100644 --- a/tests/ui/deref_addrof_macro.rs +++ b/tests/ui/deref_addrof_macro.rs @@ -1,10 +1,13 @@ -macro_rules! m { - ($($x:tt),*) => { &[$(($x, stringify!(x)),)*] }; -} +// aux-build:proc_macros.rs -#[warn(clippy::deref_addrof)] -fn f() -> [(i32, &'static str); 3] { - *m![1, 2, 3] // should be fine +#![warn(clippy::deref_addrof)] + +extern crate proc_macros; + +#[proc_macros::inline_macros] +fn f() -> i32 { + // should be fine + *inline!(&$1) } fn main() {} diff --git a/tests/ui/doc_unsafe.rs b/tests/ui/doc_unsafe.rs index b91f7aa0dd8..30674ce3708 100644 --- a/tests/ui/doc_unsafe.rs +++ b/tests/ui/doc_unsafe.rs @@ -1,9 +1,9 @@ -// aux-build:doc_unsafe_macros.rs +// aux-build:proc_macros.rs #![allow(clippy::let_unit_value)] -#[macro_use] -extern crate doc_unsafe_macros; +extern crate proc_macros; +use proc_macros::external; /// This is not sufficiently documented pub unsafe fn destroy_the_planet() { @@ -105,7 +105,11 @@ macro_rules! very_unsafe { very_unsafe!(); // we don't lint code from external macros -undocd_unsafe!(); +external! { + pub unsafe fn oy_vey() { + unimplemented!(); + } +} fn main() { unsafe { diff --git a/tests/ui/empty_loop.rs b/tests/ui/empty_loop.rs index 8fd7697eb3b..6a8e6b550c1 100644 --- a/tests/ui/empty_loop.rs +++ b/tests/ui/empty_loop.rs @@ -1,9 +1,9 @@ -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::empty_loop)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; fn should_trigger() { loop {} @@ -16,6 +16,7 @@ fn should_trigger() { } } +#[inline_macros] fn should_not_trigger() { loop { panic!("This is fine") @@ -38,14 +39,10 @@ fn should_not_trigger() { loop {} // We don't lint loops inside macros - macro_rules! foo { - () => { - loop {} - }; - } + inline!(loop {}); // We don't lint external macros - foofoo!() + external!(loop {}); } fn main() {} diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 9af2ba96272..007702ab550 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -1,11 +1,11 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)] #![warn(clippy::equatable_if_let)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; use std::cmp::Ordering; @@ -44,6 +44,7 @@ impl PartialEq for NotStructuralEq { } } +#[inline_macros] fn main() { let a = 2; let b = 3; @@ -78,14 +79,9 @@ fn main() { if Some(g) == Some(NotStructuralEq::A) {} if matches!(h, NoPartialEqStruct { a: 2, b: false }) {} - macro_rules! m1 { - (x) => { - "abc" - }; - } - if "abc" == m1!(x) { + if "abc" == inline!("abc") { println!("OK"); } - equatable_if_let!(a); + external!({ if let 2 = $a {} }); } diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index c3626c081dd..3bda7977645 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -1,11 +1,11 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)] #![warn(clippy::equatable_if_let)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; use std::cmp::Ordering; @@ -44,6 +44,7 @@ impl PartialEq for NotStructuralEq { } } +#[inline_macros] fn main() { let a = 2; let b = 3; @@ -78,14 +79,9 @@ fn main() { if let Some(NotStructuralEq::A) = Some(g) {} if let NoPartialEqStruct { a: 2, b: false } = h {} - macro_rules! m1 { - (x) => { - "abc" - }; - } - if let m1!(x) = "abc" { + if let inline!("abc") = "abc" { println!("OK"); } - equatable_if_let!(a); + external!({ if let 2 = $a {} }); } diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 40ca75b8da2..a72d87bb7ba 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -1,5 +1,5 @@ error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:59:8 + --> $DIR/equatable_if_let.rs:60:8 | LL | if let 2 = a {} | ^^^^^^^^^ help: try: `a == 2` @@ -7,82 +7,82 @@ LL | if let 2 = a {} = note: `-D clippy::equatable-if-let` implied by `-D warnings` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:60:8 + --> $DIR/equatable_if_let.rs:61:8 | LL | if let Ordering::Greater = a.cmp(&b) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:61:8 + --> $DIR/equatable_if_let.rs:62:8 | LL | if let Some(2) = c {} | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:62:8 + --> $DIR/equatable_if_let.rs:63:8 | LL | if let Struct { a: 2, b: false } = d {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:63:8 + --> $DIR/equatable_if_let.rs:64:8 | LL | if let Enum::TupleVariant(32, 64) = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:64:8 + --> $DIR/equatable_if_let.rs:65:8 | LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:65:8 + --> $DIR/equatable_if_let.rs:66:8 | LL | if let Enum::UnitVariant = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:66:8 + --> $DIR/equatable_if_let.rs:67:8 | LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` error: this pattern matching can be expressed using `matches!` - --> $DIR/equatable_if_let.rs:75:8 + --> $DIR/equatable_if_let.rs:76:8 | LL | if let NotPartialEq::A = f {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:76:8 + --> $DIR/equatable_if_let.rs:77:8 | LL | if let NotStructuralEq::A = g {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` error: this pattern matching can be expressed using `matches!` - --> $DIR/equatable_if_let.rs:77:8 + --> $DIR/equatable_if_let.rs:78:8 | LL | if let Some(NotPartialEq::A) = Some(f) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:78:8 + --> $DIR/equatable_if_let.rs:79:8 | LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` error: this pattern matching can be expressed using `matches!` - --> $DIR/equatable_if_let.rs:79:8 + --> $DIR/equatable_if_let.rs:80:8 | LL | if let NoPartialEqStruct { a: 2, b: false } = h {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:86:8 + --> $DIR/equatable_if_let.rs:82:8 | -LL | if let m1!(x) = "abc" { - | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` +LL | if let inline!("abc") = "abc" { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")` error: aborting due to 14 previous errors diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 1f989bb1220..0e208b3ed0e 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -1,12 +1,12 @@ // aux-build:proc_macro_derive.rs -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::field_reassign_with_default)] #[macro_use] extern crate proc_macro_derive; -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; // Don't lint on derives that derive `Default` // See https://github.com/rust-lang/rust-clippy/issues/6545 @@ -36,14 +36,6 @@ struct D { b: Option, } -macro_rules! m { - ($key:ident: $value:tt) => {{ - let mut data = $crate::D::default(); - data.$key = Some($value); - data - }}; -} - /// Implements .next() that returns a different number each time. struct SideEffect(i32); @@ -57,6 +49,7 @@ impl SideEffect { } } +#[inline_macros] fn main() { // wrong, produces first error in stderr let mut a: A = Default::default(); @@ -150,7 +143,18 @@ fn main() { a.i = vec![1]; // Don't lint in external macros - field_reassign_with_default!(); + external! { + #[derive(Default)] + struct A { + pub i: i32, + pub j: i64, + } + fn lint() { + let mut a: A = Default::default(); + a.i = 42; + a; + } + } // be sure suggestion is correct with generics let mut a: Wrapper = Default::default(); @@ -160,9 +164,11 @@ fn main() { a.i = 42; // Don't lint in macros - m! { - a: 42 - }; + inline!( + let mut data = $crate::D::default(); + data.$a = Some($42); + data + ); } mod m { diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index 710bb66a48a..da74f9ef9f7 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -1,132 +1,132 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:63:5 + --> $DIR/field_reassign_with_default.rs:56:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:62:5 + --> $DIR/field_reassign_with_default.rs:55:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:103:5 + --> $DIR/field_reassign_with_default.rs:96:5 | LL | a.j = 43; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:102:5 + --> $DIR/field_reassign_with_default.rs:95:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:108:5 + --> $DIR/field_reassign_with_default.rs:101:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:107:5 + --> $DIR/field_reassign_with_default.rs:100:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:114:5 + --> $DIR/field_reassign_with_default.rs:107:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:113:5 + --> $DIR/field_reassign_with_default.rs:106:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:124:5 + --> $DIR/field_reassign_with_default.rs:117:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:123:5 + --> $DIR/field_reassign_with_default.rs:116:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:128:5 + --> $DIR/field_reassign_with_default.rs:121:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:127:5 + --> $DIR/field_reassign_with_default.rs:120:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:150:5 + --> $DIR/field_reassign_with_default.rs:143:5 | LL | a.i = vec![1]; | ^^^^^^^^^^^^^^ | note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:149:5 + --> $DIR/field_reassign_with_default.rs:142:5 | LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:157:5 + --> $DIR/field_reassign_with_default.rs:161:5 | LL | a.i = true; | ^^^^^^^^^^^ | note: consider initializing the variable with `Wrapper:: { i: true }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:156:5 + --> $DIR/field_reassign_with_default.rs:160:5 | LL | let mut a: Wrapper = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:160:5 + --> $DIR/field_reassign_with_default.rs:164:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `WrapperMulti:: { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:159:5 + --> $DIR/field_reassign_with_default.rs:163:5 | LL | let mut a: WrapperMulti = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:229:13 + --> $DIR/field_reassign_with_default.rs:235:13 | LL | f.name = name.len(); | ^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:228:13 + --> $DIR/field_reassign_with_default.rs:234:13 | LL | let mut f = ImplDropAllCopy::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:245:13 + --> $DIR/field_reassign_with_default.rs:251:13 | LL | f.name = name.len(); | ^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:244:13 + --> $DIR/field_reassign_with_default.rs:250:13 | LL | let mut f = NoDropAllCopy::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/implicit_hasher.rs b/tests/ui/implicit_hasher.rs index fd96ca3f466..35d08a07bc3 100644 --- a/tests/ui/implicit_hasher.rs +++ b/tests/ui/implicit_hasher.rs @@ -1,9 +1,11 @@ -// aux-build:implicit_hasher_macros.rs +// aux-build:proc_macros.rs + #![deny(clippy::implicit_hasher)] #![allow(unused)] #[macro_use] -extern crate implicit_hasher_macros; +extern crate proc_macros; +use proc_macros::external; use std::cmp::Eq; use std::collections::{HashMap, HashSet}; @@ -68,22 +70,19 @@ impl Foo for HashSet { pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} -macro_rules! gen { - (impl) => { +#[proc_macros::inline_macros] +pub mod gen { + use super::*; + inline! { impl Foo for HashMap { fn make() -> (Self, Self) { (HashMap::new(), HashMap::with_capacity(10)) } } - }; - (fn $name:ident) => { - pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} - }; + pub fn bar(_map: &mut HashMap, _set: &mut HashSet) {} + } } -#[rustfmt::skip] -gen!(impl); -gen!(fn bar); // When the macro is in a different file, the suggestion spans can't be combined properly // and should not cause an ICE @@ -94,7 +93,9 @@ pub mod test_macro; __implicit_hasher_test_macro!(impl for HashMap where V: test_macro::A); // #4260 -implicit_hasher_fn!(); +external! { + pub fn f(input: &HashMap) {} +} // #7712 pub async fn election_vote(_data: HashMap) {} diff --git a/tests/ui/implicit_hasher.stderr b/tests/ui/implicit_hasher.stderr index 59b0fba2a4c..83b46de2eb5 100644 --- a/tests/ui/implicit_hasher.stderr +++ b/tests/ui/implicit_hasher.stderr @@ -1,11 +1,11 @@ error: impl for `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:16:35 + --> $DIR/implicit_hasher.rs:18:35 | LL | impl Foo for HashMap { | ^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/implicit_hasher.rs:2:9 + --> $DIR/implicit_hasher.rs:3:9 | LL | #![deny(clippy::implicit_hasher)] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default: | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: impl for `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:25:36 + --> $DIR/implicit_hasher.rs:27:36 | LL | impl Foo for (HashMap,) { | ^^^^^^^^^^^^^ @@ -34,7 +34,7 @@ LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Defa | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: impl for `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:30:19 + --> $DIR/implicit_hasher.rs:32:19 | LL | impl Foo for HashMap { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default: | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: impl for `HashSet` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:47:32 + --> $DIR/implicit_hasher.rs:49:32 | LL | impl Foo for HashSet { | ^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default: | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: impl for `HashSet` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:52:19 + --> $DIR/implicit_hasher.rs:54:19 | LL | impl Foo for HashSet { | ^^^^^^^^^^^^^^^ @@ -79,7 +79,7 @@ LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default: | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: parameter of type `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:69:23 + --> $DIR/implicit_hasher.rs:71:23 | LL | pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} | ^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | pub fn foo(_map: &mut HashMap, _s | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~ error: parameter of type `HashSet` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:69:53 + --> $DIR/implicit_hasher.rs:71:53 | LL | pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} | ^^^^^^^^^^^^ @@ -101,15 +101,12 @@ LL | pub fn foo(_map: &mut HashMap, _set: | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~ error: impl for `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:73:43 + --> $DIR/implicit_hasher.rs:77:43 | LL | impl Foo for HashMap { | ^^^^^^^^^^^^^ -... -LL | gen!(impl); - | ---------- in this macro invocation | - = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider adding a type parameter | LL | impl Foo for HashMap { @@ -120,37 +117,31 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: parameter of type `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:81:33 + --> $DIR/implicit_hasher.rs:83:31 | -LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} - | ^^^^^^^^^^^^^^^^^ -... -LL | gen!(fn bar); - | ------------ in this macro invocation +LL | pub fn bar(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^^^^^^ | - = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider adding a type parameter | -LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} - | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~ +LL | pub fn bar(_map: &mut HashMap, _set: &mut HashSet) {} + | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~ error: parameter of type `HashSet` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:81:63 + --> $DIR/implicit_hasher.rs:83:61 | -LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} - | ^^^^^^^^^^^^ -... -LL | gen!(fn bar); - | ------------ in this macro invocation +LL | pub fn bar(_map: &mut HashMap, _set: &mut HashSet) {} + | ^^^^^^^^^^^^ | - = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider adding a type parameter | -LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} - | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~ +LL | pub fn bar(_map: &mut HashMap, _set: &mut HashSet) {} + | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~ error: parameter of type `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:100:35 + --> $DIR/implicit_hasher.rs:101:35 | LL | pub async fn election_vote(_data: HashMap) {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed index 74ba2f1c5e7..5aaa00f8517 100644 --- a/tests/ui/inconsistent_struct_constructor.fixed +++ b/tests/ui/inconsistent_struct_constructor.fixed @@ -1,10 +1,14 @@ // run-rustfix +// aux-build:proc_macros.rs + #![warn(clippy::inconsistent_struct_constructor)] #![allow(clippy::redundant_field_names)] #![allow(clippy::unnecessary_operation)] #![allow(clippy::no_effect)] #![allow(dead_code)] +extern crate proc_macros; + #[derive(Default)] struct Foo { x: i32, @@ -12,18 +16,10 @@ struct Foo { z: i32, } -macro_rules! new_foo { - () => { - let x = 1; - let y = 1; - let z = 1; - Foo { y, x, z } - }; -} - mod without_base { use super::Foo; + #[proc_macros::inline_macros] fn test() { let x = 1; let y = 1; @@ -34,7 +30,12 @@ mod without_base { // Should NOT lint. // issue #7069. - new_foo!(); + inline!({ + let x = 1; + let y = 1; + let z = 1; + Foo { y, x, z } + }); // Should NOT lint because the order is the same as in the definition. Foo { x, y, z }; diff --git a/tests/ui/inconsistent_struct_constructor.rs b/tests/ui/inconsistent_struct_constructor.rs index ba96e1e330f..2b2dd7f59a4 100644 --- a/tests/ui/inconsistent_struct_constructor.rs +++ b/tests/ui/inconsistent_struct_constructor.rs @@ -1,10 +1,14 @@ // run-rustfix +// aux-build:proc_macros.rs + #![warn(clippy::inconsistent_struct_constructor)] #![allow(clippy::redundant_field_names)] #![allow(clippy::unnecessary_operation)] #![allow(clippy::no_effect)] #![allow(dead_code)] +extern crate proc_macros; + #[derive(Default)] struct Foo { x: i32, @@ -12,18 +16,10 @@ struct Foo { z: i32, } -macro_rules! new_foo { - () => { - let x = 1; - let y = 1; - let z = 1; - Foo { y, x, z } - }; -} - mod without_base { use super::Foo; + #[proc_macros::inline_macros] fn test() { let x = 1; let y = 1; @@ -34,7 +30,12 @@ mod without_base { // Should NOT lint. // issue #7069. - new_foo!(); + inline!({ + let x = 1; + let y = 1; + let z = 1; + Foo { y, x, z } + }); // Should NOT lint because the order is the same as in the definition. Foo { x, y, z }; diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr index c90189e964f..785a6dc9d53 100644 --- a/tests/ui/inconsistent_struct_constructor.stderr +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -1,5 +1,5 @@ error: struct constructor field order is inconsistent with struct definition field order - --> $DIR/inconsistent_struct_constructor.rs:33:9 + --> $DIR/inconsistent_struct_constructor.rs:29:9 | LL | Foo { y, x, z }; | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` @@ -7,7 +7,7 @@ LL | Foo { y, x, z }; = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` error: struct constructor field order is inconsistent with struct definition field order - --> $DIR/inconsistent_struct_constructor.rs:55:9 + --> $DIR/inconsistent_struct_constructor.rs:56:9 | LL | / Foo { LL | | z, diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index 3b96f09d7b1..f09f8ae0ccc 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -1,11 +1,11 @@ -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![allow(dead_code)] #![allow(unused_variables)] #![warn(clippy::large_enum_variant)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::external; enum LargeEnum { A(i32), @@ -155,5 +155,10 @@ enum LargeEnumOfConst { } fn main() { - large_enum_variant!(); + external!( + enum LargeEnumInMacro { + A(i32), + B([i32; 8000]), + } + ); } diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index e612480d264..15f7a099a7d 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -20,7 +20,7 @@ mod a { use mac; use mini_mac::ClippyMiniMacroTest; use mini_mac; - use mac::{inner::foofoo, inner::try_err}; + use mac::{inner::mut_mut, inner::try_err}; use mac::inner; use mac::inner::nested::string_add; use mac::inner::nested; @@ -36,7 +36,7 @@ mod a { let v: ty_macro!() = Vec::default(); inner::try_err!(); - inner::foofoo!(); + inner::mut_mut!(); nested::string_add!(); } } diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index b34817cc3b2..b1a28733294 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -36,7 +36,7 @@ mod a { let v: ty_macro!() = Vec::default(); inner::try_err!(); - inner::foofoo!(); + inner::mut_mut!(); nested::string_add!(); } } diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index 61843124ccd..68d558dede0 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -16,7 +16,7 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:23:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::mut_mut, inner::try_err};` error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:19:5 diff --git a/tests/ui/macro_use_imports_expect.rs b/tests/ui/macro_use_imports_expect.rs index 8a1b05da9ef..5aac5af26db 100644 --- a/tests/ui/macro_use_imports_expect.rs +++ b/tests/ui/macro_use_imports_expect.rs @@ -39,7 +39,7 @@ mod a { let v: ty_macro!() = Vec::default(); inner::try_err!(); - inner::foofoo!(); + inner::mut_mut!(); nested::string_add!(); } } diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index 6916a284a20..1f6df1b0a86 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -1,19 +1,13 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::manual_rem_euclid)] #![allow(clippy::let_with_type_underscore)] -#[macro_use] -extern crate macro_rules; - -macro_rules! internal_rem_euclid { - () => { - let value: i32 = 5; - let _: i32 = value.rem_euclid(4); - }; -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { let value: i32 = 5; @@ -39,10 +33,16 @@ fn main() { let _: i32 = ((4 % value) + 4) % 4; // Lint in internal macros - internal_rem_euclid!(); + inline!( + let value: i32 = 5; + let _: i32 = value.rem_euclid(4); + ); // Do not lint in external macros - manual_rem_euclid!(); + external!( + let value: i32 = 5; + let _: i32 = ((value % 4) + 4) % 4; + ); } // Should lint for params too diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 412dbddb426..b275e8a38d2 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -1,19 +1,13 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::manual_rem_euclid)] #![allow(clippy::let_with_type_underscore)] -#[macro_use] -extern crate macro_rules; - -macro_rules! internal_rem_euclid { - () => { - let value: i32 = 5; - let _: i32 = ((value % 4) + 4) % 4; - }; -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { let value: i32 = 5; @@ -39,10 +33,16 @@ fn main() { let _: i32 = ((4 % value) + 4) % 4; // Lint in internal macros - internal_rem_euclid!(); + inline!( + let value: i32 = 5; + let _: i32 = ((value % 4) + 4) % 4; + ); // Do not lint in external macros - manual_rem_euclid!(); + external!( + let value: i32 = 5; + let _: i32 = ((value % 4) + 4) % 4; + ); } // Should lint for params too diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index 6d06654638b..a43707f89c4 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:14:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,39 +7,36 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:15:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:16:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:18 + --> $DIR/manual_rem_euclid.rs:17:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:24:22 + --> $DIR/manual_rem_euclid.rs:18:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:13:22 + --> $DIR/manual_rem_euclid.rs:38:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` -... -LL | internal_rem_euclid!(); - | ---------------------- in this macro invocation | - = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation --> $DIR/manual_rem_euclid.rs:50:5 diff --git a/tests/ui/mem_replace_macro.rs b/tests/ui/mem_replace_macro.rs index 0c09344b80d..3932e7d00c1 100644 --- a/tests/ui/mem_replace_macro.rs +++ b/tests/ui/mem_replace_macro.rs @@ -1,21 +1,12 @@ -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::mem_replace_with_default)] -#[macro_use] -extern crate macro_rules; - -macro_rules! take { - ($s:expr) => { - std::mem::replace($s, Default::default()) - }; -} - -fn replace_with_default() { - let s = &mut String::from("foo"); - take!(s); - take_external!(s); -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { - replace_with_default(); + let s = &mut String::from("foo"); + inline!(std::mem::replace($s, Default::default())); + external!(std::mem::replace($s, Default::default())); } diff --git a/tests/ui/mem_replace_macro.stderr b/tests/ui/mem_replace_macro.stderr index dd69ab8b5ef..35dda93da3d 100644 --- a/tests/ui/mem_replace_macro.stderr +++ b/tests/ui/mem_replace_macro.stderr @@ -1,14 +1,11 @@ error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace_macro.rs:9:9 + --> $DIR/mem_replace_macro.rs:10:13 | -LL | std::mem::replace($s, Default::default()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | take!(s); - | -------- in this macro invocation +LL | inline!(std::mem::replace($s, Default::default())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` - = note: this error originates in the macro `take` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index 75cace18167..e6f88c6e622 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -3,15 +3,15 @@ //! The .stderr output of this test should be empty. Otherwise it's a bug somewhere. // aux-build:helper.rs -// aux-build:../../auxiliary/proc_macro_with_span.rs +// aux-build:../../auxiliary/proc_macros.rs #![warn(clippy::missing_const_for_fn)] #![feature(start)] extern crate helper; -extern crate proc_macro_with_span; +extern crate proc_macros; -use proc_macro_with_span::with_span; +use proc_macros::with_span; struct Game; diff --git a/tests/ui/missing_doc.rs b/tests/ui/missing_doc.rs index 590ad63c90b..5752048949c 100644 --- a/tests/ui/missing_doc.rs +++ b/tests/ui/missing_doc.rs @@ -1,5 +1,5 @@ // needs-asm-support -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![warn(clippy::missing_docs_in_private_items)] // When denying at the crate level, be sure to not get random warnings from the @@ -8,9 +8,9 @@ //! Some garbage docs for the crate here #![doc = "More garbage"] -extern crate proc_macro_with_span; +extern crate proc_macros; -use proc_macro_with_span::with_span; +use proc_macros::with_span; use std::arch::global_asm; type Typedef = String; diff --git a/tests/ui/missing_doc_impl.rs b/tests/ui/missing_doc_impl.rs index 0396d1193ff..e2d49b0907d 100644 --- a/tests/ui/missing_doc_impl.rs +++ b/tests/ui/missing_doc_impl.rs @@ -1,4 +1,4 @@ -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![warn(clippy::missing_docs_in_private_items)] #![allow(dead_code)] @@ -7,8 +7,8 @@ //! Some garbage docs for the crate here #![doc = "More garbage"] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; +extern crate proc_macros; +use proc_macros::with_span; struct Foo { a: isize, diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index becb9562a84..9a47d7c56ed 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -1,5 +1,5 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![allow( dead_code, @@ -10,8 +10,8 @@ clippy::unusual_byte_groupings )] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; +extern crate proc_macros; +use proc_macros::with_span; fn main() { let fail14 = 2_i32; diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index ee841bcd7e4..04261cba55a 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -1,5 +1,5 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![allow( dead_code, @@ -10,8 +10,8 @@ clippy::unusual_byte_groupings )] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; +extern crate proc_macros; +use proc_macros::with_span; fn main() { let fail14 = 2_32; diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 5073685c9f0..9082f1675a8 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,12 +1,12 @@ -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![allow(unused)] #![allow(deref_nullptr)] #![allow(clippy::unnecessary_operation)] #![allow(clippy::drop_copy)] #![warn(clippy::multiple_unsafe_ops_per_block)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::external; use core::arch::asm; @@ -113,7 +113,10 @@ unsafe fn read_char_good(ptr: *const u8) -> char { // no lint fn issue10259() { - unsafe_macro!(); + external!(unsafe { + *core::ptr::null::<()>(); + *core::ptr::null::<()>(); + }); } fn _fn_ptr(x: unsafe fn()) { diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index e0c1d3801f7..badc284ec42 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -126,7 +126,7 @@ LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:120:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:123:5 | LL | / unsafe { LL | | x(); @@ -135,18 +135,18 @@ LL | | } | |_____^ | note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:121:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:124:9 | LL | x(); | ^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:122:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:125:9 | LL | x(); | ^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:131:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:134:9 | LL | / unsafe { LL | | T::X(); @@ -155,18 +155,18 @@ LL | | } | |_________^ | note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:132:13 + --> $DIR/multiple_unsafe_ops_per_block.rs:135:13 | LL | T::X(); | ^^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:133:13 + --> $DIR/multiple_unsafe_ops_per_block.rs:136:13 | LL | T::X(); | ^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:141:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:144:5 | LL | / unsafe { LL | | x.0(); @@ -175,12 +175,12 @@ LL | | } | |_____^ | note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:142:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:145:9 | LL | x.0(); | ^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:143:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:146:9 | LL | x.0(); | ^^^^^ diff --git a/tests/ui/must_use_unit.fixed b/tests/ui/must_use_unit.fixed index 6c9aa434ac0..b7d375ff80e 100644 --- a/tests/ui/must_use_unit.fixed +++ b/tests/ui/must_use_unit.fixed @@ -1,11 +1,11 @@ //run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] #![allow(clippy::unused_unit)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::external; pub fn must_use_default() {} @@ -22,5 +22,8 @@ fn main() { must_use_with_note(); // We should not lint in external macros - must_use_unit!(); + external!( + #[must_use] + fn foo() {} + ); } diff --git a/tests/ui/must_use_unit.rs b/tests/ui/must_use_unit.rs index 8a395dc284d..74d6b4ca865 100644 --- a/tests/ui/must_use_unit.rs +++ b/tests/ui/must_use_unit.rs @@ -1,11 +1,11 @@ //run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] #![allow(clippy::unused_unit)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::external; #[must_use] pub fn must_use_default() {} @@ -22,5 +22,8 @@ fn main() { must_use_with_note(); // We should not lint in external macros - must_use_unit!(); + external!( + #[must_use] + fn foo() {} + ); } diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index ee3a856566c..06bb085442a 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -1,10 +1,10 @@ -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::mut_mut)] #![allow(unused)] #![allow(clippy::no_effect, clippy::uninlined_format_args, clippy::unnecessary_operation)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; fn fun(x: &mut &mut u32) -> bool { **x > 0 @@ -21,6 +21,7 @@ macro_rules! mut_ptr { } #[allow(unused_mut, unused_variables)] +#[inline_macros] fn main() { let mut x = &mut &mut 1u32; { @@ -37,7 +38,7 @@ fn main() { ***y + **x; } - let mut z = mut_ptr!(&mut 3u32); + let mut z = inline!(&mut $(&mut 3u32)); } fn issue939() { @@ -55,7 +56,7 @@ fn issue939() { fn issue6922() { // do not lint from an external macro - mut_mut!(); + external!(let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;); } mod issue9035 { diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 6820a85aa54..93b857eb207 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -7,54 +7,51 @@ LL | fn fun(x: &mut &mut u32) -> bool { = note: `-D clippy::mut-mut` implied by `-D warnings` error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:25:17 + --> $DIR/mut_mut.rs:26:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:19:9 + --> $DIR/mut_mut.rs:41:25 | -LL | &mut $p - | ^^^^^^^ -... -LL | let mut z = mut_ptr!(&mut 3u32); - | ------------------- in this macro invocation +LL | let mut z = inline!(&mut $(&mut 3u32)); + | ^ | - = note: this error originates in the macro `mut_ptr` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this expression mutably borrows a mutable reference. Consider reborrowing - --> $DIR/mut_mut.rs:27:21 + --> $DIR/mut_mut.rs:28:21 | LL | let mut y = &mut x; | ^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:31:32 + --> $DIR/mut_mut.rs:32:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:31:16 + --> $DIR/mut_mut.rs:32:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:36:37 + --> $DIR/mut_mut.rs:37:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:36:16 + --> $DIR/mut_mut.rs:37:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:36:21 + --> $DIR/mut_mut.rs:37:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index 17f2227ba91..86d899bb46c 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -1,4 +1,5 @@ // run-rustfix +// aux-build:proc_macros.rs #![feature(let_chains)] #![allow(unused)] #![allow( @@ -10,6 +11,8 @@ clippy::uninlined_format_args )] +extern crate proc_macros; + use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::rc::Rc; @@ -138,6 +141,7 @@ const fn in_const() -> &'static str { a } +#[proc_macros::inline_macros] fn does_not_lint() { let z; if false { @@ -195,35 +199,27 @@ fn does_not_lint() { } y = 3; - macro_rules! assign { - ($i:ident) => { - $i = 1; - }; - } let x; - assign!(x); + inline!($x = 1;); let x; if true { - assign!(x); + inline!($x = 1;); } else { x = 2; } - macro_rules! in_macro { - () => { - let x; - x = 1; + inline!({ + let x; + x = 1; - let x; - if true { - x = 1; - } else { - x = 2; - } - }; - } - in_macro!(); + let x; + if true { + x = 1; + } else { + x = 2; + } + }); // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613 let x; diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index d84457a2987..969afb38edf 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,4 +1,5 @@ // run-rustfix +// aux-build:proc_macros.rs #![feature(let_chains)] #![allow(unused)] #![allow( @@ -10,6 +11,8 @@ clippy::uninlined_format_args )] +extern crate proc_macros; + use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::rc::Rc; @@ -138,6 +141,7 @@ const fn in_const() -> &'static str { a } +#[proc_macros::inline_macros] fn does_not_lint() { let z; if false { @@ -195,35 +199,27 @@ fn does_not_lint() { } y = 3; - macro_rules! assign { - ($i:ident) => { - $i = 1; - }; - } let x; - assign!(x); + inline!($x = 1;); let x; if true { - assign!(x); + inline!($x = 1;); } else { x = 2; } - macro_rules! in_macro { - () => { - let x; - x = 1; + inline!({ + let x; + x = 1; - let x; - if true { - x = 1; - } else { - x = 2; - } - }; - } - in_macro!(); + let x; + if true { + x = 1; + } else { + x = 2; + } + }); // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613 let x; diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 0a256fb4a13..eff782f8bf1 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:24:5 + --> $DIR/needless_late_init.rs:27:5 | LL | let a; | ^^^^^^ created here @@ -13,7 +13,7 @@ LL | let a = "zero"; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:27:5 + --> $DIR/needless_late_init.rs:30:5 | LL | let b; | ^^^^^^ created here @@ -27,7 +27,7 @@ LL | let b = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:28:5 + --> $DIR/needless_late_init.rs:31:5 | LL | let c; | ^^^^^^ created here @@ -41,7 +41,7 @@ LL | let c = 2; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:32:5 + --> $DIR/needless_late_init.rs:35:5 | LL | let d: usize; | ^^^^^^^^^^^^^ created here @@ -54,7 +54,7 @@ LL | let d: usize = 1; | ~~~~~~~~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:35:5 + --> $DIR/needless_late_init.rs:38:5 | LL | let e; | ^^^^^^ created here @@ -67,7 +67,7 @@ LL | let e = format!("{}", d); | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:40:5 + --> $DIR/needless_late_init.rs:43:5 | LL | let a; | ^^^^^^ @@ -88,7 +88,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:49:5 + --> $DIR/needless_late_init.rs:52:5 | LL | let b; | ^^^^^^ @@ -109,7 +109,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:56:5 + --> $DIR/needless_late_init.rs:59:5 | LL | let d; | ^^^^^^ @@ -130,7 +130,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:64:5 + --> $DIR/needless_late_init.rs:67:5 | LL | let e; | ^^^^^^ @@ -151,7 +151,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:71:5 + --> $DIR/needless_late_init.rs:74:5 | LL | let f; | ^^^^^^ @@ -167,7 +167,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:77:5 + --> $DIR/needless_late_init.rs:80:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -187,7 +187,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:85:5 + --> $DIR/needless_late_init.rs:88:5 | LL | let x; | ^^^^^^ created here @@ -201,7 +201,7 @@ LL | let x = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:89:5 + --> $DIR/needless_late_init.rs:92:5 | LL | let x; | ^^^^^^ created here @@ -215,7 +215,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:93:5 + --> $DIR/needless_late_init.rs:96:5 | LL | let x; | ^^^^^^ created here @@ -229,7 +229,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:112:5 + --> $DIR/needless_late_init.rs:115:5 | LL | let a; | ^^^^^^ @@ -250,7 +250,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:129:5 + --> $DIR/needless_late_init.rs:132:5 | LL | let a; | ^^^^^^ diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed index f0f1f9298ac..e6ead69d148 100644 --- a/tests/ui/needless_lifetimes.fixed +++ b/tests/ui/needless_lifetimes.fixed @@ -1,5 +1,5 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::needless_lifetimes)] #![allow( @@ -12,8 +12,8 @@ clippy::get_first )] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::inline_macros; fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} @@ -502,30 +502,29 @@ mod pr_9743_output_lifetime_checks { } } +#[inline_macros] mod in_macro { - macro_rules! local_one_input_macro { - () => { - fn one_input(x: &u8) -> &u8 { - unimplemented!() - } - }; - } + use proc_macros::external; // lint local macro expands to function with needless lifetimes - local_one_input_macro!(); - - // no lint on external macro - macro_rules::needless_lifetime!(); - - macro_rules! expanded_lifetime { - ($l:lifetime) => { - fn f<$l>(arg: &$l str) -> &$l str { - arg - } + inline! { + fn one_input(x: &u8) -> &u8 { + unimplemented!() } } - expanded_lifetime!('a); + // no lint on external macro + external! { + fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + } + + inline! { + fn f<$'a>(arg: &$'a str) -> &$'a str { + arg + } + } } mod issue5787 { diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index ddfd1043003..06eb430506f 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,5 +1,5 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::needless_lifetimes)] #![allow( @@ -12,8 +12,8 @@ clippy::get_first )] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::inline_macros; fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} @@ -502,30 +502,29 @@ mod pr_9743_output_lifetime_checks { } } +#[inline_macros] mod in_macro { - macro_rules! local_one_input_macro { - () => { - fn one_input<'a>(x: &'a u8) -> &'a u8 { - unimplemented!() - } - }; - } + use proc_macros::external; // lint local macro expands to function with needless lifetimes - local_one_input_macro!(); - - // no lint on external macro - macro_rules::needless_lifetime!(); - - macro_rules! expanded_lifetime { - ($l:lifetime) => { - fn f<$l>(arg: &$l str) -> &$l str { - arg - } + inline! { + fn one_input<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() } } - expanded_lifetime!('a); + // no lint on external macro + external! { + fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + } + + inline! { + fn f<$'a>(arg: &$'a str) -> &$'a str { + arg + } + } } mod issue5787 { diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 4e3c8f20d8c..86acc4e0046 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -540,19 +540,16 @@ LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:508:13 + --> $DIR/needless_lifetimes.rs:511:9 | -LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | local_one_input_macro!(); - | ------------------------ in this macro invocation +LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_mod_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) help: elide the lifetimes | -LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { -LL + fn one_input(x: &u8) -> &u8 { +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { | error: aborting due to 46 previous errors diff --git a/tests/ui/option_env_unwrap.rs b/tests/ui/option_env_unwrap.rs index 0141fb7856d..9a56cf40d8a 100644 --- a/tests/ui/option_env_unwrap.rs +++ b/tests/ui/option_env_unwrap.rs @@ -1,24 +1,16 @@ -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::option_env_unwrap)] #![allow(clippy::map_flatten)] -#[macro_use] -extern crate macro_rules; - -macro_rules! option_env_unwrap { - ($env: expr) => { - option_env!($env).unwrap() - }; - ($env: expr, $message: expr) => { - option_env!($env).expect($message) - }; -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { let _ = option_env!("PATH").unwrap(); let _ = option_env!("PATH").expect("environment variable PATH isn't set"); - let _ = option_env_unwrap!("PATH"); - let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set"); - let _ = option_env_unwrap_external!("PATH"); - let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set"); + let _ = inline!(option_env!($"PATH").unwrap()); + let _ = inline!(option_env!($"PATH").expect($"environment variable PATH isn't set")); + let _ = external!(option_env!($"PATH").unwrap()); + let _ = external!(option_env!($"PATH").expect($"environment variable PATH isn't set")); } diff --git a/tests/ui/option_env_unwrap.stderr b/tests/ui/option_env_unwrap.stderr index bc188a07e9e..7bba62686ee 100644 --- a/tests/ui/option_env_unwrap.stderr +++ b/tests/ui/option_env_unwrap.stderr @@ -1,5 +1,5 @@ error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:18:13 + --> $DIR/option_env_unwrap.rs:10:13 | LL | let _ = option_env!("PATH").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = option_env!("PATH").unwrap(); = note: `-D clippy::option-env-unwrap` implied by `-D warnings` error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:19:13 + --> $DIR/option_env_unwrap.rs:11:13 | LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,46 +16,40 @@ LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set = help: consider using the `env!` macro instead error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:10:9 + --> $DIR/option_env_unwrap.rs:12:21 | -LL | option_env!($env).unwrap() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | let _ = option_env_unwrap!("PATH"); - | -------------------------- in this macro invocation +LL | let _ = inline!(option_env!($"PATH").unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using the `env!` macro instead - = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:13:9 + --> $DIR/option_env_unwrap.rs:13:21 | -LL | option_env!($env).expect($message) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set"); - | ----------------------------------------------------------------- in this macro invocation +LL | let _ = inline!(option_env!($"PATH").expect($"environment variable PATH isn't set")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using the `env!` macro instead - = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:22:13 + --> $DIR/option_env_unwrap.rs:14:13 | -LL | let _ = option_env_unwrap_external!("PATH"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = external!(option_env!($"PATH").unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using the `env!` macro instead - = note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `external` (in Nightly builds, run with -Z macro-backtrace for more info) error: this will panic at run-time if the environment variable doesn't exist at compile-time - --> $DIR/option_env_unwrap.rs:23:13 + --> $DIR/option_env_unwrap.rs:15:13 | -LL | let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = external!(option_env!($"PATH").expect($"environment variable PATH isn't set")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using the `env!` macro instead - = note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `external` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed index df36a9b842b..ee7b998a0b2 100644 --- a/tests/ui/ptr_as_ptr.fixed +++ b/tests/ui/ptr_as_ptr.fixed @@ -1,16 +1,12 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::ptr_as_ptr)] -extern crate macro_rules; - -macro_rules! cast_it { - ($ptr: ident) => { - $ptr.cast::() - }; -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -38,10 +34,10 @@ fn main() { let _: *mut i32 = mut_ptr.cast(); // Make sure the lint is triggered inside a macro - let _ = cast_it!(ptr); + let _ = inline!($ptr.cast::()); // Do not lint inside macros from external crates - let _ = macro_rules::ptr_as_ptr_cast!(ptr); + let _ = external!($ptr as *const i32); } #[clippy::msrv = "1.37"] diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs index 302c66462d9..c88329ce4ec 100644 --- a/tests/ui/ptr_as_ptr.rs +++ b/tests/ui/ptr_as_ptr.rs @@ -1,16 +1,12 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::ptr_as_ptr)] -extern crate macro_rules; - -macro_rules! cast_it { - ($ptr: ident) => { - $ptr as *const i32 - }; -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -38,10 +34,10 @@ fn main() { let _: *mut i32 = mut_ptr as _; // Make sure the lint is triggered inside a macro - let _ = cast_it!(ptr); + let _ = inline!($ptr as *const i32); // Do not lint inside macros from external crates - let _ = macro_rules::ptr_as_ptr_cast!(ptr); + let _ = external!($ptr as *const i32); } #[clippy::msrv = "1.37"] diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index a68e1cab6d3..78d733994ac 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -1,5 +1,5 @@ error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:18:13 + --> $DIR/ptr_as_ptr.rs:14:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` @@ -7,48 +7,45 @@ LL | let _ = ptr as *const i32; = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:19:13 + --> $DIR/ptr_as_ptr.rs:15:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:24:17 + --> $DIR/ptr_as_ptr.rs:20:17 | LL | let _ = *ptr_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:37:25 + --> $DIR/ptr_as_ptr.rs:33:25 | LL | let _: *const i32 = ptr as *const _; | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:38:23 + --> $DIR/ptr_as_ptr.rs:34:23 | LL | let _: *mut i32 = mut_ptr as _; | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:10:9 + --> $DIR/ptr_as_ptr.rs:37:21 | -LL | $ptr as *const i32 - | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::()` -... -LL | let _ = cast_it!(ptr); - | ------------- in this macro invocation +LL | let _ = inline!($ptr as *const i32); + | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::()` | - = note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:62:13 + --> $DIR/ptr_as_ptr.rs:58:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:63:13 + --> $DIR/ptr_as_ptr.rs:59:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 5d03f77e932..3c86f41f3a6 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,9 +1,9 @@ -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![warn(clippy::single_match_else)] #![allow(clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; +extern crate proc_macros; +use proc_macros::with_span; enum ExprNode { ExprAddrOf, diff --git a/tests/ui/string_add.rs b/tests/ui/string_add.rs index 16673c01e63..20edbe31fa9 100644 --- a/tests/ui/string_add.rs +++ b/tests/ui/string_add.rs @@ -1,7 +1,7 @@ -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::external; #[warn(clippy::string_add)] #[allow(clippy::string_add_assign, unused)] @@ -22,5 +22,8 @@ fn main() { x = x + 1; assert_eq!(2, x); - string_add!(); + external!({ + let y = "".to_owned(); + let z = y + "..."; + }); } diff --git a/tests/ui/toplevel_ref_arg.fixed b/tests/ui/toplevel_ref_arg.fixed index 09fb66ca37e..174c858a47d 100644 --- a/tests/ui/toplevel_ref_arg.fixed +++ b/tests/ui/toplevel_ref_arg.fixed @@ -1,17 +1,12 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::toplevel_ref_arg)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] -#[macro_use] -extern crate macro_rules; - -macro_rules! gen_binding { - () => { - let _y = &42; - }; -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { // Closures should not warn let y = |ref x| println!("{:?}", x); @@ -38,13 +33,8 @@ fn main() { for ref _x in 0..10 {} // lint in macro - #[allow(unused)] - { - gen_binding!(); - } + inline!(let _y = &42;); // do not lint in external macro - { - ref_arg_binding!(); - } + external!(let ref _y = 42;); } diff --git a/tests/ui/toplevel_ref_arg.rs b/tests/ui/toplevel_ref_arg.rs index 9d1f2f81098..4b81a06112f 100644 --- a/tests/ui/toplevel_ref_arg.rs +++ b/tests/ui/toplevel_ref_arg.rs @@ -1,17 +1,12 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::toplevel_ref_arg)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] -#[macro_use] -extern crate macro_rules; - -macro_rules! gen_binding { - () => { - let ref _y = 42; - }; -} +extern crate proc_macros; +use proc_macros::{external, inline_macros}; +#[inline_macros] fn main() { // Closures should not warn let y = |ref x| println!("{:?}", x); @@ -38,13 +33,8 @@ fn main() { for ref _x in 0..10 {} // lint in macro - #[allow(unused)] - { - gen_binding!(); - } + inline!(let ref _y = 42;); // do not lint in external macro - { - ref_arg_binding!(); - } + external!(let ref _y = 42;); } diff --git a/tests/ui/toplevel_ref_arg.stderr b/tests/ui/toplevel_ref_arg.stderr index 9c853020ab0..407c2d9fcd3 100644 --- a/tests/ui/toplevel_ref_arg.stderr +++ b/tests/ui/toplevel_ref_arg.stderr @@ -1,5 +1,5 @@ error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:20:9 + --> $DIR/toplevel_ref_arg.rs:15:9 | LL | let ref _x = 1; | ----^^^^^^----- help: try: `let _x = &1;` @@ -7,39 +7,36 @@ LL | let ref _x = 1; = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:22:9 + --> $DIR/toplevel_ref_arg.rs:17:9 | LL | let ref _y: (&_, u8) = (&1, 2); | ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:24:9 + --> $DIR/toplevel_ref_arg.rs:19:9 | LL | let ref _z = 1 + 2; | ----^^^^^^--------- help: try: `let _z = &(1 + 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:26:9 + --> $DIR/toplevel_ref_arg.rs:21:9 | LL | let ref mut _z = 1 + 2; | ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:31:9 + --> $DIR/toplevel_ref_arg.rs:26:9 | LL | let ref _x = vec![1, 2, 3]; | ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:11:13 + --> $DIR/toplevel_ref_arg.rs:36:17 | -LL | let ref _y = 42; - | ----^^^^^^------ help: try: `let _y = &42;` -... -LL | gen_binding!(); - | -------------- in this macro invocation +LL | inline!(let ref _y = 42;); + | ----^^^^^^------ help: try: `let _y = &42;` | - = note: this error originates in the macro `gen_binding` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.rs b/tests/ui/toplevel_ref_arg_non_rustfix.rs index 1a493fbce0e..2047593e7e4 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.rs +++ b/tests/ui/toplevel_ref_arg_non_rustfix.rs @@ -1,33 +1,27 @@ -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![warn(clippy::toplevel_ref_arg)] #![allow(unused)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; fn the_answer(ref mut x: u8) { *x = 42; } -macro_rules! gen_function { - () => { - fn fun_example(ref _x: usize) {} - }; -} - +#[inline_macros] fn main() { let mut x = 0; the_answer(x); // lint in macro - #[allow(unused)] - { - gen_function!(); + inline! { + fn fun_example(ref _x: usize) {} } // do not lint in external macro - { - ref_arg_function!(); + external! { + fn fun_example2(ref _x: usize) {} } } diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/tests/ui/toplevel_ref_arg_non_rustfix.stderr index e97011c7fd5..7307bd599d9 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -7,15 +7,12 @@ LL | fn the_answer(ref mut x: u8) { = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` error: `ref` directly on a function argument is ignored. Consider using a reference type instead - --> $DIR/toplevel_ref_arg_non_rustfix.rs:15:24 + --> $DIR/toplevel_ref_arg_non_rustfix.rs:20:24 | LL | fn fun_example(ref _x: usize) {} | ^^^^^^ -... -LL | gen_function!(); - | --------------- in this macro invocation | - = note: this error originates in the macro `gen_function` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 264194419c7..dc497b1690f 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -1,11 +1,11 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![deny(clippy::try_err)] #![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; use std::io; use std::task::Poll; @@ -79,36 +79,22 @@ fn nested_error() -> Result { Ok(1) } -// Bad suggestion when in macro (see #6242) -macro_rules! try_validation { - ($e: expr) => {{ - match $e { +#[inline_macros] +fn calling_macro() -> Result { + // macro + inline!( + match $(Ok::<_, i32>(5)) { Ok(_) => 0, Err(_) => return Err(1), } - }}; -} - -macro_rules! ret_one { - () => { - 1 - }; -} - -macro_rules! try_validation_in_macro { - ($e: expr) => {{ - match $e { - Ok(_) => 0, - Err(_) => return Err(ret_one!()), - } - }}; -} - -fn calling_macro() -> Result { - // macro - try_validation!(Ok::<_, i32>(5)); + ); // `Err` arg is another macro - try_validation_in_macro!(Ok::<_, i32>(5)); + inline!( + match $(Ok::<_, i32>(5)) { + Ok(_) => 0, + Err(_) => return Err(inline!(1)), + } + ); Ok(5) } @@ -121,24 +107,19 @@ fn main() { calling_macro().unwrap(); // We don't want to lint in external macros - try_err!(); -} - -macro_rules! bar { - () => { - String::from("aasdfasdfasdfa") - }; -} - -macro_rules! foo { - () => { - bar!() - }; + external! { + pub fn try_err_fn() -> Result { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { Err(err)? } else { Ok(2) } + } + } } +#[inline_macros] pub fn macro_inside(fail: bool) -> Result { if fail { - return Err(foo!()); + return Err(inline!(inline!(String::from("aasdfasdfasdfa")))); } Ok(0) } diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index bc6979bf457..86aeb75cd96 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -1,11 +1,11 @@ // run-rustfix -// aux-build:macro_rules.rs +// aux-build:proc_macros.rs #![deny(clippy::try_err)] #![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] -#[macro_use] -extern crate macro_rules; +extern crate proc_macros; +use proc_macros::{external, inline_macros}; use std::io; use std::task::Poll; @@ -79,36 +79,22 @@ fn nested_error() -> Result { Ok(1) } -// Bad suggestion when in macro (see #6242) -macro_rules! try_validation { - ($e: expr) => {{ - match $e { +#[inline_macros] +fn calling_macro() -> Result { + // macro + inline!( + match $(Ok::<_, i32>(5)) { Ok(_) => 0, Err(_) => Err(1)?, } - }}; -} - -macro_rules! ret_one { - () => { - 1 - }; -} - -macro_rules! try_validation_in_macro { - ($e: expr) => {{ - match $e { - Ok(_) => 0, - Err(_) => Err(ret_one!())?, - } - }}; -} - -fn calling_macro() -> Result { - // macro - try_validation!(Ok::<_, i32>(5)); + ); // `Err` arg is another macro - try_validation_in_macro!(Ok::<_, i32>(5)); + inline!( + match $(Ok::<_, i32>(5)) { + Ok(_) => 0, + Err(_) => Err(inline!(1))?, + } + ); Ok(5) } @@ -121,24 +107,19 @@ fn main() { calling_macro().unwrap(); // We don't want to lint in external macros - try_err!(); -} - -macro_rules! bar { - () => { - String::from("aasdfasdfasdfa") - }; -} - -macro_rules! foo { - () => { - bar!() - }; + external! { + pub fn try_err_fn() -> Result { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { Err(err)? } else { Ok(2) } + } + } } +#[inline_macros] pub fn macro_inside(fail: bool) -> Result { if fail { - Err(foo!())?; + Err(inline!(inline!(String::from("aasdfasdfasdfa"))))?; } Ok(0) } diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 0cb1328fbfc..4ad0e2e56a4 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -29,53 +29,47 @@ LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:87:23 + --> $DIR/try_err.rs:88:23 | LL | Err(_) => Err(1)?, | ^^^^^^^ help: try this: `return Err(1)` -... -LL | try_validation!(Ok::<_, i32>(5)); - | -------------------------------- in this macro invocation | - = note: this error originates in the macro `try_validation` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:102:23 + --> $DIR/try_err.rs:95:23 | -LL | Err(_) => Err(ret_one!())?, - | ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())` -... -LL | try_validation_in_macro!(Ok::<_, i32>(5)); - | ----------------------------------------- in this macro invocation +LL | Err(_) => Err(inline!(1))?, + | ^^^^^^^^^^^^^^^^ help: try this: `return Err(inline!(1))` | - = note: this error originates in the macro `try_validation_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:141:9 + --> $DIR/try_err.rs:122:9 | -LL | Err(foo!())?; - | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` +LL | Err(inline!(inline!(String::from("aasdfasdfasdfa"))))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Err(inline!(inline!(String::from("aasdfasdfasdfa"))))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:148:9 + --> $DIR/try_err.rs:129:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:150:9 + --> $DIR/try_err.rs:131:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:158:9 + --> $DIR/try_err.rs:139:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:167:16 + --> $DIR/try_err.rs:148:16 | LL | return Err(42)?; | ^^^^^^^^ help: try this: `Err(42)` diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index cbd5cc5fcee..1475d781c67 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -1,11 +1,11 @@ -// aux-build:proc_macro_with_span.rs +// aux-build:proc_macros.rs // run-rustfix #![warn(clippy::uninlined_format_args)] #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; +extern crate proc_macros; +use proc_macros::with_span; macro_rules! no_param_str { () => { diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs index cf0ea5be481..835afac393f 100644 --- a/tests/ui/uninlined_format_args.rs +++ b/tests/ui/uninlined_format_args.rs @@ -1,11 +1,11 @@ -// aux-build:proc_macro_with_span.rs +// aux-build:proc_macros.rs // run-rustfix #![warn(clippy::uninlined_format_args)] #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; +extern crate proc_macros; +use proc_macros::with_span; macro_rules! no_param_str { () => { diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 07e70873a81..674ae4f1df9 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,4 +1,4 @@ -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![warn(clippy::unit_arg)] #![allow(unused_must_use, unused_variables)] #![allow( @@ -13,9 +13,9 @@ clippy::unused_unit )] -extern crate proc_macro_with_span; +extern crate proc_macros; -use proc_macro_with_span::with_span; +use proc_macros::with_span; use std::fmt::Debug; fn foo(t: T) { diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index 22e9bd8bdc5..3b93800f8b7 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -1,12 +1,12 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::map_identity)] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; +extern crate proc_macros; +use proc_macros::with_span; struct Deep(Option); diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 8726d84a23f..2851c0c5190 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -1,12 +1,12 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs +// aux-build: proc_macros.rs #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::map_identity)] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; +extern crate proc_macros; +use proc_macros::with_span; struct Deep(Option); diff --git a/tests/ui/unnecessary_unsafety_doc.rs b/tests/ui/unnecessary_unsafety_doc.rs index c160e31afd3..431093ab369 100644 --- a/tests/ui/unnecessary_unsafety_doc.rs +++ b/tests/ui/unnecessary_unsafety_doc.rs @@ -1,10 +1,10 @@ -// aux-build:doc_unsafe_macros.rs +// aux-build:proc_macros.rs #![allow(clippy::let_unit_value)] #![warn(clippy::unnecessary_safety_doc)] -#[macro_use] -extern crate doc_unsafe_macros; +extern crate proc_macros; +use proc_macros::external; /// This is has no safety section, and does not need one either pub fn destroy_the_planet() { @@ -129,7 +129,11 @@ macro_rules! very_safe { very_safe!(); // we don't lint code from external macros -undocd_safe!(); +external!( + pub fn vey_oy() { + unimplemented!(); + } +); fn main() {} diff --git a/tests/ui/unnecessary_unsafety_doc.stderr b/tests/ui/unnecessary_unsafety_doc.stderr index 72898c93fa1..b0f20fdac5f 100644 --- a/tests/ui/unnecessary_unsafety_doc.stderr +++ b/tests/ui/unnecessary_unsafety_doc.stderr @@ -42,7 +42,7 @@ LL | very_safe!(); = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for safe trait have unnecessary `# Safety` section - --> $DIR/unnecessary_unsafety_doc.rs:147:1 + --> $DIR/unnecessary_unsafety_doc.rs:151:1 | LL | pub trait DocumentedSafeTraitWithImplementationHeader { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 8a9492aa03792f17c7b63b15d10ae9813daa4c4a Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Thu, 16 Feb 2023 17:47:28 +0800 Subject: [PATCH 19/47] enhance [`ifs_same_cond`] to lint same immutable method calls as well --- clippy_lints/src/copies.rs | 26 +++++++++++++++++++++++--- tests/ui/ifs_same_cond.rs | 10 ++++++++++ tests/ui/ifs_same_cond.stderr | 14 +++++++++++++- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index f10c35cde52..d729a5430c7 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -3,8 +3,8 @@ use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, sn use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ - capture_local_usage, eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, - is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, + capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence, + is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, }; use core::iter; use core::ops::ControlFlow; @@ -549,7 +549,27 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo /// Implementation of `IFS_SAME_COND`. fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { - for (i, j) in search_same(conds, |e| hash_expr(cx, e), |lhs, rhs| eq_expr_value(cx, lhs, rhs)) { + for (i, j) in search_same( + conds, + |e| hash_expr(cx, e), + |lhs, rhs| { + // If any side (ex. lhs) is a method call, and the caller is not mutable, + // then we can ignore side effects? + if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { + if path_to_local(caller) + .and_then(|hir_id| find_binding_init(cx, hir_id)) + .is_some() + { + // caller is not declared as mutable + SpanlessEq::new(cx).eq_expr(lhs, rhs) + } else { + false + } + } else { + eq_expr_value(cx, lhs, rhs) + } + }, + ) { span_lint_and_note( cx, IFS_SAME_COND, diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs index 9850fc0919e..e68ec6a8573 100644 --- a/tests/ui/ifs_same_cond.rs +++ b/tests/ui/ifs_same_cond.rs @@ -43,4 +43,14 @@ fn ifs_same_cond() { } } +fn issue10272() { + let a = String::from("ha"); + if a.contains("ah") { + } else if a.contains("ah") { + // Trigger this lint + } else if a.contains("ha") { + } else if a == "wow" { + } +} + fn main() {} diff --git a/tests/ui/ifs_same_cond.stderr b/tests/ui/ifs_same_cond.stderr index 4113087327a..9519f6904cb 100644 --- a/tests/ui/ifs_same_cond.stderr +++ b/tests/ui/ifs_same_cond.stderr @@ -35,5 +35,17 @@ note: same as this LL | if 2 * a == 1 { | ^^^^^^^^^^ -error: aborting due to 3 previous errors +error: this `if` has the same condition as a previous `if` + --> $DIR/ifs_same_cond.rs:49:15 + | +LL | } else if a.contains("ah") { + | ^^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/ifs_same_cond.rs:48:8 + | +LL | if a.contains("ah") { + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors From f0ae2b71ca4053db3b86c20e7d612ecc11d50ee2 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Fri, 24 Feb 2023 10:46:07 +0800 Subject: [PATCH 20/47] make [`ifs_same_cond`] use `ignore_interior_mutablility` configuration --- clippy_lints/src/copies.rs | 70 +++++++++++++++----- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/utils/conf.rs | 2 +- tests/ui-toml/ifs_same_cond/clippy.toml | 1 + tests/ui-toml/ifs_same_cond/ifs_same_cond.rs | 24 +++++++ tests/ui/ifs_same_cond.rs | 8 +++ 6 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 tests/ui-toml/ifs_same_cond/clippy.toml create mode 100644 tests/ui-toml/ifs_same_cond/ifs_same_cond.rs diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index d729a5430c7..39f8f7220f1 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -3,16 +3,19 @@ use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, sn use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ - capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence, - is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, + capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, + if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, }; use core::iter; use core::ops::ControlFlow; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_middle::query::Key; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Span, Symbol}; @@ -159,7 +162,19 @@ declare_clippy_lint! { "`if` statement with shared code in all blocks" } -declare_lint_pass!(CopyAndPaste => [ +pub struct CopyAndPaste { + ignore_interior_mutability: Vec, +} + +impl CopyAndPaste { + pub fn new(ignore_interior_mutability: Vec) -> Self { + Self { + ignore_interior_mutability, + } + } +} + +impl_lint_pass!(CopyAndPaste => [ IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, @@ -170,7 +185,14 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { let (conds, blocks) = if_sequence(expr); - lint_same_cond(cx, &conds); + let mut ignored_ty_ids = FxHashSet::default(); + for ignored_ty in &self.ignore_interior_mutability { + let path: Vec<&str> = ignored_ty.split("::").collect(); + for id in def_path_def_ids(cx, path.as_slice()) { + ignored_ty_ids.insert(id); + } + } + lint_same_cond(cx, &conds, &ignored_ty_ids); lint_same_fns_in_if_cond(cx, &conds); let all_same = !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); @@ -547,23 +569,41 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo }) } +fn method_caller_is_ignored_or_mutable( + cx: &LateContext<'_>, + caller_expr: &Expr<'_>, + ignored_ty_ids: &FxHashSet, +) -> bool { + let caller_ty = cx.typeck_results().expr_ty(caller_expr); + let is_ignored_ty = if let Some(adt_id) = caller_ty.ty_adt_id() && ignored_ty_ids.contains(&adt_id) { + true + } else { + false + }; + + if is_ignored_ty + || caller_ty.is_mutable_ptr() + || path_to_local(caller_expr) + .and_then(|hid| find_binding_init(cx, hid)) + .is_none() + { + return true; + } + + false +} + /// Implementation of `IFS_SAME_COND`. -fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { +fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &FxHashSet) { for (i, j) in search_same( conds, |e| hash_expr(cx, e), |lhs, rhs| { - // If any side (ex. lhs) is a method call, and the caller is not mutable, - // then we can ignore side effects? if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { - if path_to_local(caller) - .and_then(|hir_id| find_binding_init(cx, hir_id)) - .is_some() - { - // caller is not declared as mutable - SpanlessEq::new(cx).eq_expr(lhs, rhs) - } else { + if method_caller_is_ignored_or_mutable(cx, caller, ignored_ty_ids) { false + } else { + SpanlessEq::new(cx).eq_expr(lhs, rhs) } } else { eq_expr_value(cx, lhs, rhs) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 491732be208..bde84686cc1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -656,7 +656,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::new(regex::Regex)); - store.register_late_pass(|_| Box::new(copies::CopyAndPaste)); + let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); + store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); store.register_late_pass(|_| Box::new(format::UselessFormat)); store.register_late_pass(|_| Box::new(swap::Swap)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 1c7f3e96db8..8ba252425a3 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -437,7 +437,7 @@ define_Conf! { /// /// The maximum size of the `Err`-variant in a `Result` returned from a function (large_error_threshold: u64 = 128), - /// Lint: MUTABLE_KEY_TYPE. + /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND. /// /// A list of paths to types that should be treated like `Arc`, i.e. ignored but /// for the generic parameters for determining interior mutability diff --git a/tests/ui-toml/ifs_same_cond/clippy.toml b/tests/ui-toml/ifs_same_cond/clippy.toml new file mode 100644 index 00000000000..1615d970c68 --- /dev/null +++ b/tests/ui-toml/ifs_same_cond/clippy.toml @@ -0,0 +1 @@ +ignore-interior-mutability = ["std::cell::Cell"] \ No newline at end of file diff --git a/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs new file mode 100644 index 00000000000..92438e7d1f2 --- /dev/null +++ b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs @@ -0,0 +1,24 @@ +#![warn(clippy::ifs_same_cond)] +#![allow(clippy::if_same_then_else, clippy::comparison_chain)] + +fn main() {} + +fn issue10272() { + use std::cell::Cell; + + let x = Cell::new(true); + if x.get() { + } else if !x.take() { + } else if x.get() { + // ok, x is interior mutable type + } else { + } + + let a = [Cell::new(true)]; + if a[0].get() { + } else if a[0].take() { + } else if a[0].get() { + // ok, a contains interior mutable type + } else { + } +} diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs index e68ec6a8573..ae91611c472 100644 --- a/tests/ui/ifs_same_cond.rs +++ b/tests/ui/ifs_same_cond.rs @@ -51,6 +51,14 @@ fn issue10272() { } else if a.contains("ha") { } else if a == "wow" { } + + let p: *mut i8 = std::ptr::null_mut(); + if p.is_null() { + } else if p.align_offset(0) == 0 { + } else if p.is_null() { + // ok, p is mutable pointer + } else { + } } fn main() {} From f4ccb06d699b91b8de8b797a584ce185d6856ec2 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Mon, 13 Mar 2023 20:17:30 +0800 Subject: [PATCH 21/47] extract `is_interior_mutable_type` from [`mut_key`] to `clippy_utils::ty`; fix configuration of [`ifs_same_cond`]; add some style improvement for [`ifs_same_cond`]; --- clippy_lints/src/copies.rs | 52 +++++++--------- clippy_lints/src/mut_key.rs | 60 ++++--------------- clippy_utils/src/ty.rs | 44 ++++++++++++++ tests/ui-toml/ifs_same_cond/clippy.toml | 2 +- tests/ui-toml/ifs_same_cond/ifs_same_cond.rs | 12 +--- .../ifs_same_cond/ifs_same_cond.stderr | 15 +++++ tests/ui/ifs_same_cond.rs | 8 +++ 7 files changed, 107 insertions(+), 86 deletions(-) create mode 100644 tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 39f8f7220f1..970f5004993 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; -use clippy_utils::ty::needs_ordered_drop; +use clippy_utils::ty::{is_interior_mut_ty, needs_ordered_drop}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, @@ -8,9 +8,8 @@ use clippy_utils::{ }; use core::iter; use core::ops::ControlFlow; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::DefIdSet; use rustc_hir::intravisit; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -164,12 +163,14 @@ declare_clippy_lint! { pub struct CopyAndPaste { ignore_interior_mutability: Vec, + ignored_ty_ids: DefIdSet, } impl CopyAndPaste { pub fn new(ignore_interior_mutability: Vec) -> Self { Self { ignore_interior_mutability, + ignored_ty_ids: DefIdSet::new(), } } } @@ -182,17 +183,18 @@ impl_lint_pass!(CopyAndPaste => [ ]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + for ignored_ty in &self.ignore_interior_mutability { + let path: Vec<&str> = ignored_ty.split("::").collect(); + for id in def_path_def_ids(cx, path.as_slice()) { + self.ignored_ty_ids.insert(id); + } + } + } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { let (conds, blocks) = if_sequence(expr); - let mut ignored_ty_ids = FxHashSet::default(); - for ignored_ty in &self.ignore_interior_mutability { - let path: Vec<&str> = ignored_ty.split("::").collect(); - for id in def_path_def_ids(cx, path.as_slice()) { - ignored_ty_ids.insert(id); - } - } - lint_same_cond(cx, &conds, &ignored_ty_ids); + lint_same_cond(cx, &conds, &self.ignored_ty_ids); lint_same_fns_in_if_cond(cx, &conds); let all_same = !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); @@ -569,38 +571,30 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo }) } -fn method_caller_is_ignored_or_mutable( - cx: &LateContext<'_>, - caller_expr: &Expr<'_>, - ignored_ty_ids: &FxHashSet, -) -> bool { +fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignored_ty_ids: &DefIdSet) -> bool { let caller_ty = cx.typeck_results().expr_ty(caller_expr); - let is_ignored_ty = if let Some(adt_id) = caller_ty.ty_adt_id() && ignored_ty_ids.contains(&adt_id) { - true - } else { - false - }; + // Check if given type has inner mutability and was not set to ignored by the configuration + let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty) + && !matches!(caller_ty.ty_adt_id(), Some(adt_id) if ignored_ty_ids.contains(&adt_id)); - if is_ignored_ty + is_inner_mut_ty || caller_ty.is_mutable_ptr() + // `find_binding_init` will return the binding iff its not mutable || path_to_local(caller_expr) .and_then(|hid| find_binding_init(cx, hid)) .is_none() - { - return true; - } - - false } /// Implementation of `IFS_SAME_COND`. -fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &FxHashSet) { +fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &DefIdSet) { for (i, j) in search_same( conds, |e| hash_expr(cx, e), |lhs, rhs| { + // Ignore eq_expr side effects iff one of the expressin kind is a method call + // and the caller is not a mutable, including inner mutable type. if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { - if method_caller_is_ignored_or_mutable(cx, caller, ignored_ty_ids) { + if method_caller_is_mutable(cx, caller, ignored_ty_ids) { false } else { SpanlessEq::new(cx).eq_expr(lhs, rhs) diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 8aa814b7405..309f67521a3 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_interior_mut_ty; use clippy_utils::{def_path_def_ids, trait_ref_of_method}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty}; +use rustc_middle::query::Key; +use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -153,53 +154,18 @@ impl MutableKeyType { let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] .iter() .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); - if is_keyed_type && self.is_interior_mutable_type(cx, substs.type_at(0)) { + if !is_keyed_type { + return; + } + + let subst_ty = substs.type_at(0); + // Determines if a type contains interior mutability which would affect its implementation of + // [`Hash`] or [`Ord`]. + if is_interior_mut_ty(cx, subst_ty) + && !matches!(subst_ty.ty_adt_id(), Some(adt_id) if self.ignore_mut_def_ids.contains(&adt_id)) + { span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); } } } - - /// Determines if a type contains interior mutability which would affect its implementation of - /// [`Hash`] or [`Ord`]. - fn is_interior_mutable_type<'tcx>(&self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match *ty.kind() { - Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty), - Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty), - Array(inner_ty, size) => { - size.try_eval_target_usize(cx.tcx, cx.param_env) - .map_or(true, |u| u != 0) - && self.is_interior_mutable_type(cx, inner_ty) - }, - Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)), - Adt(def, substs) => { - // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to - // that of their type parameters. Note: we don't include `HashSet` and `HashMap` - // because they have no impl for `Hash` or `Ord`. - let def_id = def.did(); - let is_std_collection = [ - sym::Option, - sym::Result, - sym::LinkedList, - sym::Vec, - sym::VecDeque, - sym::BTreeMap, - sym::BTreeSet, - sym::Rc, - sym::Arc, - ] - .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id)); - let is_box = Some(def_id) == cx.tcx.lang_items().owned_box(); - if is_std_collection || is_box || self.ignore_mut_def_ids.contains(&def_id) { - // The type is mutable if any of its type parameters are - substs.types().any(|ty| self.is_interior_mutable_type(cx, ty)) - } else { - !ty.has_escaping_bound_vars() - && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() - && !ty.is_freeze(cx.tcx, cx.param_env) - } - }, - _ => false, - } - } } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index e0ea3952785..f1c6f1dddd8 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -1121,3 +1121,47 @@ pub fn make_normalized_projection<'tcx>( } helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?) } + +/// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`] +/// etc. +pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match *ty.kind() { + ty::Ref(_, inner_ty, mutbl) => mutbl == Mutability::Mut || is_interior_mut_ty(cx, inner_ty), + ty::Slice(inner_ty) => is_interior_mut_ty(cx, inner_ty), + ty::Array(inner_ty, size) => { + size.try_eval_target_usize(cx.tcx, cx.param_env) + .map_or(true, |u| u != 0) + && is_interior_mut_ty(cx, inner_ty) + }, + ty::Tuple(fields) => fields.iter().any(|ty| is_interior_mut_ty(cx, ty)), + ty::Adt(def, substs) => { + // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to + // that of their type parameters. Note: we don't include `HashSet` and `HashMap` + // because they have no impl for `Hash` or `Ord`. + let def_id = def.did(); + let is_std_collection = [ + sym::Option, + sym::Result, + sym::LinkedList, + sym::Vec, + sym::VecDeque, + sym::BTreeMap, + sym::BTreeSet, + sym::Rc, + sym::Arc, + ] + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id)); + let is_box = Some(def_id) == cx.tcx.lang_items().owned_box(); + if is_std_collection || is_box { + // The type is mutable if any of its type parameters are + substs.types().any(|ty| is_interior_mut_ty(cx, ty)) + } else { + !ty.has_escaping_bound_vars() + && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() + && !ty.is_freeze(cx.tcx, cx.param_env) + } + }, + _ => false, + } +} diff --git a/tests/ui-toml/ifs_same_cond/clippy.toml b/tests/ui-toml/ifs_same_cond/clippy.toml index 1615d970c68..90a36ecd920 100644 --- a/tests/ui-toml/ifs_same_cond/clippy.toml +++ b/tests/ui-toml/ifs_same_cond/clippy.toml @@ -1 +1 @@ -ignore-interior-mutability = ["std::cell::Cell"] \ No newline at end of file +ignore-interior-mutability = ["std::cell::Cell"] diff --git a/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs index 92438e7d1f2..d623ac7e020 100644 --- a/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs +++ b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs @@ -6,19 +6,13 @@ fn main() {} fn issue10272() { use std::cell::Cell; + // Because the `ignore-interior-mutability` configuration + // is set to ignore for `std::cell::Cell`, the following `get()` calls + // should trigger warning let x = Cell::new(true); if x.get() { } else if !x.take() { } else if x.get() { - // ok, x is interior mutable type - } else { - } - - let a = [Cell::new(true)]; - if a[0].get() { - } else if a[0].take() { - } else if a[0].get() { - // ok, a contains interior mutable type } else { } } diff --git a/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr b/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr new file mode 100644 index 00000000000..2841f62bc94 --- /dev/null +++ b/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr @@ -0,0 +1,15 @@ +error: this `if` has the same condition as a previous `if` + --> $DIR/ifs_same_cond.rs:15:15 + | +LL | } else if x.get() { + | ^^^^^^^ + | +note: same as this + --> $DIR/ifs_same_cond.rs:13:8 + | +LL | if x.get() { + | ^^^^^^^ + = note: `-D clippy::ifs-same-cond` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs index ae91611c472..9ce9a87626a 100644 --- a/tests/ui/ifs_same_cond.rs +++ b/tests/ui/ifs_same_cond.rs @@ -59,6 +59,14 @@ fn issue10272() { // ok, p is mutable pointer } else { } + + let x = std::cell::Cell::new(true); + if x.get() { + } else if !x.take() { + } else if x.get() { + // ok, x is interior mutable type + } else { + } } fn main() {} From 555f56862e10cda545c9b7de21f68c7d62266c35 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 9 Mar 2023 17:31:10 +0000 Subject: [PATCH 22/47] Fix semicolon insertion in `match_single_binding` --- .../src/matches/match_single_binding.rs | 21 ++-- tests/ui/match_single_binding.fixed | 41 +++++-- tests/ui/match_single_binding.rs | 55 ++++++++- tests/ui/match_single_binding.stderr | 111 +++++++++++++++--- tests/ui/match_single_binding2.fixed | 4 +- tests/ui/match_single_binding2.stderr | 4 +- 6 files changed, 192 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index eec5c1143d4..89da7a55cbd 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -3,7 +3,7 @@ use clippy_utils::macros::HirNode; use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_applicability}; use clippy_utils::{get_parent_expr, is_refutable, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -24,22 +24,27 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let bind_names = arms[0].pat.span; let match_body = peel_blocks(arms[0].body); let mut app = Applicability::MaybeIncorrect; - let (snippet_body, from_macro) = snippet_block_with_context( + let mut snippet_body = snippet_block_with_context( cx, match_body.span, arms[0].span.ctxt(), "..", Some(expr.span), &mut app, - ); - let mut snippet_body = snippet_body.to_string(); + ) + .0 + .to_string(); // Do we need to add ';' to suggestion ? - if matches!(match_body.kind, ExprKind::Block(..)) { - // macro + expr_ty(body) == () - if from_macro && cx.typeck_results().expr_ty(match_body).is_unit() { - snippet_body.push(';'); + if let Node::Stmt(stmt) = cx.tcx.hir().get_parent(expr.hir_id) + && let StmtKind::Expr(_) = stmt.kind + && match match_body.kind { + // We don't need to add a ; to blocks, unless that block is from a macro expansion + ExprKind::Block(block, _) => block.span.from_expansion(), + _ => true, } + { + snippet_body.push(';'); } match arms[0].pat.kind { diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 6cfb6661a03..201301cc9b7 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::match_single_binding)] -#![allow(unused_variables)] -#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)] +#![allow( + unused, + clippy::let_unit_value, + clippy::no_effect, + clippy::toplevel_ref_arg, + clippy::uninlined_format_args +)] struct Point { x: i32, @@ -109,10 +114,9 @@ fn main() { // Lint let x = 1; - println!("Not an array index start"); + println!("Not an array index start") } -#[allow(dead_code)] fn issue_8723() { let (mut val, idx) = ("a b", 1); @@ -125,16 +129,15 @@ fn issue_8723() { let _ = val; } -#[allow(dead_code)] +fn side_effects() {} + fn issue_9575() { - fn side_effects() {} let _ = || { side_effects(); - println!("Needs curlies"); + println!("Needs curlies") }; } -#[allow(dead_code)] fn issue_9725(r: Option) { let x = r; match x { @@ -146,3 +149,25 @@ fn issue_9725(r: Option) { }, }; } + +fn issue_10447() -> usize { + (); + + let a = (); + + side_effects(); + + let b = side_effects(); + + println!("1"); + + let c = println!("1"); + + let in_expr = [ + (), + side_effects(), + println!("1"), + ]; + + 2 +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index f188aeb5f2f..8b047b19ce9 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::match_single_binding)] -#![allow(unused_variables)] -#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)] +#![allow( + unused, + clippy::let_unit_value, + clippy::no_effect, + clippy::toplevel_ref_arg, + clippy::uninlined_format_args +)] struct Point { x: i32, @@ -127,7 +132,6 @@ fn main() { } } -#[allow(dead_code)] fn issue_8723() { let (mut val, idx) = ("a b", 1); @@ -141,15 +145,14 @@ fn issue_8723() { let _ = val; } -#[allow(dead_code)] +fn side_effects() {} + fn issue_9575() { - fn side_effects() {} let _ = || match side_effects() { _ => println!("Needs curlies"), }; } -#[allow(dead_code)] fn issue_9725(r: Option) { match r { x => match x { @@ -162,3 +165,43 @@ fn issue_9725(r: Option) { }, }; } + +fn issue_10447() -> usize { + match 1 { + _ => (), + } + + let a = match 1 { + _ => (), + }; + + match 1 { + _ => side_effects(), + } + + let b = match 1 { + _ => side_effects(), + }; + + match 1 { + _ => println!("1"), + } + + let c = match 1 { + _ => println!("1"), + }; + + let in_expr = [ + match 1 { + _ => (), + }, + match 1 { + _ => side_effects(), + }, + match 1 { + _ => println!("1"), + }, + ]; + + 2 +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index e960d64ad2b..9d16af76c6a 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -1,5 +1,5 @@ error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:28:5 + --> $DIR/match_single_binding.rs:33:5 | LL | / match (a, b, c) { LL | | (x, y, z) => { @@ -18,7 +18,7 @@ LL + } | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:34:5 + --> $DIR/match_single_binding.rs:39:5 | LL | / match (a, b, c) { LL | | (x, y, z) => println!("{} {} {}", x, y, z), @@ -32,7 +32,7 @@ LL + println!("{} {} {}", x, y, z); | error: this match could be replaced by its body itself - --> $DIR/match_single_binding.rs:51:5 + --> $DIR/match_single_binding.rs:56:5 | LL | / match a { LL | | _ => println!("whatever"), @@ -40,7 +40,7 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("whatever");` error: this match could be replaced by its body itself - --> $DIR/match_single_binding.rs:55:5 + --> $DIR/match_single_binding.rs:60:5 | LL | / match a { LL | | _ => { @@ -59,7 +59,7 @@ LL + } | error: this match could be replaced by its body itself - --> $DIR/match_single_binding.rs:62:5 + --> $DIR/match_single_binding.rs:67:5 | LL | / match a { LL | | _ => { @@ -81,7 +81,7 @@ LL + } | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:72:5 + --> $DIR/match_single_binding.rs:77:5 | LL | / match p { LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), @@ -95,7 +95,7 @@ LL + println!("Coords: ({}, {})", x, y); | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:76:5 + --> $DIR/match_single_binding.rs:81:5 | LL | / match p { LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), @@ -109,7 +109,7 @@ LL + println!("Coords: ({}, {})", x1, y1); | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:81:5 + --> $DIR/match_single_binding.rs:86:5 | LL | / match x { LL | | ref r => println!("Got a reference to {}", r), @@ -123,7 +123,7 @@ LL + println!("Got a reference to {}", r); | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:86:5 + --> $DIR/match_single_binding.rs:91:5 | LL | / match x { LL | | ref mut mr => println!("Got a mutable reference to {}", mr), @@ -137,7 +137,7 @@ LL + println!("Got a mutable reference to {}", mr); | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:90:5 + --> $DIR/match_single_binding.rs:95:5 | LL | / let product = match coords() { LL | | Point { x, y } => x * y, @@ -151,7 +151,7 @@ LL + let product = x * y; | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:98:18 + --> $DIR/match_single_binding.rs:103:18 | LL | .map(|i| match i.unwrap() { | __________________^ @@ -168,16 +168,16 @@ LL ~ }) | error: this match could be replaced by its body itself - --> $DIR/match_single_binding.rs:124:5 + --> $DIR/match_single_binding.rs:129:5 | LL | / match x { LL | | // => LL | | _ => println!("Not an array index start"), LL | | } - | |_____^ help: consider using the match body instead: `println!("Not an array index start");` + | |_____^ help: consider using the match body instead: `println!("Not an array index start")` error: this assignment could be simplified - --> $DIR/match_single_binding.rs:134:5 + --> $DIR/match_single_binding.rs:138:5 | LL | / val = match val.split_at(idx) { LL | | (pre, suf) => { @@ -197,7 +197,7 @@ LL ~ }; | error: this match could be replaced by its scrutinee and body - --> $DIR/match_single_binding.rs:147:16 + --> $DIR/match_single_binding.rs:151:16 | LL | let _ = || match side_effects() { | ________________^ @@ -209,12 +209,12 @@ help: consider using the scrutinee and body instead | LL ~ let _ = || { LL + side_effects(); -LL + println!("Needs curlies"); +LL + println!("Needs curlies") LL ~ }; | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:154:5 + --> $DIR/match_single_binding.rs:157:5 | LL | / match r { LL | | x => match x { @@ -238,5 +238,80 @@ LL + }, LL ~ }; | -error: aborting due to 15 previous errors +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:170:5 + | +LL | / match 1 { +LL | | _ => (), +LL | | } + | |_____^ help: consider using the match body instead: `();` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:174:13 + | +LL | let a = match 1 { + | _____________^ +LL | | _ => (), +LL | | }; + | |_____^ help: consider using the match body instead: `()` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:178:5 + | +LL | / match 1 { +LL | | _ => side_effects(), +LL | | } + | |_____^ help: consider using the match body instead: `side_effects();` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:182:13 + | +LL | let b = match 1 { + | _____________^ +LL | | _ => side_effects(), +LL | | }; + | |_____^ help: consider using the match body instead: `side_effects()` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:186:5 + | +LL | / match 1 { +LL | | _ => println!("1"), +LL | | } + | |_____^ help: consider using the match body instead: `println!("1");` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:190:13 + | +LL | let c = match 1 { + | _____________^ +LL | | _ => println!("1"), +LL | | }; + | |_____^ help: consider using the match body instead: `println!("1")` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:195:9 + | +LL | / match 1 { +LL | | _ => (), +LL | | }, + | |_________^ help: consider using the match body instead: `()` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:198:9 + | +LL | / match 1 { +LL | | _ => side_effects(), +LL | | }, + | |_________^ help: consider using the match body instead: `side_effects()` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:201:9 + | +LL | / match 1 { +LL | | _ => println!("1"), +LL | | }, + | |_________^ help: consider using the match body instead: `println!("1")` + +error: aborting due to 24 previous errors diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index 6a7db67e311..e3cf56a4293 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -30,7 +30,7 @@ fn main() { #[rustfmt::skip] Some((first, _second)) => { let (a, b) = get_tup(); - println!("a {:?} and b {:?}", a, b); + println!("a {:?} and b {:?}", a, b) }, None => println!("nothing"), } @@ -49,5 +49,5 @@ fn main() { 0 => 1, _ => 2, }; - println!("Single branch"); + println!("Single branch") } diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index 22bf7d8be4a..e180b93e76d 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -27,7 +27,7 @@ LL | | } help: consider using a `let` statement | LL ~ let (a, b) = get_tup(); -LL + println!("a {:?} and b {:?}", a, b); +LL + println!("a {:?} and b {:?}", a, b) | error: this match could be replaced by its scrutinee and body @@ -61,7 +61,7 @@ LL ~ match x { LL + 0 => 1, LL + _ => 2, LL + }; -LL + println!("Single branch"); +LL + println!("Single branch") | error: aborting due to 4 previous errors From 6631480a7b7cb7b3e7d53c9c4cd7915f7ebc3110 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Mon, 13 Mar 2023 19:13:56 +0100 Subject: [PATCH 23/47] Fix false positive with `a = a` --- clippy_lints/src/swap.rs | 1 + tests/ui/swap.fixed | 8 ++++++++ tests/ui/swap.rs | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 1aeac724ab1..5087c19e5f3 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -190,6 +190,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { && first.span.eq_ctxt(second.span) && is_same(cx, lhs0, rhs1) && is_same(cx, lhs1, rhs0) + && !is_same(cx, lhs1, rhs1) // Ignore a = b; a = a (#10421) && let Some(lhs_sugg) = match &lhs0 { ExprOrIdent::Expr(expr) => Sugg::hir_opt(cx, expr), ExprOrIdent::Ident(ident) => Some(Sugg::NonParen(ident.as_str().into())), diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 04008c0d9b3..775b0dbde88 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -186,3 +186,11 @@ const fn issue_9864(mut u: u32) -> u32 { v = temp; u + v } + +#[allow(clippy::let_and_return)] +const fn issue_10421(x: u32) -> u32 { + let a = x; + let a = a; + let a = a; + a +} diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index ef8a81c8341..bc9a78b49c7 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -215,3 +215,11 @@ const fn issue_9864(mut u: u32) -> u32 { v = temp; u + v } + +#[allow(clippy::let_and_return)] +const fn issue_10421(x: u32) -> u32 { + let a = x; + let a = a; + let a = a; + a +} From 011bb463370aceee33d7f00a39d87ae8fc856a1c Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Tue, 14 Mar 2023 10:24:28 +0800 Subject: [PATCH 24/47] update lint configuration doc for [`ifs_same_cond`] --- book/src/lint_configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 995dd2f04b1..9ed6627b741 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -519,6 +519,7 @@ for the generic parameters for determining interior mutability **Default Value:** `["bytes::Bytes"]` (`Vec`) * [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) +* [ifs_same_cond](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond) ### allow-mixed-uninlined-format-args From 5924b46543853ea8d8bbbe886a09036c4fa5a087 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 14 Mar 2023 12:44:23 +0200 Subject: [PATCH 25/47] Fix documentation for `derived_hash_with_manual_eq` The documentation retained "vice versa" from the previous incarnation of the lint but the lint itself no longer lints against manual `Hash` implementations with a derived `PartialEq`. I also adjusted the documentation for `PartialOrd`-`Ord` lint as "vice versa" seemed a little confusing to me there (as to what it was refering to exactly.) --- clippy_lints/src/derive.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index b8428d66a5d..1a9dad47839 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -24,8 +24,8 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Checks for deriving `Hash` but implementing `PartialEq` - /// explicitly or vice versa. + /// Lints against manual `PartialEq` implementations for types with a derived `Hash` + /// implementation. /// /// ### Why is this bad? /// The implementation of these traits must agree (for @@ -54,8 +54,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for deriving `Ord` but implementing `PartialOrd` - /// explicitly or vice versa. + /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord` + /// or `PartialOrd` implementation. /// /// ### Why is this bad? /// The implementation of these traits must agree (for From f2eddc5924756bbef97e0a02f5120139ca74ecb6 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Tue, 14 Mar 2023 17:18:26 +0000 Subject: [PATCH 26/47] Remove box expressions from HIR --- clippy_lints/src/infinite_iter.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 3 +-- clippy_lints/src/matches/significant_drop_in_scrutinee.rs | 1 - clippy_lints/src/methods/unnecessary_sort_by.rs | 4 ---- clippy_lints/src/no_effect.rs | 6 ++---- clippy_lints/src/shadow.rs | 3 +-- clippy_lints/src/significant_drop_tightening.rs | 1 - clippy_lints/src/utils/author.rs | 5 ----- clippy_utils/src/check_proc_macro.rs | 1 - clippy_utils/src/eager_or_lazy.rs | 3 +-- clippy_utils/src/hir_utils.rs | 3 +-- clippy_utils/src/sugg.rs | 1 - clippy_utils/src/visitors.rs | 1 - 13 files changed, 7 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index d1d2db27c6f..fe28c526be3 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -167,7 +167,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { Finite }, ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), - ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), + ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), ExprKind::Call(path, _) => { if let ExprKind::Path(ref qpath) = path.kind { cx.qpath_res(qpath, path.hir_id) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index b1bc10802e1..f0a1b1dfe56 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -124,8 +124,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t #[allow(clippy::too_many_lines)] fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { - ExprKind::Box(e) - | ExprKind::Unary(_, e) + ExprKind::Unary(_, e) | ExprKind::Cast(e, _) | ExprKind::Type(e, _) | ExprKind::Field(e, _) diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index b33a2478172..04225beeb70 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -321,7 +321,6 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { self.has_significant_drop = true; } } - ExprKind::Box(..) | ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Unary(..) | diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 5201da52bbf..67618f7038a 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -33,10 +33,6 @@ struct SortByKeyDetection { /// contains a and the other replaces it with b) fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool { match (&a_expr.kind, &b_expr.kind) { - // Two boxes with mirrored contents - (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { - mirrored_exprs(left_expr, a_ident, right_expr, b_ident) - }, // Two arrays with mirrored contents (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => { iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident)) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 79c1ae4861e..e3712190e67 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -127,8 +127,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | ExprKind::Type(inner, _) | ExprKind::Unary(_, inner) | ExprKind::Field(inner, _) - | ExprKind::AddrOf(_, _, inner) - | ExprKind::Box(inner) => has_no_effect(cx, inner), + | ExprKind::AddrOf(_, _, inner) => has_no_effect(cx, inner), ExprKind::Struct(_, fields, ref base) => { !has_drop(cx, cx.typeck_results().expr_ty(expr)) && fields.iter().all(|field| has_no_effect(cx, field.expr)) @@ -234,8 +233,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + | ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), ExprKind::Struct(_, fields, ref base) => { if has_drop(cx, cx.typeck_results().expr_ty(expr)) { None diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 87f966ced0d..ae7d19624ba 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -213,8 +213,7 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_ } loop { expr = match expr.kind { - ExprKind::Box(e) - | ExprKind::AddrOf(_, _, e) + ExprKind::AddrOf(_, _, e) | ExprKind::Block( &Block { stmts: [], diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index e2d90edec5a..e12681c0a0c 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -380,7 +380,6 @@ impl<'cx, 'sdt, 'tcx> Visitor<'tcx> for SigDropFinder<'cx, 'sdt, 'tcx> { | hir::ExprKind::Assign(..) | hir::ExprKind::AssignOp(..) | hir::ExprKind::Binary(..) - | hir::ExprKind::Box(..) | hir::ExprKind::Call(..) | hir::ExprKind::Field(..) | hir::ExprKind::If(..) diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index f31c3fdb095..bc4adf1596d 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -395,11 +395,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } self.expr(field!(let_expr.init)); }, - ExprKind::Box(inner) => { - bind!(self, inner); - kind!("Box({inner})"); - self.expr(inner); - }, ExprKind::Array(elements) => { bind!(self, elements); kind!("Array({elements})"); diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 43f0df145f0..d3a6929f67e 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -112,7 +112,6 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) { /// Get the search patterns to use for the given expression fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) { match e.kind { - ExprKind::Box(e) => (Pat::Str("box"), expr_search_pat(tcx, e).1), ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")), ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1), diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index ee2f816f181..babbc7294a1 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -199,8 +199,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, // Memory allocation, custom operator, loop, or call to an unknown function - ExprKind::Box(_) - | ExprKind::Unary(..) + ExprKind::Unary(..) | ExprKind::Binary(..) | ExprKind::Loop(..) | ExprKind::Call(..) => self.eagerness = Lazy, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 0603755f8a9..3a6d23ca5c1 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -249,7 +249,6 @@ impl HirEqInterExpr<'_, '_, '_> { both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) && both(le, re, |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Box(l), &ExprKind::Box(r)) => self.eq_expr(l, r), (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) }, @@ -628,7 +627,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(j); } }, - ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { + ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { self.hash_expr(e); }, ExprKind::Call(fun, args) => { diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 85bf28b708b..44cb5d5756a 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -133,7 +133,6 @@ impl<'a> Sugg<'a> { match expr.kind { hir::ExprKind::AddrOf(..) - | hir::ExprKind::Box(..) | hir::ExprKind::If(..) | hir::ExprKind::Let(..) | hir::ExprKind::Closure { .. } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index d27a20bd4df..86a93f64fb7 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -600,7 +600,6 @@ pub fn for_each_unconsumed_temporary<'tcx, B>( helper(typeck, false, e, f)?; }, ExprKind::Block(&Block { expr: Some(e), .. }, _) - | ExprKind::Box(e) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => { helper(typeck, true, e, f)?; From 3eea49446bae42709b96bd36a0eb2c0966b6b4ac Mon Sep 17 00:00:00 2001 From: blyxyas Date: Tue, 14 Mar 2023 20:59:39 +0100 Subject: [PATCH 27/47] Ignore external macros --- clippy_lints/src/swap.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 5087c19e5f3..938ca3a6cee 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -6,7 +6,8 @@ use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core} use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; @@ -75,7 +76,9 @@ declare_lint_pass!(Swap => [MANUAL_SWAP, ALMOST_SWAPPED]); impl<'tcx> LateLintPass<'tcx> for Swap { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { check_manual_swap(cx, block); - check_suspicious_swap(cx, block); + if !in_external_macro(cx.sess(), block.span) { + check_suspicious_swap(cx, block); + } check_xor_swap(cx, block); } } From e2ba75d69da533775f8db90a3348521458cf4fed Mon Sep 17 00:00:00 2001 From: blyxyas Date: Wed, 15 Mar 2023 16:27:46 +0100 Subject: [PATCH 28/47] Add macro test --- tests/ui/swap.fixed | 13 +++++++++++-- tests/ui/swap.rs | 13 +++++++++++-- tests/ui/swap.stderr | 34 +++++++++++++++++----------------- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 775b0dbde88..78d8eedc38a 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -8,7 +8,8 @@ redundant_semicolons, dead_code, unused_assignments, - unused_variables + unused_variables, + clippy::let_and_return )] struct Foo(u32); @@ -187,8 +188,16 @@ const fn issue_9864(mut u: u32) -> u32 { u + v } -#[allow(clippy::let_and_return)] +macro_rules! issue_10421 { + () => { + let a = 1; + let b = a; + let b = b; + }; +} + const fn issue_10421(x: u32) -> u32 { + issue_10421!(); let a = x; let a = a; let a = a; diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index bc9a78b49c7..c995af8ecf9 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -8,7 +8,8 @@ redundant_semicolons, dead_code, unused_assignments, - unused_variables + unused_variables, + clippy::let_and_return )] struct Foo(u32); @@ -216,8 +217,16 @@ const fn issue_9864(mut u: u32) -> u32 { u + v } -#[allow(clippy::let_and_return)] +macro_rules! issue_10421 { + () => { + let a = 1; + let b = a; + let b = b; + }; +} + const fn issue_10421(x: u32) -> u32 { + issue_10421!(); let a = x; let a = a; let a = a; diff --git a/tests/ui/swap.stderr b/tests/ui/swap.stderr index 825c9261e19..e9dacb3119e 100644 --- a/tests/ui/swap.stderr +++ b/tests/ui/swap.stderr @@ -1,5 +1,5 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually - --> $DIR/swap.rs:25:5 + --> $DIR/swap.rs:26:5 | LL | / let temp = bar.a; LL | | bar.a = bar.b; @@ -10,7 +10,7 @@ LL | | bar.b = temp; = note: `-D clippy::manual-swap` implied by `-D warnings` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:37:5 + --> $DIR/swap.rs:38:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -18,7 +18,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:46:5 + --> $DIR/swap.rs:47:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -26,7 +26,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:65:5 + --> $DIR/swap.rs:66:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -34,7 +34,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `a` and `b` manually - --> $DIR/swap.rs:76:5 + --> $DIR/swap.rs:77:5 | LL | / a ^= b; LL | | b ^= a; @@ -42,7 +42,7 @@ LL | | a ^= b; | |___________^ help: try: `std::mem::swap(&mut a, &mut b);` error: this looks like you are swapping `bar.a` and `bar.b` manually - --> $DIR/swap.rs:84:5 + --> $DIR/swap.rs:85:5 | LL | / bar.a ^= bar.b; LL | | bar.b ^= bar.a; @@ -50,7 +50,7 @@ LL | | bar.a ^= bar.b; | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:92:5 + --> $DIR/swap.rs:93:5 | LL | / foo[0] ^= foo[1]; LL | | foo[1] ^= foo[0]; @@ -58,7 +58,7 @@ LL | | foo[0] ^= foo[1]; | |_____________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually - --> $DIR/swap.rs:121:5 + --> $DIR/swap.rs:122:5 | LL | / let temp = foo[0][1]; LL | | foo[0][1] = bar[1][0]; @@ -68,7 +68,7 @@ LL | | bar[1][0] = temp; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `a` and `b` manually - --> $DIR/swap.rs:135:7 + --> $DIR/swap.rs:136:7 | LL | ; let t = a; | _______^ @@ -79,7 +79,7 @@ LL | | b = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `c.0` and `a` manually - --> $DIR/swap.rs:144:7 + --> $DIR/swap.rs:145:7 | LL | ; let t = c.0; | _______^ @@ -90,7 +90,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `b` and `a` manually - --> $DIR/swap.rs:170:5 + --> $DIR/swap.rs:171:5 | LL | / let t = b; LL | | b = a; @@ -100,7 +100,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:132:5 + --> $DIR/swap.rs:133:5 | LL | / a = b; LL | | b = a; @@ -110,7 +110,7 @@ LL | | b = a; = note: `-D clippy::almost-swapped` implied by `-D warnings` error: this looks like you are trying to swap `c.0` and `a` - --> $DIR/swap.rs:141:5 + --> $DIR/swap.rs:142:5 | LL | / c.0 = a; LL | | a = c.0; @@ -119,7 +119,7 @@ LL | | a = c.0; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:148:5 + --> $DIR/swap.rs:149:5 | LL | / let a = b; LL | | let b = a; @@ -128,7 +128,7 @@ LL | | let b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `d` and `c` - --> $DIR/swap.rs:153:5 + --> $DIR/swap.rs:154:5 | LL | / d = c; LL | | c = d; @@ -137,7 +137,7 @@ LL | | c = d; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:157:5 + --> $DIR/swap.rs:158:5 | LL | / let a = b; LL | | b = a; @@ -146,7 +146,7 @@ LL | | b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `s.0.x` and `s.0.y` manually - --> $DIR/swap.rs:205:5 + --> $DIR/swap.rs:206:5 | LL | / let t = s.0.x; LL | | s.0.x = s.0.y; From 4b9cb857f9b6da4e3b274601a6c439aac8a2c425 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Wed, 15 Mar 2023 23:18:25 +0100 Subject: [PATCH 29/47] Rename lint --- CHANGELOG.md | 2 +- .../src/{allow_attribute.rs => allow_attributes.rs} | 6 +++--- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 4 ++-- tests/ui/{allow_attribute.fixed => allow_attributes.fixed} | 2 +- tests/ui/{allow_attribute.rs => allow_attributes.rs} | 2 +- .../ui/{allow_attribute.stderr => allow_attributes.stderr} | 6 +++--- 7 files changed, 12 insertions(+), 12 deletions(-) rename clippy_lints/src/{allow_attribute.rs => allow_attributes.rs} (95%) rename tests/ui/{allow_attribute.fixed => allow_attributes.fixed} (93%) rename tests/ui/{allow_attribute.rs => allow_attributes.rs} (93%) rename tests/ui/{allow_attribute.stderr => allow_attributes.stderr} (69%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b00fd962ca..c090a844858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4382,7 +4382,7 @@ Released 2018-09-13 [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core -[`allow_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attribute +[`allow_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes [`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason [`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range [`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range diff --git a/clippy_lints/src/allow_attribute.rs b/clippy_lints/src/allow_attributes.rs similarity index 95% rename from clippy_lints/src/allow_attribute.rs rename to clippy_lints/src/allow_attributes.rs index 28fc45d0554..3d660a11ea3 100644 --- a/clippy_lints/src/allow_attribute.rs +++ b/clippy_lints/src/allow_attributes.rs @@ -40,12 +40,12 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "1.69.0"] - pub ALLOW_ATTRIBUTE, + pub ALLOW_ATTRIBUTES, restriction, "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." } -declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTE]); +declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]); impl LateLintPass<'_> for AllowAttribute { // Separate each crate's features. @@ -58,7 +58,7 @@ impl LateLintPass<'_> for AllowAttribute { then { span_lint_and_sugg( cx, - ALLOW_ATTRIBUTE, + ALLOW_ATTRIBUTES, ident.span, "#[allow] attribute found", "replace it with", "expect".into() diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 73f74d59f4a..25f15973490 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -35,7 +35,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, - crate::allow_attribute::ALLOW_ATTRIBUTE_INFO, + crate::allow_attributes::ALLOW_ATTRIBUTES_INFO, crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, crate::as_conversions::AS_CONVERSIONS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a3c65a69c99..c455c3e3058 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -67,7 +67,7 @@ mod declared_lints; mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` -mod allow_attribute; +mod allow_attributes; mod almost_complete_range; mod approx_const; mod as_conversions; @@ -934,7 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock)); store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); - store.register_late_pass(|_| Box::new(allow_attribute::AllowAttribute)); + store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/allow_attribute.fixed b/tests/ui/allow_attributes.fixed similarity index 93% rename from tests/ui/allow_attribute.fixed rename to tests/ui/allow_attributes.fixed index 01759ced09d..b8dd0619e6d 100644 --- a/tests/ui/allow_attribute.fixed +++ b/tests/ui/allow_attributes.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(unused)] -#![warn(clippy::allow_attribute)] +#![warn(clippy::allow_attributes)] #![feature(lint_reasons)] fn main() {} diff --git a/tests/ui/allow_attribute.rs b/tests/ui/allow_attributes.rs similarity index 93% rename from tests/ui/allow_attribute.rs rename to tests/ui/allow_attributes.rs index b8e341fe9e4..295f560906a 100644 --- a/tests/ui/allow_attribute.rs +++ b/tests/ui/allow_attributes.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(unused)] -#![warn(clippy::allow_attribute)] +#![warn(clippy::allow_attributes)] #![feature(lint_reasons)] fn main() {} diff --git a/tests/ui/allow_attribute.stderr b/tests/ui/allow_attributes.stderr similarity index 69% rename from tests/ui/allow_attribute.stderr rename to tests/ui/allow_attributes.stderr index 58b7323b068..681837e9ed7 100644 --- a/tests/ui/allow_attribute.stderr +++ b/tests/ui/allow_attributes.stderr @@ -1,13 +1,13 @@ error: #[allow] attribute found - --> $DIR/allow_attribute.rs:11:3 + --> $DIR/allow_attributes.rs:11:3 | LL | #[allow(dead_code)] | ^^^^^ help: replace it with: `expect` | - = note: `-D clippy::allow-attribute` implied by `-D warnings` + = note: `-D clippy::allow-attributes` implied by `-D warnings` error: #[allow] attribute found - --> $DIR/allow_attribute.rs:20:30 + --> $DIR/allow_attributes.rs:20:30 | LL | #[cfg_attr(panic = "unwind", allow(dead_code))] | ^^^^^ help: replace it with: `expect` From 1cab0cbaf0dddb4cd403c4a0c391315801a22835 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Wed, 15 Mar 2023 23:20:52 +0100 Subject: [PATCH 30/47] Fix formatting, remove commented code, etc... --- clippy_lints/src/allow_attributes.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/allow_attributes.rs b/clippy_lints/src/allow_attributes.rs index 3d660a11ea3..15d46e954a9 100644 --- a/clippy_lints/src/allow_attributes.rs +++ b/clippy_lints/src/allow_attributes.rs @@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// Detects uses of the `#[allow]` attribute and suggests replacing it with + /// Detects uses of the `#[allow]` attribute and suggests replacing it with /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) /// /// The expect attribute is still unstable and requires the `lint_reasons` @@ -28,11 +28,11 @@ declare_clippy_lint! { /// fn foo() -> usize { /// let mut a = Vec::new(); /// a.len() - ///} + /// } /// ``` /// Use instead: /// ```rust,ignore - /// # #![feature(lint_reasons)] + /// #![feature(lint_reasons)] /// #[expect(unused_mut)] /// fn foo() -> usize { /// let mut a = Vec::new(); @@ -61,18 +61,10 @@ impl LateLintPass<'_> for AllowAttribute { ALLOW_ATTRIBUTES, ident.span, "#[allow] attribute found", - "replace it with", "expect".into() - // format!("expect{}", snippet( - // cx, - // ident.span - // .with_lo( - // ident.span.hi() + BytePos(2) // Cut *( - // ) - // .with_hi( - // attr.meta().unwrap().span.hi() - BytePos(1) // Cut ) - // ) - // , "...")) - , Applicability::MachineApplicable); + "replace it with", + "expect".into(), + Applicability::MachineApplicable, + ); } } } From d1b28f3a5f6c9836a3a1018427739a5a0cfc3f06 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 16 Mar 2023 12:08:07 +0100 Subject: [PATCH 31/47] Fix clippy. --- clippy_utils/src/macros.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index e135bd9feee..c0e32068eca 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -533,6 +533,14 @@ struct FormatArgsValues<'tcx> { } impl<'tcx> FormatArgsValues<'tcx> { + fn new_empty(format_string_span: SpanData) -> Self { + Self { + value_args: Vec::new(), + pos_to_value_index: Vec::new(), + format_string_span, + } + } + fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self { let mut pos_to_value_index = Vec::new(); let mut value_args = Vec::new(); @@ -997,12 +1005,13 @@ impl<'tcx> FormatArgsExpn<'tcx> { .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?; let newline = macro_name == sym::format_args_nl; + // ::core::fmt::Arguments::new_const(pieces) // ::core::fmt::Arguments::new_v1(pieces, args) // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg) - if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind + if let ExprKind::Call(callee, [pieces, rest @ ..]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind && let TyKind::Path(QPath::LangItem(LangItem::FormatArguments, _, _)) = ty.kind - && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted") + && matches!(seg.ident.as_str(), "new_const" | "new_v1" | "new_v1_formatted") { let format_string = FormatString::new(cx, pieces)?; @@ -1026,7 +1035,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { return None; } - let positions = if let Some(fmt_arg) = rest.first() { + let positions = if let Some(fmt_arg) = rest.get(1) { // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse // them. @@ -1042,7 +1051,11 @@ impl<'tcx> FormatArgsExpn<'tcx> { })) }; - let values = FormatArgsValues::new(args, format_string.span.data()); + let values = if let Some(args) = rest.first() { + FormatArgsValues::new(args, format_string.span.data()) + } else { + FormatArgsValues::new_empty(format_string.span.data()) + }; let args = izip!(positions, parsed_args, parser.arg_places) .map(|(position, parsed_arg, arg_span)| { From afe27ba1a2fbf902ecb5bd02590871be5f46f34d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 5 Mar 2023 12:38:23 +0100 Subject: [PATCH 32/47] Issue function modifiers in the right order in manual_async_fn lint --- clippy_lints/src/manual_async_fn.rs | 14 +++++++-- tests/ui/manual_async_fn.fixed | 6 ++++ tests/ui/manual_async_fn.rs | 12 ++++++++ tests/ui/manual_async_fn.stderr | 47 ++++++++++++++++++++++++++++- 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 3778eb4c732..af52090d8a4 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, - ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind, + ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, span: Span, - _: LocalDefId, + def_id: LocalDefId, ) { if_chain! { if let Some(header) = kind.header(); @@ -60,6 +60,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let ExprKind::Block(block, _) = body.value.kind; if block.stmts.is_empty(); if let Some(closure_body) = desugared_async_block(cx, block); + if let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = + cx.tcx.hir().get_by_def_id(def_id); then { let header_span = span.with_hi(ret_ty.span.hi()); @@ -70,15 +72,21 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { "this function can be simplified using the `async fn` syntax", |diag| { if_chain! { + if let Some(vis_snip) = snippet_opt(cx, *vis_span); if let Some(header_snip) = snippet_opt(cx, header_span); if let Some(ret_pos) = position_before_rarrow(&header_snip); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { + let header_snip = if !vis_snip.is_empty() { + format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) + } else { + format!("async {}", &header_snip[..ret_pos]) + }; let help = format!("make the function `async` and {ret_sugg}"); diag.span_suggestion( header_span, help, - format!("async {}{ret_snip}", &header_snip[..ret_pos]), + format!("{header_snip}{ret_snip}"), Applicability::MachineApplicable ); diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index b7e46a4a8cc..5cc4a43af7e 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -107,4 +107,10 @@ mod issue_5765 { } } +pub async fn issue_10450() -> i32 { 42 } + +pub(crate) async fn issue_10450_2() -> i32 { 42 } + +pub(self) async fn issue_10450_3() -> i32 { 42 } + fn main() {} diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index b05429da662..ba504b8a823 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -127,4 +127,16 @@ mod issue_5765 { } } +pub fn issue_10450() -> impl Future { + async { 42 } +} + +pub(crate) fn issue_10450_2() -> impl Future { + async { 42 } +} + +pub(self) fn issue_10450_3() -> impl Future { + async { 42 } +} + fn main() {} diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index 0a903ed6fd4..f5ee3eb7ccc 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -161,5 +161,50 @@ help: move the body of the async block to the enclosing function LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { 42 } | ~~~~~~ -error: aborting due to 10 previous errors +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:130:1 + | +LL | pub fn issue_10450() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | pub async fn issue_10450() -> i32 { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: move the body of the async block to the enclosing function + | +LL | pub fn issue_10450() -> impl Future { 42 } + | ~~~~~~ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:134:1 + | +LL | pub(crate) fn issue_10450_2() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | pub(crate) async fn issue_10450_2() -> i32 { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: move the body of the async block to the enclosing function + | +LL | pub(crate) fn issue_10450_2() -> impl Future { 42 } + | ~~~~~~ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:138:1 + | +LL | pub(self) fn issue_10450_3() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | pub(self) async fn issue_10450_3() -> i32 { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: move the body of the async block to the enclosing function + | +LL | pub(self) fn issue_10450_3() -> impl Future { 42 } + | ~~~~~~ + +error: aborting due to 13 previous errors From 9546517a842f31be2258a70ae940dd9071e2913a Mon Sep 17 00:00:00 2001 From: blyxyas Date: Thu, 16 Mar 2023 19:48:23 +0100 Subject: [PATCH 33/47] Add external macro test + Now it works --- clippy_lints/src/swap.rs | 5 ++--- tests/ui/auxiliary/macro_rules.rs | 10 +++++++++ tests/ui/swap.fixed | 10 +++------ tests/ui/swap.rs | 10 +++------ tests/ui/swap.stderr | 34 +++++++++++++++---------------- 5 files changed, 35 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 938ca3a6cee..50298898d03 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -76,9 +76,7 @@ declare_lint_pass!(Swap => [MANUAL_SWAP, ALMOST_SWAPPED]); impl<'tcx> LateLintPass<'tcx> for Swap { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { check_manual_swap(cx, block); - if !in_external_macro(cx.sess(), block.span) { - check_suspicious_swap(cx, block); - } + check_suspicious_swap(cx, block); check_xor_swap(cx, block); } } @@ -191,6 +189,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { if let Some((lhs0, rhs0)) = parse(first) && let Some((lhs1, rhs1)) = parse(second) && first.span.eq_ctxt(second.span) + && !in_external_macro(&cx.sess(), first.span) && is_same(cx, lhs0, rhs1) && is_same(cx, lhs1, rhs0) && !is_same(cx, lhs1, rhs1) // Ignore a = b; a = a (#10421) diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 1dc92c1b92b..a9bb61451dc 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -27,3 +27,13 @@ macro_rules! mut_mut { let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32; }; } + +#[macro_export] +macro_rules! issue_10421 { + () => { + let mut a = 1; + let mut b = 2; + a = b; + b = a; + }; +} diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 78d8eedc38a..9703674d1a4 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -1,4 +1,5 @@ // run-rustfix +// aux-build: macro_rules.rs #![warn(clippy::all)] #![allow( @@ -188,13 +189,8 @@ const fn issue_9864(mut u: u32) -> u32 { u + v } -macro_rules! issue_10421 { - () => { - let a = 1; - let b = a; - let b = b; - }; -} +#[macro_use] +extern crate macro_rules; const fn issue_10421(x: u32) -> u32 { issue_10421!(); diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index c995af8ecf9..a0228065e46 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -1,4 +1,5 @@ // run-rustfix +// aux-build: macro_rules.rs #![warn(clippy::all)] #![allow( @@ -217,13 +218,8 @@ const fn issue_9864(mut u: u32) -> u32 { u + v } -macro_rules! issue_10421 { - () => { - let a = 1; - let b = a; - let b = b; - }; -} +#[macro_use] +extern crate macro_rules; const fn issue_10421(x: u32) -> u32 { issue_10421!(); diff --git a/tests/ui/swap.stderr b/tests/ui/swap.stderr index e9dacb3119e..0c246268499 100644 --- a/tests/ui/swap.stderr +++ b/tests/ui/swap.stderr @@ -1,5 +1,5 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually - --> $DIR/swap.rs:26:5 + --> $DIR/swap.rs:27:5 | LL | / let temp = bar.a; LL | | bar.a = bar.b; @@ -10,7 +10,7 @@ LL | | bar.b = temp; = note: `-D clippy::manual-swap` implied by `-D warnings` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:38:5 + --> $DIR/swap.rs:39:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -18,7 +18,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:47:5 + --> $DIR/swap.rs:48:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -26,7 +26,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:66:5 + --> $DIR/swap.rs:67:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -34,7 +34,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `a` and `b` manually - --> $DIR/swap.rs:77:5 + --> $DIR/swap.rs:78:5 | LL | / a ^= b; LL | | b ^= a; @@ -42,7 +42,7 @@ LL | | a ^= b; | |___________^ help: try: `std::mem::swap(&mut a, &mut b);` error: this looks like you are swapping `bar.a` and `bar.b` manually - --> $DIR/swap.rs:85:5 + --> $DIR/swap.rs:86:5 | LL | / bar.a ^= bar.b; LL | | bar.b ^= bar.a; @@ -50,7 +50,7 @@ LL | | bar.a ^= bar.b; | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:93:5 + --> $DIR/swap.rs:94:5 | LL | / foo[0] ^= foo[1]; LL | | foo[1] ^= foo[0]; @@ -58,7 +58,7 @@ LL | | foo[0] ^= foo[1]; | |_____________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually - --> $DIR/swap.rs:122:5 + --> $DIR/swap.rs:123:5 | LL | / let temp = foo[0][1]; LL | | foo[0][1] = bar[1][0]; @@ -68,7 +68,7 @@ LL | | bar[1][0] = temp; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `a` and `b` manually - --> $DIR/swap.rs:136:7 + --> $DIR/swap.rs:137:7 | LL | ; let t = a; | _______^ @@ -79,7 +79,7 @@ LL | | b = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `c.0` and `a` manually - --> $DIR/swap.rs:145:7 + --> $DIR/swap.rs:146:7 | LL | ; let t = c.0; | _______^ @@ -90,7 +90,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `b` and `a` manually - --> $DIR/swap.rs:171:5 + --> $DIR/swap.rs:172:5 | LL | / let t = b; LL | | b = a; @@ -100,7 +100,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:133:5 + --> $DIR/swap.rs:134:5 | LL | / a = b; LL | | b = a; @@ -110,7 +110,7 @@ LL | | b = a; = note: `-D clippy::almost-swapped` implied by `-D warnings` error: this looks like you are trying to swap `c.0` and `a` - --> $DIR/swap.rs:142:5 + --> $DIR/swap.rs:143:5 | LL | / c.0 = a; LL | | a = c.0; @@ -119,7 +119,7 @@ LL | | a = c.0; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:149:5 + --> $DIR/swap.rs:150:5 | LL | / let a = b; LL | | let b = a; @@ -128,7 +128,7 @@ LL | | let b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `d` and `c` - --> $DIR/swap.rs:154:5 + --> $DIR/swap.rs:155:5 | LL | / d = c; LL | | c = d; @@ -137,7 +137,7 @@ LL | | c = d; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:158:5 + --> $DIR/swap.rs:159:5 | LL | / let a = b; LL | | b = a; @@ -146,7 +146,7 @@ LL | | b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `s.0.x` and `s.0.y` manually - --> $DIR/swap.rs:206:5 + --> $DIR/swap.rs:207:5 | LL | / let t = s.0.x; LL | | s.0.x = s.0.y; From ffabdab8cf1499e1857d9ebcac2c410220137025 Mon Sep 17 00:00:00 2001 From: "Samuel \"Sam\" Tardieu" Date: Sat, 11 Mar 2023 10:20:44 +0100 Subject: [PATCH 34/47] New lint to detect `&std::path::MAIN_SEPARATOR.to_string()` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/manual_main_separator_str.rs | 72 +++++++++++++++++++ clippy_utils/src/msrvs.rs | 1 + clippy_utils/src/paths.rs | 1 + tests/ui/manual_main_separator_str.fixed | 39 ++++++++++ tests/ui/manual_main_separator_str.rs | 39 ++++++++++ tests/ui/manual_main_separator_str.stderr | 28 ++++++++ 9 files changed, 184 insertions(+) create mode 100644 clippy_lints/src/manual_main_separator_str.rs create mode 100644 tests/ui/manual_main_separator_str.fixed create mode 100644 tests/ui/manual_main_separator_str.rs create mode 100644 tests/ui/manual_main_separator_str.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index c090a844858..47a503510c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4662,6 +4662,7 @@ Released 2018-09-13 [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else +[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 25f15973490..2331e857b1f 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -263,6 +263,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_clamp::MANUAL_CLAMP_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, + crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, crate::manual_retain::MANUAL_RETAIN_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4bb0e2ec96c..100cba45679 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -180,6 +180,7 @@ mod manual_bits; mod manual_clamp; mod manual_is_ascii_check; mod manual_let_else; +mod manual_main_separator_str; mod manual_non_exhaustive; mod manual_rem_euclid; mod manual_retain; @@ -936,6 +937,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock)); store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute)); + store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv()))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs new file mode 100644 index 00000000000..a6d318f952c --- /dev/null +++ b/clippy_lints/src/manual_main_separator_str.rs @@ -0,0 +1,72 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{is_trait_method, match_def_path, paths, peel_hir_expr_refs}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for references on `std::path::MAIN_SEPARATOR.to_string()` used + /// to build a `&str`. + /// + /// ### Why is this bad? + /// There exists a `std::path::MAIN_SEPARATOR_STR` which does not require + /// an extra memory allocation. + /// + /// ### Example + /// ```rust + /// let s: &str = &std::path::MAIN_SEPARATOR.to_string(); + /// ``` + /// Use instead: + /// ```rust + /// let s: &str = std::path::MAIN_SEPARATOR_STR; + /// ``` + #[clippy::version = "1.70.0"] + pub MANUAL_MAIN_SEPARATOR_STR, + complexity, + "`&std::path::MAIN_SEPARATOR.to_string()` can be replaced by `std::path::MAIN_SEPARATOR_STR`" +} + +pub struct ManualMainSeparatorStr { + msrv: Msrv, +} + +impl ManualMainSeparatorStr { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]); + +impl LateLintPass<'_> for ManualMainSeparatorStr { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) && + let (target, _) = peel_hir_expr_refs(expr) && + is_trait_method(cx, target, sym::ToString) && + let ExprKind::MethodCall(path, receiver, &[], _) = target.kind && + path.ident.name == sym::to_string && + let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind && + let Res::Def(DefKind::Const, receiver_def_id) = path.res && + match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR) && + let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() && + ty.is_str() + { + span_lint_and_sugg( + cx, + MANUAL_MAIN_SEPARATOR_STR, + expr.span, + "taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`", + "replace with", + "std::path::MAIN_SEPARATOR_STR".to_owned(), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index dbf9f3b621d..e05de2dc99c 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -19,6 +19,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { + 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 4aae0f7284e..c919575bfe9 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -67,6 +67,7 @@ pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"]; pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; +pub const PATH_MAIN_SEPARATOR: [&str; 3] = ["std", "path", "MAIN_SEPARATOR"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"]; pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; diff --git a/tests/ui/manual_main_separator_str.fixed b/tests/ui/manual_main_separator_str.fixed new file mode 100644 index 00000000000..50f46d6b355 --- /dev/null +++ b/tests/ui/manual_main_separator_str.fixed @@ -0,0 +1,39 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::manual_main_separator_str)] + +use std::path::MAIN_SEPARATOR; + +fn len(s: &str) -> usize { + s.len() +} + +struct U<'a> { + f: &'a str, + g: &'a String, +} + +struct V { + f: T, +} + +fn main() { + // Should lint + let _: &str = std::path::MAIN_SEPARATOR_STR; + let _ = len(std::path::MAIN_SEPARATOR_STR); + let _: Vec = std::path::MAIN_SEPARATOR_STR.encode_utf16().collect(); + + // Should lint for field `f` only + let _ = U { + f: std::path::MAIN_SEPARATOR_STR, + g: &MAIN_SEPARATOR.to_string(), + }; + + // Should not lint + let _: &String = &MAIN_SEPARATOR.to_string(); + let _ = &MAIN_SEPARATOR.to_string(); + let _ = V { + f: &MAIN_SEPARATOR.to_string(), + }; +} diff --git a/tests/ui/manual_main_separator_str.rs b/tests/ui/manual_main_separator_str.rs new file mode 100644 index 00000000000..2dbb9e66151 --- /dev/null +++ b/tests/ui/manual_main_separator_str.rs @@ -0,0 +1,39 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::manual_main_separator_str)] + +use std::path::MAIN_SEPARATOR; + +fn len(s: &str) -> usize { + s.len() +} + +struct U<'a> { + f: &'a str, + g: &'a String, +} + +struct V { + f: T, +} + +fn main() { + // Should lint + let _: &str = &MAIN_SEPARATOR.to_string(); + let _ = len(&MAIN_SEPARATOR.to_string()); + let _: Vec = MAIN_SEPARATOR.to_string().encode_utf16().collect(); + + // Should lint for field `f` only + let _ = U { + f: &MAIN_SEPARATOR.to_string(), + g: &MAIN_SEPARATOR.to_string(), + }; + + // Should not lint + let _: &String = &MAIN_SEPARATOR.to_string(); + let _ = &MAIN_SEPARATOR.to_string(); + let _ = V { + f: &MAIN_SEPARATOR.to_string(), + }; +} diff --git a/tests/ui/manual_main_separator_str.stderr b/tests/ui/manual_main_separator_str.stderr new file mode 100644 index 00000000000..e6cefde66a7 --- /dev/null +++ b/tests/ui/manual_main_separator_str.stderr @@ -0,0 +1,28 @@ +error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String` + --> $DIR/manual_main_separator_str.rs:23:19 + | +LL | let _: &str = &MAIN_SEPARATOR.to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR` + | + = note: `-D clippy::manual-main-separator-str` implied by `-D warnings` + +error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String` + --> $DIR/manual_main_separator_str.rs:24:17 + | +LL | let _ = len(&MAIN_SEPARATOR.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR` + +error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String` + --> $DIR/manual_main_separator_str.rs:25:23 + | +LL | let _: Vec = MAIN_SEPARATOR.to_string().encode_utf16().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR` + +error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String` + --> $DIR/manual_main_separator_str.rs:29:12 + | +LL | f: &MAIN_SEPARATOR.to_string(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR` + +error: aborting due to 4 previous errors + From dec6ef8a74db38ff80669255911ce7fc541d4c44 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 19 Mar 2023 13:42:12 +0100 Subject: [PATCH 35/47] Correct active members link in `CONTRIBUTING.md` --- CONTRIBUTING.md | 2 +- triagebot.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3158080d2b3..3df13280369 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,7 +50,7 @@ a [developer guide] and is a good place to start your journey. All issues on Clippy are mentored, if you want help simply ask someone from the Clippy team directly by mentioning them in the issue or over on [Zulip]. All currently active team members can be found -[here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3) +[here](https://github.com/rust-lang/rust-clippy/blob/master/triagebot.toml#L18) Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy issues. You can use `@rustbot claim` to assign the issue to yourself. diff --git a/triagebot.toml b/triagebot.toml index 6f50ef932e1..3f8f6a7b98c 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -17,6 +17,7 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB [assign.owners] "/.github" = ["@flip1995"] +"/util/gh-pages" = ["@xFrednet"] "*" = [ "@flip1995", "@Manishearth", From 5810f1fe3c74e62bbbc32a31b90f7ec48f14bc27 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 14 Mar 2023 14:19:06 +0100 Subject: [PATCH 36/47] remove some trait solver helpers they add more complexity then they are worth. It's confusing which of these helpers should be used in which context. --- clippy_lints/src/future_not_send.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 9fb73a371b8..ed0bd58c770 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -9,7 +9,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; -use rustc_trait_selection::traits::{self, FulfillmentError}; +use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt}; declare_clippy_lint! { /// ### What it does @@ -79,8 +79,10 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); let infcx = cx.tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); let cause = traits::ObligationCause::misc(span, fn_def_id); - let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait); + ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); + let send_errors = ocx.select_all_or_error(); if !send_errors.is_empty() { span_lint_and_then( cx, From 6f62887968779b050d44d4029254187d8936cd3c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 13 Mar 2023 18:54:05 +0000 Subject: [PATCH 37/47] Use local key in providers --- clippy_lints/src/cognitive_complexity.rs | 2 +- clippy_lints/src/derivable_impls.rs | 2 +- clippy_lints/src/derive.rs | 2 +- clippy_lints/src/functions/must_use.rs | 6 +++--- clippy_lints/src/partialeq_ne_impl.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index e8531157e0f..a8926b29ac8 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { span: Span, def_id: LocalDefId, ) { - if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) { + if !cx.tcx.has_attr(def_id, sym::test) { let expr = if is_async_fn(kind) { match get_async_fn_body(cx.tcx, body) { Some(b) => b, diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 8a5a28c6b3d..8f68f90a2a1 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -181,7 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { self_ty, .. }) = item.kind; - if !cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived); + if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived); if !item.span.from_expansion(); if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::Default, def_id); diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index b8428d66a5d..715348e869e 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -212,7 +212,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { }) = item.kind { let ty = cx.tcx.type_of(item.owner_id).subst_identity(); - let is_automatically_derived = cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived); + let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 29bdc46b647..eacbf6c6ec9 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -22,7 +22,7 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use); + let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind { let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -47,7 +47,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use); + let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr); } else if is_public @@ -73,7 +73,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use); + let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr); } else if let hir::TraitFn::Provided(eid) = *eid { diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 5aa3c6f2f93..a8c4823fe53 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -36,7 +36,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind; - if !cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived); + if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived); if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); if trait_ref.path.res.def_id() == eq_trait; then { From 84b6049eb9502cd9ba2f1518c26b6525b2fb2752 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 18 Mar 2023 23:22:04 +0100 Subject: [PATCH 38/47] Use uninit checking from rustc rustc has proper heuristics for actually checking whether a type allows being left uninitialized (by asking CTFE). We can now use this for our helper instead of rolling our own bad version with false positives. --- clippy_utils/src/ty.rs | 19 +++++++++---------- tests/ui/uninit.rs | 23 +++++++++++++++++++---- tests/ui/uninit.stderr | 16 ++++++++-------- tests/ui/uninit_vec.rs | 27 +++++++++++++++++++++++++++ tests/ui/uninit_vec.stderr | 33 ++++++++++++++++++++++----------- 5 files changed, 85 insertions(+), 33 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index f1c6f1dddd8..0b47234647f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -16,9 +16,9 @@ use rustc_infer::infer::{ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind, - Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, - UintTy, VariantDef, VariantDiscr, + self, layout::ValidityRequirement, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, + Predicate, PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; @@ -538,13 +538,12 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { } /// Checks if a given type looks safe to be uninitialized. -pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - match *ty.kind() { - ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component), - ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), - ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did()), - _ => false, - } +pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + cx.tcx + .check_validity_requirement((ValidityRequirement::Uninit, cx.param_env.and(ty))) + // For types containing generic parameters we cannot get a layout to check. + // Therefore, we are conservative and assume that they don't allow uninit. + .unwrap_or(false) } /// Gets an iterator over all predicates which apply to the given item. diff --git a/tests/ui/uninit.rs b/tests/ui/uninit.rs index 21131731708..412b36b4ee8 100644 --- a/tests/ui/uninit.rs +++ b/tests/ui/uninit.rs @@ -3,13 +3,15 @@ use std::mem::{self, MaybeUninit}; +union MyOwnMaybeUninit { + value: u8, + uninit: (), +} + fn main() { let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; - // edge case: For now we lint on empty arrays - let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; - - // edge case: For now we accept unit tuples + // This is OK, because ZSTs do not contain data. let _: () = unsafe { MaybeUninit::uninit().assume_init() }; // This is OK, because `MaybeUninit` allows uninitialized data. @@ -21,6 +23,19 @@ fn main() { // This is OK, because all constitutent types are uninit-compatible. let _: (MaybeUninit, [MaybeUninit; 2]) = unsafe { MaybeUninit::uninit().assume_init() }; + // This is OK, because our own MaybeUninit is just as fine as the one from core. + let _: MyOwnMaybeUninit = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because empty arrays don't contain data. + let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; + // Was a false negative. let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; + + polymorphic::<()>(); + + fn polymorphic() { + // We are conservative around polymorphic types. + let _: T = unsafe { mem::MaybeUninit::uninit().assume_init() }; + } } diff --git a/tests/ui/uninit.stderr b/tests/ui/uninit.stderr index 15ef2349489..9e01b9a4aa8 100644 --- a/tests/ui/uninit.stderr +++ b/tests/ui/uninit.stderr @@ -1,5 +1,5 @@ error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:7:29 + --> $DIR/uninit.rs:12:29 | LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,16 +7,16 @@ LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; = note: `#[deny(clippy::uninit_assumed_init)]` on by default error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:10:31 - | -LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:25:29 + --> $DIR/uninit.rs:33:29 | LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:39:29 + | +LL | let _: T = unsafe { mem::MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: aborting due to 3 previous errors diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index 194e4fc157e..59ec64a7ab1 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -7,6 +7,11 @@ struct MyVec { vec: Vec, } +union MyOwnMaybeUninit { + value: u8, + uninit: (), +} + fn main() { // with_capacity() -> set_len() should be detected let mut vec: Vec = Vec::with_capacity(1000); @@ -97,4 +102,26 @@ fn main() { unsafe { vec.set_len(0); } + + // ZSTs should not be detected + let mut vec: Vec<()> = Vec::with_capacity(1000); + unsafe { + vec.set_len(10); + } + + // unions should not be detected + let mut vec: Vec = Vec::with_capacity(1000); + unsafe { + vec.set_len(10); + } + + polymorphic::<()>(); + + fn polymorphic() { + // We are conservative around polymorphic types. + let mut vec: Vec = Vec::with_capacity(1000); + unsafe { + vec.set_len(10); + } + } } diff --git a/tests/ui/uninit_vec.stderr b/tests/ui/uninit_vec.stderr index 77fc689f076..9cdf0c95ad9 100644 --- a/tests/ui/uninit_vec.stderr +++ b/tests/ui/uninit_vec.stderr @@ -1,5 +1,5 @@ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:12:5 + --> $DIR/uninit_vec.rs:17:5 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | vec.set_len(200); = note: `-D clippy::uninit-vec` implied by `-D warnings` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:18:5 + --> $DIR/uninit_vec.rs:23:5 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> $DIR/uninit_vec.rs:24:5 + --> $DIR/uninit_vec.rs:29:5 | LL | let mut vec: Vec = Vec::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> $DIR/uninit_vec.rs:30:5 + --> $DIR/uninit_vec.rs:35:5 | LL | let mut vec: Vec = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> $DIR/uninit_vec.rs:35:5 + --> $DIR/uninit_vec.rs:40:5 | LL | let mut vec: Vec = Vec::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:49:5 + --> $DIR/uninit_vec.rs:54:5 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:58:5 + --> $DIR/uninit_vec.rs:63:5 | LL | my_vec.vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:63:5 + --> $DIR/uninit_vec.rs:68:5 | LL | my_vec.vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -82,7 +82,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:42:9 + --> $DIR/uninit_vec.rs:47:9 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:45:9 + --> $DIR/uninit_vec.rs:50:9 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -101,5 +101,16 @@ LL | vec.set_len(200); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 10 previous errors +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:122:9 + | +LL | let mut vec: Vec = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | vec.set_len(10); + | ^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: aborting due to 11 previous errors From 728ee6f8cdddd9bc7deda8271419c7cedfdf518a Mon Sep 17 00:00:00 2001 From: "Samuel \"Sam\" Tardieu" Date: Tue, 21 Mar 2023 20:18:47 +0100 Subject: [PATCH 39/47] Really dogfood clippy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dogfood success condition was inverted in `tests/dogfood.rs`: ```rust assert!(!failed_packages.is_empty(), …); ``` while instead the `failed_packages` collection must be empty: ```rust assert!(failed_packages.is_empty(), …); ``` And indeed, several clippy lint source files were not clean and had to be fixed in the process. --- clippy_lints/src/let_with_type_underscore.rs | 2 +- clippy_lints/src/manual_async_fn.rs | 7 ++++--- clippy_lints/src/manual_main_separator_str.rs | 4 +++- clippy_lints/src/redundant_async_block.rs | 2 +- clippy_lints/src/swap.rs | 4 ++-- clippy_lints/src/wildcard_imports.rs | 8 +++----- tests/dogfood.rs | 4 ++-- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index ba51973f2f9..c01e3882d52 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::*; +use rustc_hir::{Local, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index af52090d8a4..c7254b32d0b 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -77,11 +77,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let Some(ret_pos) = position_before_rarrow(&header_snip); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { - let header_snip = if !vis_snip.is_empty() { - format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) - } else { + let header_snip = if vis_snip.is_empty() { format!("async {}", &header_snip[..ret_pos]) + } else { + format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) }; + let help = format!("make the function `async` and {ret_sugg}"); diag.span_suggestion( header_span, diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs index a6d318f952c..c292bbe4e93 100644 --- a/clippy_lints/src/manual_main_separator_str.rs +++ b/clippy_lints/src/manual_main_separator_str.rs @@ -3,7 +3,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::{is_trait_method, match_def_path, paths, peel_hir_expr_refs}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -69,4 +69,6 @@ impl LateLintPass<'_> for ManualMainSeparatorStr { ); } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/redundant_async_block.rs b/clippy_lints/src/redundant_async_block.rs index 27ad4308637..b2adbcead00 100644 --- a/clippy_lints/src/redundant_async_block.rs +++ b/clippy_lints/src/redundant_async_block.rs @@ -1,5 +1,5 @@ use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet}; -use rustc_ast::ast::*; +use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind}; use rustc_ast::visit::Visitor as AstVisitor; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 50298898d03..f7eef03d1d4 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -189,7 +189,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { if let Some((lhs0, rhs0)) = parse(first) && let Some((lhs1, rhs1)) = parse(second) && first.span.eq_ctxt(second.span) - && !in_external_macro(&cx.sess(), first.span) + && !in_external_macro(cx.sess(), first.span) && is_same(cx, lhs0, rhs1) && is_same(cx, lhs1, rhs0) && !is_same(cx, lhs1, rhs1) // Ignore a = b; a = a (#10421) @@ -260,8 +260,8 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr< /// Implementation of the xor case for `MANUAL_SWAP` lint. fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { for [s1, s2, s3] in block.stmts.array_windows::<3>() { + let ctxt = s1.span.ctxt(); if_chain! { - let ctxt = s1.span.ctxt(); if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt); if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt); if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt); diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e105452e1c5..36f910c983f 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -158,12 +158,10 @@ impl LateLintPass<'_> for WildcardImports { let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false); let imports_string = if imports.len() == 1 { imports.pop().unwrap() + } else if braced_glob { + imports.join(", ") } else { - if braced_glob { - imports.join(", ") - } else { - format!("{{{}}}", imports.join(", ")) - } + format!("{{{}}}", imports.join(", ")) }; let sugg = if braced_glob { diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 9643c2c9707..3a5d478fa31 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -37,10 +37,10 @@ fn dogfood_clippy() { } assert!( - !failed_packages.is_empty(), + failed_packages.is_empty(), "Dogfood failed for packages `{}`", failed_packages.iter().format(", "), - ) + ); } #[test] From 349708655ea924bb34861025e3ee6d99d58e3f8d Mon Sep 17 00:00:00 2001 From: "Samuel \"Sam\" Tardieu" Date: Sun, 12 Mar 2023 14:06:13 +0100 Subject: [PATCH 40/47] Do not propose to remove `async move` if variables are captured by ref --- clippy_lints/src/redundant_async_block.rs | 37 +++++++++++++++++- tests/ui/redundant_async_block.fixed | 47 +++++++++++++++++++++++ tests/ui/redundant_async_block.rs | 47 +++++++++++++++++++++++ tests/ui/redundant_async_block.stderr | 22 ++++++++--- 4 files changed, 147 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/redundant_async_block.rs b/clippy_lints/src/redundant_async_block.rs index 27ad4308637..24dcd52b93e 100644 --- a/clippy_lints/src/redundant_async_block.rs +++ b/clippy_lints/src/redundant_async_block.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.69.0"] pub REDUNDANT_ASYNC_BLOCK, - complexity, + nursery, "`async { future.await }` can be replaced by `future`" } declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]); @@ -48,6 +48,11 @@ impl EarlyLintPass for RedundantAsyncBlock { !future.span.from_expansion() && !await_in_expr(future) { + if captures_value(last) { + // If the async block captures variables then there is no equivalence. + return; + } + span_lint_and_sugg( cx, REDUNDANT_ASYNC_BLOCK, @@ -82,3 +87,33 @@ impl<'ast> AstVisitor<'ast> for AwaitDetector { } } } + +/// Check whether an expression may have captured a local variable. +/// This is done by looking for paths with only one segment, except as +/// a prefix of `.await` since this would be captured by value. +/// +/// This function will sometimes return `true` even tough there are no +/// captures happening: at the AST level, it is impossible to +/// dinstinguish a function call from a call to a closure which comes +/// from the local environment. +fn captures_value(expr: &Expr) -> bool { + let mut detector = CaptureDetector::default(); + detector.visit_expr(expr); + detector.capture_found +} + +#[derive(Default)] +struct CaptureDetector { + capture_found: bool, +} + +impl<'ast> AstVisitor<'ast> for CaptureDetector { + fn visit_expr(&mut self, ex: &'ast Expr) { + match (&ex.kind, self.capture_found) { + (ExprKind::Await(fut), _) if matches!(fut.kind, ExprKind::Path(..)) => (), + (ExprKind::Path(_, path), _) if path.segments.len() == 1 => self.capture_found = true, + (_, false) => rustc_ast::visit::walk_expr(self, ex), + _ => (), + } + } +} diff --git a/tests/ui/redundant_async_block.fixed b/tests/ui/redundant_async_block.fixed index 5f9931df45e..d26b7a332cb 100644 --- a/tests/ui/redundant_async_block.fixed +++ b/tests/ui/redundant_async_block.fixed @@ -3,6 +3,8 @@ #![allow(unused)] #![warn(clippy::redundant_async_block)] +use std::future::Future; + async fn func1(n: usize) -> usize { n + 1 } @@ -62,3 +64,48 @@ fn main() { let fut = async_await_parameter_in_macro!(func2()); let fut = async_await_in_macro!(std::convert::identity); } + +#[allow(clippy::let_and_return)] +fn capture_local() -> impl Future { + // Lint + let fut = async { 17 }; + fut +} + +fn capture_local_closure(s: &str) -> impl Future { + let f = move || std::future::ready(s); + // Do not lint: `f` would not live long enough + async move { f().await } +} + +#[allow(clippy::let_and_return)] +fn capture_arg(s: &str) -> impl Future { + // Lint + let fut = async move { s }; + fut +} + +#[derive(Debug, Clone)] +struct F {} + +impl F { + async fn run(&self) {} +} + +pub async fn run() { + let f = F {}; + let c = f.clone(); + // Do not lint: `c` would not live long enough + spawn(async move { c.run().await }); + let _f = f; +} + +fn spawn(_: F) {} + +async fn work(_: &str) {} + +fn capture() { + let val = "Hello World".to_owned(); + // Do not lint: `val` would not live long enough + spawn(async { work(&{ val }).await }); +} diff --git a/tests/ui/redundant_async_block.rs b/tests/ui/redundant_async_block.rs index de3c9970c65..04726e62805 100644 --- a/tests/ui/redundant_async_block.rs +++ b/tests/ui/redundant_async_block.rs @@ -3,6 +3,8 @@ #![allow(unused)] #![warn(clippy::redundant_async_block)] +use std::future::Future; + async fn func1(n: usize) -> usize { n + 1 } @@ -62,3 +64,48 @@ fn main() { let fut = async_await_parameter_in_macro!(func2()); let fut = async_await_in_macro!(std::convert::identity); } + +#[allow(clippy::let_and_return)] +fn capture_local() -> impl Future { + // Lint + let fut = async { 17 }; + async move { fut.await } +} + +fn capture_local_closure(s: &str) -> impl Future { + let f = move || std::future::ready(s); + // Do not lint: `f` would not live long enough + async move { f().await } +} + +#[allow(clippy::let_and_return)] +fn capture_arg(s: &str) -> impl Future { + // Lint + let fut = async move { s }; + async move { fut.await } +} + +#[derive(Debug, Clone)] +struct F {} + +impl F { + async fn run(&self) {} +} + +pub async fn run() { + let f = F {}; + let c = f.clone(); + // Do not lint: `c` would not live long enough + spawn(async move { c.run().await }); + let _f = f; +} + +fn spawn(_: F) {} + +async fn work(_: &str) {} + +fn capture() { + let val = "Hello World".to_owned(); + // Do not lint: `val` would not live long enough + spawn(async { work(&{ val }).await }); +} diff --git a/tests/ui/redundant_async_block.stderr b/tests/ui/redundant_async_block.stderr index b16d96dce84..1a1c1603e08 100644 --- a/tests/ui/redundant_async_block.stderr +++ b/tests/ui/redundant_async_block.stderr @@ -1,5 +1,5 @@ error: this async expression only awaits a single future - --> $DIR/redundant_async_block.rs:13:13 + --> $DIR/redundant_async_block.rs:15:13 | LL | let x = async { f.await }; | ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f` @@ -7,22 +7,34 @@ LL | let x = async { f.await }; = note: `-D clippy::redundant-async-block` implied by `-D warnings` error: this async expression only awaits a single future - --> $DIR/redundant_async_block.rs:46:16 + --> $DIR/redundant_async_block.rs:48:16 | LL | let fut2 = async { fut1.await }; | ^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1` error: this async expression only awaits a single future - --> $DIR/redundant_async_block.rs:49:16 + --> $DIR/redundant_async_block.rs:51:16 | LL | let fut2 = async move { fut1.await }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1` error: this async expression only awaits a single future - --> $DIR/redundant_async_block.rs:51:15 + --> $DIR/redundant_async_block.rs:53:15 | LL | let fut = async { async { 42 }.await }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }` -error: aborting due to 4 previous errors +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:72:5 + | +LL | async move { fut.await } + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut` + +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:85:5 + | +LL | async move { fut.await } + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut` + +error: aborting due to 6 previous errors From b138bb587bfc2c0e5a3a55820848cc9b2f26149b Mon Sep 17 00:00:00 2001 From: "Samuel \"Sam\" Tardieu" Date: Wed, 22 Mar 2023 00:34:35 +0100 Subject: [PATCH 41/47] Do not propose to simplify a not expression coming from a macro --- clippy_lints/src/booleans.rs | 25 +++++++++++++------------ tests/ui/nonminimal_bool.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index e8106beec37..29fde9336c0 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -495,18 +495,19 @@ struct NotSimplificationVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind { - if let Some(suggestion) = simplify_not(self.cx, inner) { - span_lint_and_sugg( - self.cx, - NONMINIMAL_BOOL, - expr.span, - "this boolean expression can be simplified", - "try", - suggestion, - Applicability::MachineApplicable, - ); - } + if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind && + !inner.span.from_expansion() && + let Some(suggestion) = simplify_not(self.cx, inner) + { + span_lint_and_sugg( + self.cx, + NONMINIMAL_BOOL, + expr.span, + "this boolean expression can be simplified", + "try", + suggestion, + Applicability::MachineApplicable, + ); } walk_expr(self, expr); diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index e9b4367ca65..3b5a374b4a7 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -63,3 +63,32 @@ fn issue9428() { println!("foo"); } } + +fn issue_10523() { + macro_rules! a { + ($v:expr) => { + $v.is_some() + }; + } + let x: Option = None; + if !a!(x) {} +} + +fn issue_10523_1() { + macro_rules! a { + ($v:expr) => { + !$v.is_some() + }; + } + let x: Option = None; + if a!(x) {} +} + +fn issue_10523_2() { + macro_rules! a { + () => { + !None::.is_some() + }; + } + if a!() {} +} From cae7b877113652f96aee1361b9356bd9720bddd1 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 19 Mar 2023 21:32:34 +0400 Subject: [PATCH 42/47] rustc: Remove unused `Session` argument from some attribute functions --- clippy_lints/src/functions/must_use.rs | 6 +++--- clippy_utils/src/attrs.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index eacbf6c6ec9..67877780c0e 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -28,7 +28,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) { + } else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) { check_must_use_candidate( cx, sig.decl, @@ -51,7 +51,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr); } else if is_public - && !is_proc_macro(cx.sess(), attrs) + && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { check_must_use_candidate( @@ -78,7 +78,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr); } else if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir().body(eid); - if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) { + if attr.is_none() && is_public && !is_proc_macro(attrs) { check_must_use_candidate( cx, sig.decl, diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 7987a233bdc..bc3d774540a 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -145,8 +145,8 @@ pub fn get_unique_attr<'a>( /// Return true if the attributes contain any of `proc_macro`, /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise -pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(|attr| sess.is_proc_macro_attr(attr)) +pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool { + attrs.iter().any(|attr| attr.is_proc_macro_attr()) } /// Return true if the attributes contain `#[doc(hidden)]` From 966d5b0e348c9bd29eb1d9ddc2c2cf42555a25b7 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 22 Mar 2023 22:12:19 +0100 Subject: [PATCH 43/47] Cache `has_sig_drop_attr` for `significant_drop_tightening` The lint is very slow as it doesn't cache the deeply nested check for the attribute. If we cache it, we can reduce the time spent on checking `rustc_borrowck` from 28s to 9s, which is a nice improvement. In the profile, the time inside `has_sig_drop_attr` goes from 66% to 0.2%, which is a lot more reasonable. See the PR for nice graphs. --- .../src/significant_drop_tightening.rs | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index e2d90edec5a..c3e99aa0055 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -1,9 +1,9 @@ -use crate::FxHashSet; use clippy_utils::{ diagnostics::span_lint_and_then, get_attr, source::{indent_of, snippet}, }; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::{ self as hir, @@ -58,6 +58,7 @@ impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]); pub struct SignificantDropTightening<'tcx> { /// Auxiliary structure used to avoid having to verify the same type multiple times. seen_types: FxHashSet>, + type_cache: FxHashMap, bool>, } impl<'tcx> SignificantDropTightening<'tcx> { @@ -118,7 +119,7 @@ impl<'tcx> SignificantDropTightening<'tcx> { stmt: &hir::Stmt<'_>, cb: impl Fn(&mut SigDropAuxParams), ) { - let mut sig_drop_finder = SigDropFinder::new(cx, &mut self.seen_types); + let mut sig_drop_finder = SigDropFinder::new(cx, &mut self.seen_types, &mut self.type_cache); sig_drop_finder.visit_expr(expr); if sig_drop_finder.has_sig_drop { cb(sdap); @@ -296,15 +297,24 @@ impl Default for SigDropAuxParams { struct SigDropChecker<'cx, 'sdt, 'tcx> { cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet>, + type_cache: &'sdt mut FxHashMap, bool>, } impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> { - pub(crate) fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet>) -> Self { + pub(crate) fn new( + cx: &'cx LateContext<'tcx>, + seen_types: &'sdt mut FxHashSet>, + type_cache: &'sdt mut FxHashMap, bool>, + ) -> Self { seen_types.clear(); - Self { cx, seen_types } + Self { + cx, + seen_types, + type_cache, + } } - pub(crate) fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { + pub(crate) fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>) -> bool { if let Some(adt) = ty.ty_adt_def() { let mut iter = get_attr( self.cx.sess(), @@ -340,6 +350,16 @@ impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> { } } + pub(crate) fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { + // The borrow checker prevents us from using something fancier like or_insert_with. + if let Some(ty) = self.type_cache.get(&ty) { + return *ty; + } + let value = self.has_sig_drop_attr_uncached(ty); + self.type_cache.insert(ty, value); + value + } + fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool { !self.seen_types.insert(ty) } @@ -353,11 +373,15 @@ struct SigDropFinder<'cx, 'sdt, 'tcx> { } impl<'cx, 'sdt, 'tcx> SigDropFinder<'cx, 'sdt, 'tcx> { - fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet>) -> Self { + fn new( + cx: &'cx LateContext<'tcx>, + seen_types: &'sdt mut FxHashSet>, + type_cache: &'sdt mut FxHashMap, bool>, + ) -> Self { Self { cx, has_sig_drop: false, - sig_drop_checker: SigDropChecker::new(cx, seen_types), + sig_drop_checker: SigDropChecker::new(cx, seen_types, type_cache), } } } From 8bdd54e8c6f3b19d66030551a40f7c781335905f Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 24 Feb 2023 18:32:52 -0800 Subject: [PATCH 44/47] Add `CastKind::Transmute` to MIR Updates `interpret`, `codegen_ssa`, and `codegen_cranelift` to consume the new cast instead of the intrinsic. Includes `CastTransmute` for custom MIR building, to be able to test the extra UB. --- clippy_utils/src/qualify_min_const_fn.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 24403e8b6f3..ff492a5104d 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -176,6 +176,9 @@ fn check_rvalue<'tcx>( // FIXME(dyn-star) unimplemented!() }, + Rvalue::Cast(CastKind::Transmute, _, _) => { + Err((span, "transmute can attempt to turn pointers into integers, so is unstable in const fn".into())) + }, // binops are fine on integers Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { check_operand(tcx, lhs, span, body)?; From b506eb5338d3c23f73190615367bca14a36b9ae3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 21 Mar 2023 22:11:40 +0000 Subject: [PATCH 45/47] Rename AliasEq -> AliasRelate --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 24403e8b6f3..58f7742ab87 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -37,7 +37,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, - ty::PredicateKind::AliasEq(..) => panic!("alias eq predicate on function: {predicate:#?}"), + ty::PredicateKind::AliasRelate(..) => panic!("alias relate predicate on function: {predicate:#?}"), ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"), From d7d3dbf06022c3ec020c3906afa0090ad5ae239c Mon Sep 17 00:00:00 2001 From: "Samuel \"Sam\" Tardieu" Date: Sat, 11 Mar 2023 12:44:18 +0100 Subject: [PATCH 46/47] New lint: detect unnecessary struct building --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/unnecessary_struct_initialization.rs | 84 +++++++++++++++++++ tests/ui/needless_update.rs | 2 +- tests/ui/no_effect.rs | 7 +- tests/ui/no_effect.stderr | 60 ++++++------- tests/ui/unnecessary_operation.fixed | 8 +- tests/ui/unnecessary_operation.rs | 8 +- tests/ui/unnecessary_operation.stderr | 40 ++++----- .../unnecessary_struct_initialization.fixed | 73 ++++++++++++++++ tests/ui/unnecessary_struct_initialization.rs | 77 +++++++++++++++++ .../unnecessary_struct_initialization.stderr | 46 ++++++++++ 13 files changed, 355 insertions(+), 54 deletions(-) create mode 100644 clippy_lints/src/unnecessary_struct_initialization.rs create mode 100644 tests/ui/unnecessary_struct_initialization.fixed create mode 100644 tests/ui/unnecessary_struct_initialization.rs create mode 100644 tests/ui/unnecessary_struct_initialization.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 47a503510c1..1323f973ccf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4987,6 +4987,7 @@ Released 2018-09-13 [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by +[`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2331e857b1f..8ca91301472 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -618,6 +618,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, + crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO, crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO, crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 100cba45679..c9210bf73f8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -302,6 +302,7 @@ mod unit_types; mod unnamed_address; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; +mod unnecessary_struct_initialization; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; @@ -938,6 +939,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute)); store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv()))); + store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs new file mode 100644 index 00000000000..af0b4b1592f --- /dev/null +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -0,0 +1,84 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_expr, path_to_local, source::snippet, ty::is_copy}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for initialization of a `struct` by copying a base without setting + /// any field. + /// + /// ### Why is this bad? + /// Readibility suffers from unnecessary struct building. + /// + /// ### Example + /// ```rust + /// struct S { s: String } + /// + /// let a = S { s: String::from("Hello, world!") }; + /// let b = S { ..a }; + /// ``` + /// Use instead: + /// ```rust + /// struct S { s: String } + /// + /// let a = S { s: String::from("Hello, world!") }; + /// let b = a; + /// ``` + #[clippy::version = "1.70.0"] + pub UNNECESSARY_STRUCT_INITIALIZATION, + complexity, + "struct built from a base that can be written mode concisely" +} +declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]); + +impl LateLintPass<'_> for UnnecessaryStruct { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Struct(_, &[], Some(base)) = expr.kind { + if let Some(parent) = get_parent_expr(cx, expr) && + let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) && + parent_ty.is_any_ptr() + { + if is_copy(cx, cx.typeck_results().expr_ty(expr)) && path_to_local(base).is_some() { + // When the type implements `Copy`, a reference to the new struct works on the + // copy. Using the original would borrow it. + return; + } + + if parent_ty.is_mutable_ptr() && !is_mutable(cx, base) { + // The original can be used in a mutable reference context only if it is mutable. + return; + } + } + + // TODO: do not propose to replace *XX if XX is not Copy + if let ExprKind::Unary(UnOp::Deref, target) = base.kind && + matches!(target.kind, ExprKind::Path(..)) && + !is_copy(cx, cx.typeck_results().expr_ty(expr)) + { + // `*base` cannot be used instead of the struct in the general case if it is not Copy. + return; + } + + span_lint_and_sugg( + cx, + UNNECESSARY_STRUCT_INITIALIZATION, + expr.span, + "unnecessary struct building", + "replace with", + snippet(cx, base.span, "..").into_owned(), + rustc_errors::Applicability::MachineApplicable, + ); + } + } +} + +fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(hir_id) = path_to_local(expr) && + let Node::Pat(pat) = cx.tcx.hir().get(hir_id) + { + matches!(pat.kind, PatKind::Binding(BindingAnnotation::MUT, ..)) + } else { + true + } +} diff --git a/tests/ui/needless_update.rs b/tests/ui/needless_update.rs index b93ff048a62..4e8517cad10 100644 --- a/tests/ui/needless_update.rs +++ b/tests/ui/needless_update.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_update)] -#![allow(clippy::no_effect)] +#![allow(clippy::no_effect, clippy::unnecessary_struct_initialization)] struct S { pub a: i32, diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index f08eb092e6b..ac8b1e756f2 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -1,7 +1,12 @@ #![feature(box_syntax, fn_traits, unboxed_closures)] #![warn(clippy::no_effect_underscore_binding)] #![allow(dead_code, path_statements)] -#![allow(clippy::deref_addrof, clippy::redundant_field_names, clippy::uninlined_format_args)] +#![allow( + clippy::deref_addrof, + clippy::redundant_field_names, + clippy::uninlined_format_args, + clippy::unnecessary_struct_initialization +)] struct Unit; struct Tuple(i32); diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index 6a1e636f9a6..2efefd2b2a3 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -1,5 +1,5 @@ error: statement with no effect - --> $DIR/no_effect.rs:92:5 + --> $DIR/no_effect.rs:97:5 | LL | 0; | ^^ @@ -7,157 +7,157 @@ LL | 0; = note: `-D clippy::no-effect` implied by `-D warnings` error: statement with no effect - --> $DIR/no_effect.rs:93:5 + --> $DIR/no_effect.rs:98:5 | LL | s2; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:94:5 + --> $DIR/no_effect.rs:99:5 | LL | Unit; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:95:5 + --> $DIR/no_effect.rs:100:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:96:5 + --> $DIR/no_effect.rs:101:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:97:5 + --> $DIR/no_effect.rs:102:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:98:5 + --> $DIR/no_effect.rs:103:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:99:5 + --> $DIR/no_effect.rs:104:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:100:5 + --> $DIR/no_effect.rs:105:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:101:5 + --> $DIR/no_effect.rs:106:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:102:5 + --> $DIR/no_effect.rs:107:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:103:5 + --> $DIR/no_effect.rs:108:5 | LL | &6; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:104:5 + --> $DIR/no_effect.rs:109:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:105:5 + --> $DIR/no_effect.rs:110:5 | LL | box 42; | ^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:106:5 + --> $DIR/no_effect.rs:111:5 | LL | ..; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:107:5 + --> $DIR/no_effect.rs:112:5 | LL | 5..; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:108:5 + --> $DIR/no_effect.rs:113:5 | LL | ..5; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:109:5 + --> $DIR/no_effect.rs:114:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:110:5 + --> $DIR/no_effect.rs:115:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:111:5 + --> $DIR/no_effect.rs:116:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:112:5 + --> $DIR/no_effect.rs:117:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:113:5 + --> $DIR/no_effect.rs:118:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:114:5 + --> $DIR/no_effect.rs:119:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:115:5 + --> $DIR/no_effect.rs:120:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:117:5 + --> $DIR/no_effect.rs:122:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:119:5 + --> $DIR/no_effect.rs:124:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:120:5 + --> $DIR/no_effect.rs:125:5 | LL | let _unused = 1; | ^^^^^^^^^^^^^^^^ @@ -165,19 +165,19 @@ LL | let _unused = 1; = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:121:5 + --> $DIR/no_effect.rs:126:5 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:122:5 + --> $DIR/no_effect.rs:127:5 | LL | let _duck = Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:123:5 + --> $DIR/no_effect.rs:128:5 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index d37163570ab..ec931c726ee 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -1,7 +1,13 @@ // run-rustfix #![feature(box_syntax)] -#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] +#![allow( + clippy::deref_addrof, + dead_code, + unused, + clippy::no_effect, + clippy::unnecessary_struct_initialization +)] #![warn(clippy::unnecessary_operation)] struct Tuple(i32); diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index a14fd4bca0e..d896df32e41 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -1,7 +1,13 @@ // run-rustfix #![feature(box_syntax)] -#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] +#![allow( + clippy::deref_addrof, + dead_code, + unused, + clippy::no_effect, + clippy::unnecessary_struct_initialization +)] #![warn(clippy::unnecessary_operation)] struct Tuple(i32); diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index f66d08ecb82..c183082ec86 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -1,5 +1,5 @@ error: unnecessary operation - --> $DIR/unnecessary_operation.rs:51:5 + --> $DIR/unnecessary_operation.rs:57:5 | LL | Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` @@ -7,109 +7,109 @@ LL | Tuple(get_number()); = note: `-D clippy::unnecessary-operation` implied by `-D warnings` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:52:5 + --> $DIR/unnecessary_operation.rs:58:5 | LL | Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:53:5 + --> $DIR/unnecessary_operation.rs:59:5 | LL | Struct { ..get_struct() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:54:5 + --> $DIR/unnecessary_operation.rs:60:5 | LL | Enum::Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:55:5 + --> $DIR/unnecessary_operation.rs:61:5 | LL | Enum::Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:56:5 + --> $DIR/unnecessary_operation.rs:62:5 | LL | 5 + get_number(); | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:57:5 + --> $DIR/unnecessary_operation.rs:63:5 | LL | *&get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:58:5 + --> $DIR/unnecessary_operation.rs:64:5 | LL | &get_number(); | ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:59:5 + --> $DIR/unnecessary_operation.rs:65:5 | LL | (5, 6, get_number()); | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:60:5 + --> $DIR/unnecessary_operation.rs:66:5 | LL | box get_number(); | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:61:5 + --> $DIR/unnecessary_operation.rs:67:5 | LL | get_number()..; | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:62:5 + --> $DIR/unnecessary_operation.rs:68:5 | LL | ..get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:63:5 + --> $DIR/unnecessary_operation.rs:69:5 | LL | 5..get_number(); | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:64:5 + --> $DIR/unnecessary_operation.rs:70:5 | LL | [42, get_number()]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:65:5 + --> $DIR/unnecessary_operation.rs:71:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:66:5 + --> $DIR/unnecessary_operation.rs:72:5 | LL | (42, get_number()).1; | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:67:5 + --> $DIR/unnecessary_operation.rs:73:5 | LL | [get_number(); 55]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:68:5 + --> $DIR/unnecessary_operation.rs:74:5 | LL | [42; 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:69:5 + --> $DIR/unnecessary_operation.rs:75:5 | LL | / { LL | | get_number() @@ -117,7 +117,7 @@ LL | | }; | |______^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:72:5 + --> $DIR/unnecessary_operation.rs:78:5 | LL | / FooString { LL | | s: String::from("blah"), diff --git a/tests/ui/unnecessary_struct_initialization.fixed b/tests/ui/unnecessary_struct_initialization.fixed new file mode 100644 index 00000000000..b47129e4a36 --- /dev/null +++ b/tests/ui/unnecessary_struct_initialization.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::unnecessary_struct_initialization)] + +struct S { + f: String, +} + +#[derive(Clone, Copy)] +struct T { + f: u32, +} + +struct U { + f: u32, +} + +impl Clone for U { + fn clone(&self) -> Self { + // Do not lint: `Self` does not implement `Copy` + Self { ..*self } + } +} + +#[derive(Copy)] +struct V { + f: u32, +} + +impl Clone for V { + fn clone(&self) -> Self { + // Lint: `Self` implements `Copy` + *self + } +} + +fn main() { + // Should lint: `a` would be consumed anyway + let a = S { f: String::from("foo") }; + let mut b = a; + + // Should lint: `b` would be consumed, and is mutable + let c = &mut b; + + // Should not lint as `d` is not mutable + let d = S { f: String::from("foo") }; + let e = &mut S { ..d }; + + // Should lint as `f` would be consumed anyway + let f = S { f: String::from("foo") }; + let g = &f; + + // Should lint: the result of an expression is mutable + let h = &mut *Box::new(S { f: String::from("foo") }); + + // Should not lint: `m` would be both alive and borrowed + let m = T { f: 17 }; + let n = &T { ..m }; + + // Should not lint: `m` should not be modified + let o = &mut T { ..m }; + o.f = 32; + assert_eq!(m.f, 17); + + // Should not lint: `m` should not be modified + let o = &mut T { ..m } as *mut T; + unsafe { &mut *o }.f = 32; + assert_eq!(m.f, 17); + + // Should lint: the result of an expression is mutable and temporary + let p = &mut *Box::new(T { f: 5 }); +} diff --git a/tests/ui/unnecessary_struct_initialization.rs b/tests/ui/unnecessary_struct_initialization.rs new file mode 100644 index 00000000000..63b11c626e5 --- /dev/null +++ b/tests/ui/unnecessary_struct_initialization.rs @@ -0,0 +1,77 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::unnecessary_struct_initialization)] + +struct S { + f: String, +} + +#[derive(Clone, Copy)] +struct T { + f: u32, +} + +struct U { + f: u32, +} + +impl Clone for U { + fn clone(&self) -> Self { + // Do not lint: `Self` does not implement `Copy` + Self { ..*self } + } +} + +#[derive(Copy)] +struct V { + f: u32, +} + +impl Clone for V { + fn clone(&self) -> Self { + // Lint: `Self` implements `Copy` + Self { ..*self } + } +} + +fn main() { + // Should lint: `a` would be consumed anyway + let a = S { f: String::from("foo") }; + let mut b = S { ..a }; + + // Should lint: `b` would be consumed, and is mutable + let c = &mut S { ..b }; + + // Should not lint as `d` is not mutable + let d = S { f: String::from("foo") }; + let e = &mut S { ..d }; + + // Should lint as `f` would be consumed anyway + let f = S { f: String::from("foo") }; + let g = &S { ..f }; + + // Should lint: the result of an expression is mutable + let h = &mut S { + ..*Box::new(S { f: String::from("foo") }) + }; + + // Should not lint: `m` would be both alive and borrowed + let m = T { f: 17 }; + let n = &T { ..m }; + + // Should not lint: `m` should not be modified + let o = &mut T { ..m }; + o.f = 32; + assert_eq!(m.f, 17); + + // Should not lint: `m` should not be modified + let o = &mut T { ..m } as *mut T; + unsafe { &mut *o }.f = 32; + assert_eq!(m.f, 17); + + // Should lint: the result of an expression is mutable and temporary + let p = &mut T { + ..*Box::new(T { f: 5 }) + }; +} diff --git a/tests/ui/unnecessary_struct_initialization.stderr b/tests/ui/unnecessary_struct_initialization.stderr new file mode 100644 index 00000000000..ca497057702 --- /dev/null +++ b/tests/ui/unnecessary_struct_initialization.stderr @@ -0,0 +1,46 @@ +error: unnecessary struct building + --> $DIR/unnecessary_struct_initialization.rs:34:9 + | +LL | Self { ..*self } + | ^^^^^^^^^^^^^^^^ help: replace with: `*self` + | + = note: `-D clippy::unnecessary-struct-initialization` implied by `-D warnings` + +error: unnecessary struct building + --> $DIR/unnecessary_struct_initialization.rs:41:17 + | +LL | let mut b = S { ..a }; + | ^^^^^^^^^ help: replace with: `a` + +error: unnecessary struct building + --> $DIR/unnecessary_struct_initialization.rs:44:18 + | +LL | let c = &mut S { ..b }; + | ^^^^^^^^^ help: replace with: `b` + +error: unnecessary struct building + --> $DIR/unnecessary_struct_initialization.rs:52:14 + | +LL | let g = &S { ..f }; + | ^^^^^^^^^ help: replace with: `f` + +error: unnecessary struct building + --> $DIR/unnecessary_struct_initialization.rs:55:18 + | +LL | let h = &mut S { + | __________________^ +LL | | ..*Box::new(S { f: String::from("foo") }) +LL | | }; + | |_____^ help: replace with: `*Box::new(S { f: String::from("foo") })` + +error: unnecessary struct building + --> $DIR/unnecessary_struct_initialization.rs:74:18 + | +LL | let p = &mut T { + | __________________^ +LL | | ..*Box::new(T { f: 5 }) +LL | | }; + | |_____^ help: replace with: `*Box::new(T { f: 5 })` + +error: aborting due to 6 previous errors + From 8134414e03f6ddedf95d96758251124082dbe164 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 24 Mar 2023 13:36:15 +0100 Subject: [PATCH 47/47] Bump nightly version -> 2023-03-24 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index d788c6359d7..0b2458ea007 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-03-10" +channel = "nightly-2023-03-24" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]