diff options
| author | Philipp Krones <hello@philkrones.com> | 2023-12-16 14:12:50 +0100 |
|---|---|---|
| committer | Philipp Krones <hello@philkrones.com> | 2023-12-16 14:12:50 +0100 |
| commit | 61f4a73c6789078ac2cd04a6555636cf6cd667cf (patch) | |
| tree | a1d756065ee276d10e365c6f6f91082ad08df211 | |
| parent | 3ad8e2d129a67b0dc450d6ef1100ae41d2cd4b8a (diff) | |
| parent | a859e5cc1ce100df22346a1005da30532d04de59 (diff) | |
| download | rust-61f4a73c6789078ac2cd04a6555636cf6cd667cf.tar.gz rust-61f4a73c6789078ac2cd04a6555636cf6cd667cf.zip | |
Merge commit 'a859e5cc1ce100df22346a1005da30532d04de59' into clippyup
108 files changed, 3064 insertions, 637 deletions
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index f67233dec62..73c25550742 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -206,6 +206,7 @@ jobs: max-parallel: 6 matrix: integration: + - 'matthiaskrgr/clippy_ci_panic_test' - 'rust-lang/cargo' - 'rust-lang/chalk' - 'rust-lang/rustfmt' @@ -220,7 +221,6 @@ jobs: - 'rust-itertools/itertools' - 'rust-lang-nursery/failure' - 'rust-lang/log' - - 'matthiaskrgr/clippy_ci_panic_test' runs-on: ubuntu-latest diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 2e9b755caa0..70aff7f60e0 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -4946,6 +4946,7 @@ Released 2018-09-13 [`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints [`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr [`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt +[`blocks_in_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_conditions [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison @@ -5145,9 +5146,11 @@ Released 2018-09-13 [`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask +[`ineffective_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_open_options [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string [`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match [`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter +[`infinite_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_loop [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string [`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display [`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields @@ -5462,6 +5465,7 @@ Released 2018-09-13 [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once +[`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs @@ -5582,6 +5586,7 @@ Released 2018-09-13 [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented +[`uninhabited_references`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninhabited_references [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec [`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args diff --git a/src/tools/clippy/clippy_dev/src/lint.rs b/src/tools/clippy/clippy_dev/src/lint.rs index a19be1bca6c..906a9727810 100644 --- a/src/tools/clippy/clippy_dev/src/lint.rs +++ b/src/tools/clippy/clippy_dev/src/lint.rs @@ -1,6 +1,6 @@ use crate::{cargo_clippy_path, exit_if_err}; -use std::fs; use std::process::{self, Command}; +use std::{env, fs}; pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) { let is_file = match fs::metadata(path) { @@ -13,7 +13,7 @@ pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) { if is_file { exit_if_err( - Command::new("cargo") + Command::new(env::var("CARGO").unwrap_or("cargo".into())) .args(["run", "--bin", "clippy-driver", "--"]) .args(["-L", "./target/debug"]) .args(["-Z", "no-codegen"]) @@ -23,7 +23,11 @@ pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) { .status(), ); } else { - exit_if_err(Command::new("cargo").arg("build").status()); + exit_if_err( + Command::new(env::var("CARGO").unwrap_or("cargo".into())) + .arg("build") + .status(), + ); let status = Command::new(cargo_clippy_path()) .arg("clippy") diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs index 535c25e69f1..ea925f6709f 100644 --- a/src/tools/clippy/clippy_dev/src/serve.rs +++ b/src/tools/clippy/clippy_dev/src/serve.rs @@ -2,8 +2,8 @@ use std::ffi::OsStr; use std::num::ParseIntError; use std::path::Path; use std::process::Command; -use std::thread; use std::time::{Duration, SystemTime}; +use std::{env, thread}; /// # Panics /// @@ -16,7 +16,7 @@ pub fn run(port: u16, lint: Option<&String>) -> ! { loop { if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") { - Command::new("cargo") + Command::new(env::var("CARGO").unwrap_or("cargo".into())) .arg("collect-metadata") .spawn() .unwrap() diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index a9375214be4..ad8b7ded46b 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -16,7 +16,7 @@ clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } itertools = "0.11" quine-mc_cluskey = "0.2" -regex-syntax = "0.7" +regex-syntax = "0.8" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } tempfile = { version = "3.3.0", optional = true } diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs index e052d36f115..e3daf75c3eb 100644 --- a/src/tools/clippy/clippy_lints/src/as_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs @@ -48,11 +48,10 @@ declare_lint_pass!(AsConversions => [AS_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for AsConversions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if in_external_macro(cx.sess(), expr.span) || is_from_proc_macro(cx, expr) { - return; - } - - if let ExprKind::Cast(_, _) = expr.kind { + if let ExprKind::Cast(_, _) = expr.kind + && !in_external_macro(cx.sess(), expr.span) + && !is_from_proc_macro(cx, expr) + { span_lint_and_help( cx, AS_CONVERSIONS, diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs new file mode 100644 index 00000000000..1417e230aee --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs @@ -0,0 +1,137 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet_block_with_applicability; +use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::{get_parent_expr, higher}; +use core::ops::ControlFlow; +use rustc_errors::Applicability; +use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `if` conditions that use blocks containing an + /// expression, statements or conditions that use closures with blocks. + /// + /// ### Why is this bad? + /// Style, using blocks in the condition makes it hard to read. + /// + /// ### Examples + /// ```no_run + /// # fn somefunc() -> bool { true }; + /// if { true } { /* ... */ } + /// + /// if { let x = somefunc(); x } { /* ... */ } + /// ``` + /// + /// Use instead: + /// ```no_run + /// # fn somefunc() -> bool { true }; + /// if true { /* ... */ } + /// + /// let res = { let x = somefunc(); x }; + /// if res { /* ... */ } + /// ``` + #[clippy::version = "1.45.0"] + pub BLOCKS_IN_CONDITIONS, + style, + "useless or complex blocks that can be eliminated in conditions" +} + +declare_lint_pass!(BlocksInConditions => [BLOCKS_IN_CONDITIONS]); + +const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; + +impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + let Some((cond, keyword, desc)) = higher::If::hir(expr) + .map(|hif| (hif.cond, "if", "an `if` condition")) + .or(if let ExprKind::Match(match_ex, _, MatchSource::Normal) = expr.kind { + Some((match_ex, "match", "a `match` scrutinee")) + } else { + None + }) + else { + return; + }; + let complex_block_message = &format!( + "in {desc}, avoid complex blocks or closures with blocks; \ + instead, move the block or closure higher and bind it with a `let`", + ); + + if let ExprKind::Block(block, _) = &cond.kind { + if block.rules == BlockCheckMode::DefaultBlock { + if block.stmts.is_empty() { + if let Some(ex) = &block.expr { + // don't dig into the expression here, just suggest that they remove + // the block + if expr.span.from_expansion() || ex.span.from_expansion() { + return; + } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BLOCKS_IN_CONDITIONS, + cond.span, + BRACED_EXPR_MESSAGE, + "try", + snippet_block_with_applicability(cx, ex.span, "..", Some(expr.span), &mut applicability) + .to_string(), + applicability, + ); + } + } else { + let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); + if span.from_expansion() || expr.span.from_expansion() { + return; + } + // move block higher + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BLOCKS_IN_CONDITIONS, + expr.span.with_hi(cond.span.hi()), + complex_block_message, + "try", + format!( + "let res = {}; {keyword} res", + snippet_block_with_applicability(cx, block.span, "..", Some(expr.span), &mut applicability), + ), + applicability, + ); + } + } + } else { + let _: Option<!> = for_each_expr(cond, |e| { + if let ExprKind::Closure(closure) = e.kind { + // do not lint if the closure is called using an iterator (see #1141) + if let Some(parent) = get_parent_expr(cx, e) + && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind + && let caller = cx.typeck_results().expr_ty(self_arg) + && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && implements_trait(cx, caller, iter_id, &[]) + { + return ControlFlow::Continue(Descend::No); + } + + let body = cx.tcx.hir().body(closure.body); + let ex = &body.value; + if let ExprKind::Block(block, _) = ex.kind { + if !body.value.span.from_expansion() && !block.stmts.is_empty() { + span_lint(cx, BLOCKS_IN_CONDITIONS, ex.span, complex_block_message); + return ControlFlow::Continue(Descend::No); + } + } + } + ControlFlow::Continue(Descend::Yes) + }); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs deleted file mode 100644 index 692309629b7..00000000000 --- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs +++ /dev/null @@ -1,139 +0,0 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::source::snippet_block_with_applicability; -use clippy_utils::ty::implements_trait; -use clippy_utils::visitors::{for_each_expr, Descend}; -use clippy_utils::{get_parent_expr, higher}; -use core::ops::ControlFlow; -use rustc_errors::Applicability; -use rustc_hir::{BlockCheckMode, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_session::declare_lint_pass; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `if` conditions that use blocks containing an - /// expression, statements or conditions that use closures with blocks. - /// - /// ### Why is this bad? - /// Style, using blocks in the condition makes it hard to read. - /// - /// ### Examples - /// ```no_run - /// # fn somefunc() -> bool { true }; - /// if { true } { /* ... */ } - /// - /// if { let x = somefunc(); x } { /* ... */ } - /// ``` - /// - /// Use instead: - /// ```no_run - /// # fn somefunc() -> bool { true }; - /// if true { /* ... */ } - /// - /// let res = { let x = somefunc(); x }; - /// if res { /* ... */ } - /// ``` - #[clippy::version = "1.45.0"] - pub BLOCKS_IN_IF_CONDITIONS, - style, - "useless or complex blocks that can be eliminated in conditions" -} - -declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]); - -const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; -const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ - instead, move the block or closure higher and bind it with a `let`"; - -impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - if let Some(higher::If { cond, .. }) = higher::If::hir(expr) { - if let ExprKind::Block(block, _) = &cond.kind { - if block.rules == BlockCheckMode::DefaultBlock { - if block.stmts.is_empty() { - if let Some(ex) = &block.expr { - // don't dig into the expression here, just suggest that they remove - // the block - if expr.span.from_expansion() || ex.span.from_expansion() { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BLOCKS_IN_IF_CONDITIONS, - cond.span, - BRACED_EXPR_MESSAGE, - "try", - format!( - "{}", - snippet_block_with_applicability( - cx, - ex.span, - "..", - Some(expr.span), - &mut applicability - ) - ), - applicability, - ); - } - } else { - let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); - if span.from_expansion() || expr.span.from_expansion() { - return; - } - // move block higher - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BLOCKS_IN_IF_CONDITIONS, - expr.span.with_hi(cond.span.hi()), - COMPLEX_BLOCK_MESSAGE, - "try", - format!( - "let res = {}; if res", - snippet_block_with_applicability( - cx, - block.span, - "..", - Some(expr.span), - &mut applicability - ), - ), - applicability, - ); - } - } - } else { - let _: Option<!> = for_each_expr(cond, |e| { - if let ExprKind::Closure(closure) = e.kind { - // do not lint if the closure is called using an iterator (see #1141) - if let Some(parent) = get_parent_expr(cx, e) - && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind - && let caller = cx.typeck_results().expr_ty(self_arg) - && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) - && implements_trait(cx, caller, iter_id, &[]) - { - return ControlFlow::Continue(Descend::No); - } - - let body = cx.tcx.hir().body(closure.body); - let ex = &body.value; - if let ExprKind::Block(block, _) = ex.kind { - if !body.value.span.from_expansion() && !block.stmts.is_empty() { - span_lint(cx, BLOCKS_IN_IF_CONDITIONS, ex.span, COMPLEX_BLOCK_MESSAGE); - return ControlFlow::Continue(Descend::No); - } - } - } - ControlFlow::Continue(Descend::Yes) - }); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index d3d4f3c41c8..0ca4a0e067d 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -57,7 +57,6 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..)) && let ref_ty = cx.typeck_results().expr_ty(deref_target) && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind() - && !is_from_proc_macro(cx, e) { if let Some(parent_expr) = get_parent_expr(cx, e) { if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) @@ -75,6 +74,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { return; } } + if is_from_proc_macro(cx, e) { + return; + } span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs index b55cd8833b7..8bfb7383f14 100644 --- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -9,11 +9,10 @@ use rustc_middle::ty::{self, Ty, TypeAndMut}; use super::AS_PTR_CAST_MUT; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) { - if let ty::RawPtr( - TypeAndMut { - mutbl: Mutability::Mut, ty: ptrty, - }, - ) = cast_to.kind() + if let ty::RawPtr(TypeAndMut { + mutbl: Mutability::Mut, + ty: ptrty, + }) = cast_to.kind() && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = cx.typeck_results().node_type(cast_expr.hir_id).kind() diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs index 0c555c1acc5..35e36e9ef3f 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -3,12 +3,29 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; +use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind}; +use rustc_hir_pretty::qpath_to_string; use rustc_lint::LateContext; use rustc_middle::ty::{self, TypeAndMut}; +use rustc_span::sym; use super::PTR_AS_PTR; +enum OmitFollowedCastReason<'a> { + None, + Null(&'a QPath<'a>), + NullMut(&'a QPath<'a>), +} + +impl OmitFollowedCastReason<'_> { + fn corresponding_item(&self) -> Option<&QPath<'_>> { + match self { + OmitFollowedCastReason::None => None, + OmitFollowedCastReason::Null(x) | OmitFollowedCastReason::NullMut(x) => Some(*x), + } + } +} + pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { if !msrv.meets(msrvs::POINTER_CAST) { return; @@ -25,7 +42,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { && to_pointee_ty.is_sized(cx.tcx, cx.param_env) { let mut app = Applicability::MachineApplicable; - let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app); let turbofish = match &cast_to_hir_ty.kind { TyKind::Infer => String::new(), TyKind::Ptr(mut_ty) => { @@ -41,13 +57,44 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { _ => return, }; + // following `cast` does not compile because it fails to infer what type is expected + // as type argument to `std::ptr::ptr_null` or `std::ptr::ptr_null_mut`, so + // we omit following `cast`: + let omit_cast = if let ExprKind::Call(func, []) = cast_expr.kind + && let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind + { + let method_defid = path.res.def_id(); + if cx.tcx.is_diagnostic_item(sym::ptr_null, method_defid) { + OmitFollowedCastReason::Null(qpath) + } else if cx.tcx.is_diagnostic_item(sym::ptr_null_mut, method_defid) { + OmitFollowedCastReason::NullMut(qpath) + } else { + OmitFollowedCastReason::None + } + } else { + OmitFollowedCastReason::None + }; + + let (help, final_suggestion) = if let Some(method) = omit_cast.corresponding_item() { + // don't force absolute path + let method = qpath_to_string(method); + ("try call directly", format!("{method}{turbofish}()")) + } else { + let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app); + + ( + "try `pointer::cast`, a safer alternative", + format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()), + ) + }; + span_lint_and_sugg( cx, PTR_AS_PTR, expr.span, "`as` casting between raw pointers without changing its mutability", - "try `pointer::cast`, a safer alternative", - format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()), + help, + final_suggestion, app, ); } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 3627096ed91..1220eb89013 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -63,7 +63,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO, crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO, crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO, - crate::blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS_INFO, + crate::blocks_in_conditions::BLOCKS_IN_CONDITIONS_INFO, crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO, crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO, crate::booleans::NONMINIMAL_BOOL_INFO, @@ -215,6 +215,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO, crate::indexing_slicing::INDEXING_SLICING_INFO, crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO, + crate::ineffective_open_options::INEFFECTIVE_OPEN_OPTIONS_INFO, crate::infinite_iter::INFINITE_ITER_INFO, crate::infinite_iter::MAYBE_INFINITE_ITER_INFO, crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO, @@ -265,6 +266,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, crate::loops::EXPLICIT_ITER_LOOP_INFO, crate::loops::FOR_KV_MAP_INFO, + crate::loops::INFINITE_LOOP_INFO, crate::loops::ITER_NEXT_LOOP_INFO, crate::loops::MANUAL_FIND_INFO, crate::loops::MANUAL_FLATTEN_INFO, @@ -598,6 +600,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::reference::DEREF_ADDROF_INFO, crate::regex::INVALID_REGEX_INFO, crate::regex::TRIVIAL_REGEX_INFO, + crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO, crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO, crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, crate::returns::LET_AND_RETURN_INFO, @@ -678,6 +681,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::unicode::INVISIBLE_CHARACTERS_INFO, crate::unicode::NON_ASCII_LITERAL_INFO, crate::unicode::UNICODE_NOT_NFC_INFO, + crate::uninhabited_references::UNINHABITED_REFERENCES_INFO, crate::uninit_vec::UNINIT_VEC_INFO, crate::unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD_INFO, crate::unit_types::LET_UNIT_VALUE_INFO, diff --git a/src/tools/clippy/clippy_lints/src/doc/markdown.rs b/src/tools/clippy/clippy_lints/src/doc/markdown.rs index c0b11eb0dd1..a58219c2946 100644 --- a/src/tools/clippy/clippy_lints/src/doc/markdown.rs +++ b/src/tools/clippy/clippy_lints/src/doc/markdown.rs @@ -9,11 +9,21 @@ use url::Url; use crate::doc::DOC_MARKDOWN; pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) { - for word in text.split(|c: char| c.is_whitespace() || c == '\'') { + for orig_word in text.split(|c: char| c.is_whitespace() || c == '\'') { // Trim punctuation as in `some comment (see foo::bar).` // ^^ // Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix. - let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':'); + let trim_pattern = |c: char| !c.is_alphanumeric() && c != ':'; + let mut word = orig_word.trim_end_matches(trim_pattern); + + // If word is immediately followed by `()`, claw it back. + if let Some(tmp_word) = orig_word.get(..word.len() + 2) + && tmp_word.ends_with("()") + { + word = tmp_word; + } + + word = word.trim_start_matches(trim_pattern); // Remove leading or trailing single `:` which may be part of a sentence. if word.starts_with(':') && !word.starts_with("::") { @@ -84,7 +94,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { return; } - if has_underscore(word) || word.contains("::") || is_camel_case(word) { + if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") { let mut applicability = Applicability::MachineApplicable; span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index f1366c434f4..e8c1e5db35e 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -15,7 +15,7 @@ declare_clippy_lint! { /// replaced with `(e)print!()` / `(e)println!()` /// /// ### Why is this bad? - /// Using `(e)println! is clearer and more concise + /// Using `(e)println!` is clearer and more concise /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 4811691c80d..0f5a9ea5d84 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -194,7 +194,12 @@ fn is_same_generics<'tcx>( .enumerate() .skip(1) // skip `Self` implicit arg .all(|(arg_index, arg)| { - if [implied_by_generics.host_effect_index, implied_generics.host_effect_index].contains(&Some(arg_index)) { + if [ + implied_by_generics.host_effect_index, + implied_generics.host_effect_index, + ] + .contains(&Some(arg_index)) + { // skip host effect params in determining whether generics are same return true; } diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs new file mode 100644 index 00000000000..955f90d4262 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs @@ -0,0 +1,95 @@ +use crate::methods::method_call; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::declare_lint_pass; +use rustc_span::{sym, BytePos, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks if both `.write(true)` and `.append(true)` methods are called + /// on a same `OpenOptions`. + /// + /// ### Why is this bad? + /// `.append(true)` already enables `write(true)`, making this one + /// superflous. + /// + /// ### Example + /// ```no_run + /// # use std::fs::OpenOptions; + /// let _ = OpenOptions::new() + /// .write(true) + /// .append(true) + /// .create(true) + /// .open("file.json"); + /// ``` + /// Use instead: + /// ```no_run + /// # use std::fs::OpenOptions; + /// let _ = OpenOptions::new() + /// .append(true) + /// .create(true) + /// .open("file.json"); + /// ``` + #[clippy::version = "1.76.0"] + pub INEFFECTIVE_OPEN_OPTIONS, + suspicious, + "usage of both `write(true)` and `append(true)` on same `OpenOptions`" +} + +declare_lint_pass!(IneffectiveOpenOptions => [INEFFECTIVE_OPEN_OPTIONS]); + +fn index_if_arg_is_boolean(args: &[Expr<'_>], call_span: Span) -> Option<Span> { + if let [arg] = args + && let ExprKind::Lit(lit) = peel_blocks(arg).kind + && lit.node == LitKind::Bool(true) + { + // The `.` is not included in the span so we cheat a little bit to include it as well. + Some(call_span.with_lo(call_span.lo() - BytePos(1))) + } else { + None + } +} + +impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let Some(("open", mut receiver, [_arg], _, _)) = method_call(expr) else { + return; + }; + let receiver_ty = cx.typeck_results().expr_ty(receiver); + match receiver_ty.peel_refs().kind() { + ty::Adt(adt, _) if cx.tcx.is_diagnostic_item(sym::FsOpenOptions, adt.did()) => {}, + _ => return, + } + + let mut append = None; + let mut write = None; + + while let Some((name, recv, args, _, span)) = method_call(receiver) { + if name == "append" { + append = index_if_arg_is_boolean(args, span); + } else if name == "write" { + write = index_if_arg_is_boolean(args, span); + } + receiver = recv; + } + + if let Some(write_span) = write + && append.is_some() + { + span_lint_and_sugg( + cx, + INEFFECTIVE_OPEN_OPTIONS, + write_span, + "unnecessary use of `.write(true)` because there is `.append(true)`", + "remove `.write(true)`", + String::new(), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 1c59b2df853..7758d6a58e6 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -50,6 +50,8 @@ extern crate clippy_utils; #[macro_use] extern crate declare_clippy_lint; +use std::collections::BTreeMap; + use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; @@ -74,7 +76,7 @@ mod assertions_on_result_states; mod async_yields_async; mod attrs; mod await_holding_invalid; -mod blocks_in_if_conditions; +mod blocks_in_conditions; mod bool_assert_comparison; mod bool_to_int_with_if; mod booleans; @@ -153,6 +155,7 @@ mod implied_bounds_in_impls; mod inconsistent_struct_constructor; mod index_refutable_slice; mod indexing_slicing; +mod ineffective_open_options; mod infinite_iter; mod inherent_impl; mod inherent_to_string; @@ -289,6 +292,7 @@ mod ref_option_ref; mod ref_patterns; mod reference; mod regex; +mod repeat_vec_with_capacity; mod reserve_after_initialization; mod return_self_not_must_use; mod returns; @@ -325,6 +329,7 @@ mod tuple_array_conversions; mod types; mod undocumented_unsafe_blocks; mod unicode; +mod uninhabited_references; mod uninit_vec; mod unit_return_expecting_ord; mod unit_types; @@ -653,7 +658,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); store.register_late_pass(|_| Box::new(attrs::Attributes)); - store.register_late_pass(|_| Box::new(blocks_in_if_conditions::BlocksInIfConditions)); + store.register_late_pass(|_| Box::new(blocks_in_conditions::BlocksInConditions)); store.register_late_pass(|_| Box::new(unicode::Unicode)); store.register_late_pass(|_| Box::new(uninit_vec::UninitVec)); store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)); @@ -722,6 +727,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { Box::new(vec::UselessVec { too_large_for_stack, msrv: msrv(), + span_to_lint_map: BTreeMap::new(), }) }); store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented)); @@ -1069,6 +1075,9 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)); store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType)); store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)); + store.register_late_pass(|_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity)); + store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences)); + store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs new file mode 100644 index 00000000000..9b88dd76e5c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs @@ -0,0 +1,125 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{fn_def_id, is_lint_allowed}; +use hir::intravisit::{walk_expr, Visitor}; +use hir::{Expr, ExprKind, FnRetTy, FnSig, Node}; +use rustc_ast::Label; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::INFINITE_LOOP; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'_>, + loop_block: &'tcx hir::Block<'_>, + label: Option<Label>, +) { + if is_lint_allowed(cx, INFINITE_LOOP, expr.hir_id) { + return; + } + + // Skip check if this loop is not in a function/method/closure. (In some weird case) + let Some(parent_fn_ret) = get_parent_fn_ret_ty(cx, expr) else { + return; + }; + // Or, its parent function is already returning `Never` + if matches!( + parent_fn_ret, + FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Never, + .. + }) + ) { + return; + } + + let mut loop_visitor = LoopVisitor { + cx, + label, + is_finite: false, + loop_depth: 0, + }; + loop_visitor.visit_block(loop_block); + + let is_finite_loop = loop_visitor.is_finite; + + if !is_finite_loop { + span_lint_and_then(cx, INFINITE_LOOP, expr.span, "infinite loop detected", |diag| { + if let FnRetTy::DefaultReturn(ret_span) = parent_fn_ret { + diag.span_suggestion( + ret_span, + "if this is intentional, consider specifing `!` as function return", + " -> !", + Applicability::MaybeIncorrect, + ); + } else { + diag.help("if this is not intended, try adding a `break` or `return` condition in the loop"); + } + }); + } +} + +fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<FnRetTy<'tcx>> { + for (_, parent_node) in cx.tcx.hir().parent_iter(expr.hir_id) { + match parent_node { + Node::Item(hir::Item { + kind: hir::ItemKind::Fn(FnSig { decl, .. }, _, _), + .. + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(FnSig { decl, .. }, _), + .. + }) + | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(FnSig { decl, .. }, _), + .. + }) + | Node::Expr(Expr { + kind: ExprKind::Closure(hir::Closure { fn_decl: decl, .. }), + .. + }) => return Some(decl.output), + _ => (), + } + } + None +} + +struct LoopVisitor<'hir, 'tcx> { + cx: &'hir LateContext<'tcx>, + label: Option<Label>, + loop_depth: usize, + is_finite: bool, +} + +impl<'hir> Visitor<'hir> for LoopVisitor<'hir, '_> { + fn visit_expr(&mut self, ex: &'hir Expr<'_>) { + match &ex.kind { + ExprKind::Break(hir::Destination { label, .. }, ..) => { + // Assuming breaks the loop when `loop_depth` is 0, + // as it could only means this `break` breaks current loop or any of its upper loop. + // Or, the depth is not zero but the label is matched. + if self.loop_depth == 0 || (label.is_some() && *label == self.label) { + self.is_finite = true; + } + }, + ExprKind::Ret(..) => self.is_finite = true, + ExprKind::Loop(..) => { + self.loop_depth += 1; + walk_expr(self, ex); + self.loop_depth = self.loop_depth.saturating_sub(1); + }, + _ => { + // Calls to a function that never return + if let Some(did) = fn_def_id(self.cx, ex) { + let fn_ret_ty = self.cx.tcx.fn_sig(did).skip_binder().output().skip_binder(); + if fn_ret_ty.is_never() { + self.is_finite = true; + return; + } + } + walk_expr(self, ex); + }, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 892336878c7..3c9bde86bb6 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -3,6 +3,7 @@ mod explicit_counter_loop; mod explicit_into_iter_loop; mod explicit_iter_loop; mod for_kv_map; +mod infinite_loop; mod iter_next_loop; mod manual_find; mod manual_flatten; @@ -635,6 +636,48 @@ declare_clippy_lint! { "checking for emptiness of a `Vec` in the loop condition and popping an element in the body" } +declare_clippy_lint! { + /// ### What it does + /// Checks for infinite loops in a function where the return type is not `!` + /// and lint accordingly. + /// + /// ### Why is this bad? + /// A loop should be gently exited somewhere, or at least mark its parent function as + /// never return (`!`). + /// + /// ### Example + /// ```no_run,ignore + /// fn run_forever() { + /// loop { + /// // do something + /// } + /// } + /// ``` + /// If infinite loops are as intended: + /// ```no_run,ignore + /// fn run_forever() -> ! { + /// loop { + /// // do something + /// } + /// } + /// ``` + /// Otherwise add a `break` or `return` condition: + /// ```no_run,ignore + /// fn run_forever() { + /// loop { + /// // do something + /// if condition { + /// break; + /// } + /// } + /// } + /// ``` + #[clippy::version = "1.75.0"] + pub INFINITE_LOOP, + restriction, + "possibly unintended infinite loop" +} + pub struct Loops { msrv: Msrv, enforce_iter_loop_reborrow: bool, @@ -669,6 +712,7 @@ impl_lint_pass!(Loops => [ MANUAL_FIND, MANUAL_WHILE_LET_SOME, UNUSED_ENUMERATE_INDEX, + INFINITE_LOOP, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -707,10 +751,11 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // check for `loop { if let {} else break }` that could be `while let` // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) - if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind { + if let ExprKind::Loop(block, label, LoopSource::Loop, _) = expr.kind { // also check for empty `loop {}` statements, skipping those in #[panic_handler] empty_loop::check(cx, expr, block); while_let_loop::check(cx, expr, block); + infinite_loop::check(cx, expr, block, label); } while_let_on_iterator::check(cx, expr); diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 28abecd0429..4acf46f73c9 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -13,8 +13,7 @@ use rustc_lint::LateContext; use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{sym, Symbol}; -use std::iter::{self}; -use std::mem; +use std::{iter, mem}; /// Checks for looping over a range and then indexing a sequence with it. /// The iteratee must be a range literal. diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 31cfb41640d..72cf1d7a354 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -105,7 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // case somebody does that for some reason && (is_infinity(const_1) && is_neg_infinity(const_2) || is_neg_infinity(const_1) && is_infinity(const_2)) - && !is_from_proc_macro(cx, expr) && let Some(local_snippet) = snippet_opt(cx, first.span) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { @@ -113,6 +112,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { (BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => Variant::ManualIsFinite, _ => return, }; + if is_from_proc_macro(cx, expr) { + return; + } span_lint_and_then(cx, variant.lint(), expr.span, variant.msg(), |diag| { match variant { diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs index 737c70496c2..781db4b97f0 100644 --- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs +++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs @@ -108,18 +108,16 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_ let arg_kind = &args[0].kind; if let ExprKind::Path(qpath) = &func.kind { - if let QPath::TypeRelative(_, _) = qpath { - // String::from(...) or String::try_from(...) - if let QPath::TypeRelative(ty, path_seg) = qpath - && [sym::from, sym::try_from].contains(&path_seg.ident.name) - && let TyKind::Path(qpath) = &ty.kind - && let QPath::Resolved(_, path) = qpath - && let [path_seg] = path.segments - && path_seg.ident.name == sym::String - && is_expr_kind_empty_str(arg_kind) - { - warn_then_suggest(cx, span); - } + // String::from(...) or String::try_from(...) + if let QPath::TypeRelative(ty, path_seg) = qpath + && [sym::from, sym::try_from].contains(&path_seg.ident.name) + && let TyKind::Path(qpath) = &ty.kind + && let QPath::Resolved(_, path) = qpath + && let [path_seg] = path.segments + && path_seg.ident.name == sym::String + && is_expr_kind_empty_str(arg_kind) + { + warn_then_suggest(cx, span); } else if let QPath::Resolved(_, path) = qpath { // From::from(...) or TryFrom::try_from(...) if let [path_seg1, path_seg2] = path.segments diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 25c681bb9d9..b4f60ffadd7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -3906,7 +3906,7 @@ impl_lint_pass!(Methods => [ ]); /// Extracts a method call name, args, and `Span` of the method name. -fn method_call<'tcx>( +pub fn method_call<'tcx>( recv: &'tcx hir::Expr<'tcx>, ) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> { if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 4a651396f14..4429f032605 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -19,10 +19,6 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) { - if is_from_proc_macro(cx, expr) { - return; - } - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); @@ -32,7 +28,7 @@ pub(super) fn check<'tcx>( let body = cx.tcx.hir().body(body); let body_expr = &body.value; - if usage::BindingUsageFinder::are_params_used(cx, body) { + if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 395b22ebc5d..c4775b6bd04 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -15,8 +15,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ - self, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, - TraitPredicate, Ty, + self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; use rustc_span::{sym, Symbol}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -359,6 +358,7 @@ fn get_input_traits_and_projections<'tcx>( (trait_predicates, projection_predicates) } +#[expect(clippy::too_many_lines)] fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool { for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) { match node { @@ -387,22 +387,21 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< if let Some((callee_def_id, call_generic_args, recv, call_args)) = get_callee_generic_args_and_args(cx, parent_expr) { - // FIXME: the `instantiate_identity()` below seems incorrect, since we eventually - // call `tcx.try_instantiate_and_normalize_erasing_regions` further down - // (i.e., we are explicitly not in the identity context). - let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); + let bound_fn_sig = cx.tcx.fn_sig(callee_def_id); + let fn_sig = bound_fn_sig.skip_binder(); if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) - && let Some(param_ty) = fn_sig.inputs().get(arg_index) - && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind() + && let param_ty = fn_sig.input(arg_index).skip_binder() + && let ty::Param(ParamTy { index: param_index , ..}) = *param_ty.kind() // https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021 - && (*param_index as usize) < call_generic_args.len() + && (param_index as usize) < call_generic_args.len() { if fn_sig + .skip_binder() .inputs() .iter() .enumerate() .filter(|(i, _)| *i != arg_index) - .any(|(_, ty)| ty.contains(*param_ty)) + .any(|(_, ty)| ty.contains(param_ty)) { return false; } @@ -414,7 +413,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< .iter() .filter(|predicate| { if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && trait_predicate.trait_ref.self_ty() == *param_ty + && trait_predicate.trait_ref.self_ty() == param_ty { true } else { @@ -425,7 +424,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< let new_subst = cx .tcx .mk_args_from_iter(call_generic_args.iter().enumerate().map(|(i, t)| { - if i == (*param_index as usize) { + if i == param_index as usize { GenericArg::from(ty) } else { t @@ -433,7 +432,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< })); if trait_predicates.any(|predicate| { - let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, new_subst); + let predicate = bound_fn_sig.rebind(predicate).instantiate(cx.tcx, new_subst); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); !cx.tcx .infer_ctxt() @@ -443,12 +442,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< return false; } - let output_ty = fn_sig.output(); - if output_ty.contains(*param_ty) { + let output_ty = cx.tcx.instantiate_bound_regions_with_erased(fn_sig.output()); + if output_ty.contains(param_ty) { if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions( new_subst, cx.param_env, - EarlyBinder::bind(output_ty), + bound_fn_sig.rebind(output_ty), ) { expr = parent_expr; ty = new_ty; diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index 8f2a5390781..0f18e943451 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -52,7 +52,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// fn sum(v: &[u8]) -> u8 { - /// assert!(v.len() > 4); + /// assert!(v.len() > 3); /// // no bounds checks /// v[0] + v[1] + v[2] + v[3] /// } @@ -87,11 +87,14 @@ enum LengthComparison { LengthLessThanOrEqualInt, /// `5 <= v.len()` IntLessThanOrEqualLength, + /// `5 == v.len()` + /// `v.len() == 5` + LengthEqualInt, } /// Extracts parts out of a length comparison expression. /// -/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, `v.len()`))` +/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, v.len()))` fn len_comparison<'hir>( bin_op: BinOp, left: &'hir Expr<'hir>, @@ -114,6 +117,8 @@ fn len_comparison<'hir>( (Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, *right as usize, left)), (Rel::Le, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanOrEqualLength, *left as usize, right)), (Rel::Le, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanOrEqualInt, *right as usize, left)), + (Rel::Eq, int_lit_pat!(left), _) => Some((LengthComparison::LengthEqualInt, *left as usize, right)), + (Rel::Eq, _, int_lit_pat!(right)) => Some((LengthComparison::LengthEqualInt, *right as usize, left)), _ => None, } } @@ -316,11 +321,11 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>> continue; }; - match entry { + match *entry { IndexEntry::AssertWithIndex { highest_index, asserted_len, - indexes, + ref indexes, comparison, assert_span, slice, @@ -343,6 +348,12 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>> "assert!({}.len() > {highest_index})", snippet(cx, slice.span, "..") )), + // `highest_index` here is rather a length, so we need to add 1 to it + LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => Some(format!( + "assert!({}.len() == {})", + snippet(cx, slice.span, ".."), + highest_index + 1 + )), _ => None, }; @@ -354,7 +365,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>> indexes, |diag| { diag.span_suggestion( - *assert_span, + assert_span, "provide the highest index that is indexed with", sugg, Applicability::MachineApplicable, @@ -364,7 +375,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>> } }, IndexEntry::IndexWithoutAssert { - indexes, + ref indexes, highest_index, slice, } if indexes.len() > 1 => { diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 34119c6911c..3ff40081c47 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -213,7 +213,9 @@ fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { if parent_id == cur_id { break; } - let Some(parent_node) = vis.cx.tcx.opt_hir_node(parent_id) else { break }; + let Some(parent_node) = vis.cx.tcx.opt_hir_node(parent_id) else { + break; + }; let stop_early = match parent_node { Node::Expr(expr) => check_expr(vis, expr), diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index 85166b0dd95..a32bca3d038 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_copy; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -169,6 +169,7 @@ fn needless_borrow_count<'tcx>( ) -> usize { let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); + let drop_trait_def_id = cx.tcx.lang_items().drop_trait(); let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder(); let predicates = cx.tcx.param_env(fn_id).caller_bounds(); @@ -223,7 +224,14 @@ fn needless_borrow_count<'tcx>( // elements are modified each time `check_referent` is called. let mut args_with_referent_ty = callee_args.to_vec(); - let mut check_reference_and_referent = |reference, referent| { + let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| { + if let ExprKind::Field(base, _) = &referent.kind { + let base_ty = cx.typeck_results().expr_ty(base); + if drop_trait_def_id.map_or(false, |id| implements_trait(cx, base_ty, id, &[])) { + return false; + } + } + let referent_ty = cx.typeck_results().expr_ty(referent); if !is_copy(cx, referent_ty) diff --git a/src/tools/clippy/clippy_lints/src/needless_if.rs b/src/tools/clippy/clippy_lints/src/needless_if.rs index 41d05d72284..51bee4b51f6 100644 --- a/src/tools/clippy/clippy_lints/src/needless_if.rs +++ b/src/tools/clippy/clippy_lints/src/needless_if.rs @@ -44,7 +44,6 @@ impl LateLintPass<'_> for NeedlessIf { && block.stmts.is_empty() && block.expr.is_none() && !in_external_macro(cx.sess(), expr.span) - && !is_from_proc_macro(cx, expr) && let Some(then_snippet) = snippet_opt(cx, then.span) // Ignore // - empty macro expansions @@ -53,6 +52,7 @@ impl LateLintPass<'_> for NeedlessIf { // - #[cfg]'d out code && then_snippet.chars().all(|ch| matches!(ch, '{' | '}') || ch.is_ascii_whitespace()) && let Some(cond_snippet) = snippet_opt(cx, cond.span) + && !is_from_proc_macro(cx, expr) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 6e65dd628a4..5978da83199 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -87,6 +87,17 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { if let StmtKind::Semi(expr) = stmt.kind { + // move `expr.span.from_expansion()` ahead + if expr.span.from_expansion() { + return false; + } + let expr = peel_blocks(expr); + + if is_operator_overridden(cx, expr) { + // Return `true`, to prevent `check_unnecessary_operation` from + // linting on this statement as well. + return true; + } if has_no_effect(cx, expr) { span_lint_hir_and_then( cx, @@ -153,11 +164,26 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { false } -fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if expr.span.from_expansion() { - return false; +fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + // It's very hard or impossable to check whether overridden operator have side-effect this lint. + // So, this function assume user-defined operator is overridden with an side-effect. + // The definition of user-defined structure here is ADT-type, + // Althrough this will weaken the ability of this lint, less error lint-fix happen. + match expr.kind { + ExprKind::Binary(..) | ExprKind::Unary(..) => { + // No need to check type of `lhs` and `rhs` + // because if the operator is overridden, at least one operand is ADT type + + // reference: rust/compiler/rustc_middle/src/ty/typeck_results.rs: `is_method_call`. + // use this function to check whether operator is overridden in `ExprKind::{Binary, Unary}`. + cx.typeck_results().is_method_call(expr) + }, + _ => false, } - match peel_blocks(expr).kind { +} + +fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match expr.kind { ExprKind::Lit(..) | ExprKind::Closure { .. } => true, ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)), ExprKind::Index(a, b, _) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 1a2b20bf438..c081dec9b6b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -132,7 +132,11 @@ impl ArithmeticSideEffects { } // Common entry-point to avoid code duplication. - fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + fn issue_lint<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_from_proc_macro(cx, expr) { + return; + } + let msg = "arithmetic operation that can potentially result in unexpected side-effects"; span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg); self.expr_span = Some(expr.span); @@ -160,10 +164,10 @@ impl ArithmeticSideEffects { fn manage_bin_ops<'tcx>( &mut self, cx: &LateContext<'tcx>, - expr: &hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'_>, op: &Spanned<hir::BinOpKind>, - lhs: &hir::Expr<'tcx>, - rhs: &hir::Expr<'tcx>, + lhs: &'tcx hir::Expr<'_>, + rhs: &'tcx hir::Expr<'_>, ) { if constant_simple(cx, cx.typeck_results(), expr).is_some() { return; @@ -236,10 +240,10 @@ impl ArithmeticSideEffects { /// provided input. fn manage_method_call<'tcx>( &mut self, - args: &[hir::Expr<'tcx>], + args: &'tcx [hir::Expr<'_>], cx: &LateContext<'tcx>, - ps: &hir::PathSegment<'tcx>, - receiver: &hir::Expr<'tcx>, + ps: &'tcx hir::PathSegment<'_>, + receiver: &'tcx hir::Expr<'_>, ) { let Some(arg) = args.first() else { return; @@ -264,8 +268,8 @@ impl ArithmeticSideEffects { fn manage_unary_ops<'tcx>( &mut self, cx: &LateContext<'tcx>, - expr: &hir::Expr<'tcx>, - un_expr: &hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'_>, + un_expr: &'tcx hir::Expr<'_>, un_op: hir::UnOp, ) { let hir::UnOp::Neg = un_op else { @@ -287,14 +291,13 @@ impl ArithmeticSideEffects { fn should_skip_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) -> bool { is_lint_allowed(cx, ARITHMETIC_SIDE_EFFECTS, expr.hir_id) - || is_from_proc_macro(cx, expr) || self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) } } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if self.should_skip_expr(cx, expr) { return; } diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs index 9f5897d2ca7..85979903b58 100644 --- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs +++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs @@ -4,8 +4,9 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), ("clippy::blacklisted_name", "clippy::disallowed_names"), - ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"), - ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"), + ("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"), + ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"), + ("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"), ("clippy::box_vec", "clippy::box_collection"), ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs new file mode 100644 index 00000000000..5a4933a3fce --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs @@ -0,0 +1,114 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::VecArgs; +use clippy_utils::macros::root_macro_call; +use clippy_utils::source::snippet; +use clippy_utils::{expr_or_init, fn_def_id, match_def_path, paths}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Looks for patterns such as `vec![Vec::with_capacity(x); n]` or `iter::repeat(Vec::with_capacity(x))`. + /// + /// ### Why is this bad? + /// These constructs work by cloning the element, but cloning a `Vec<_>` does not + /// respect the old vector's capacity and effectively discards it. + /// + /// This makes `iter::repeat(Vec::with_capacity(x))` especially suspicious because the user most certainly + /// expected that the yielded `Vec<_>` will have the requested capacity, otherwise one can simply write + /// `iter::repeat(Vec::new())` instead and it will have the same effect. + /// + /// Similarly for `vec![x; n]`, the element `x` is cloned to fill the vec. + /// Unlike `iter::repeat` however, the vec repeat macro does not have to clone the value `n` times + /// but just `n - 1` times, because it can reuse the passed value for the last slot. + /// That means that the last `Vec<_>` gets the requested capacity but all other ones do not. + /// + /// ### Example + /// ```rust + /// # use std::iter; + /// + /// let _: Vec<Vec<u8>> = vec![Vec::with_capacity(42); 123]; + /// let _: Vec<Vec<u8>> = iter::repeat(Vec::with_capacity(42)).take(123).collect(); + /// ``` + /// Use instead: + /// ```rust + /// # use std::iter; + /// + /// let _: Vec<Vec<u8>> = iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect(); + /// // ^^^ this closure executes 123 times + /// // and the vecs will have the expected capacity + /// ``` + #[clippy::version = "1.74.0"] + pub REPEAT_VEC_WITH_CAPACITY, + suspicious, + "repeating a `Vec::with_capacity` expression which does not retain capacity" +} + +declare_lint_pass!(RepeatVecWithCapacity => [REPEAT_VEC_WITH_CAPACITY]); + +fn emit_lint(cx: &LateContext<'_>, span: Span, kind: &str, note: &'static str, sugg_msg: &'static str, sugg: String) { + span_lint_and_then( + cx, + REPEAT_VEC_WITH_CAPACITY, + span, + &format!("repeating `Vec::with_capacity` using `{kind}`, which does not retain capacity"), + |diag| { + diag.note(note); + diag.span_suggestion_verbose(span, sugg_msg, sugg, Applicability::MaybeIncorrect); + }, + ); +} + +/// Checks `vec![Vec::with_capacity(x); n]` +fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(mac_call) = root_macro_call(expr.span) + && cx.tcx.is_diagnostic_item(sym::vec_macro, mac_call.def_id) + && let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr) + && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY)) + && !len_expr.span.from_expansion() + && let Some(Constant::Int(2..)) = constant(cx, cx.typeck_results(), expr_or_init(cx, len_expr)) + { + emit_lint( + cx, + expr.span.source_callsite(), + "vec![x; n]", + "only the last `Vec` will have the capacity", + "if you intended to initialize multiple `Vec`s with an initial capacity, try", + format!( + "(0..{}).map(|_| {}).collect::<Vec<_>>()", + snippet(cx, len_expr.span, ""), + snippet(cx, repeat_expr.span, "..") + ), + ); + } +} + +/// Checks `iter::repeat(Vec::with_capacity(x))` +fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) { + if !expr.span.from_expansion() + && fn_def_id(cx, expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::iter_repeat, did)) + && let ExprKind::Call(_, [repeat_expr]) = expr.kind + && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY)) + && !repeat_expr.span.from_expansion() + { + emit_lint( + cx, + expr.span, + "iter::repeat", + "none of the yielded `Vec`s will have the requested capacity", + "if you intended to create an iterator that yields `Vec`s with an initial capacity, try", + format!("std::iter::repeat_with(|| {})", snippet(cx, repeat_expr.span, "..")), + ); + } +} + +impl LateLintPass<'_> for RepeatVecWithCapacity { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + check_vec_macro(cx, expr); + check_repeat_fn(cx, expr); + } +} diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs index 1316343e0c2..7a351dab2d4 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl { .. }) && let item = cx.tcx.hir().item(id) && let ItemKind::Impl(Impl { - items, - of_trait, - self_ty, - .. - }) = &item.kind + items, + of_trait, + self_ty, + .. + }) = &item.kind && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { if !map.contains_key(res) { @@ -75,24 +75,24 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { match of_trait { Some(trait_ref) => { - let mut methods_in_trait: BTreeSet<Symbol> = if let Some(Node::TraitRef(TraitRef { - path, .. - })) = cx.tcx.opt_hir_node(trait_ref.hir_ref_id) - && let Res::Def(DefKind::Trait, did) = path.res - { - // FIXME: if - // `rustc_middle::ty::assoc::AssocItems::items` is public, - // we can iterate its keys instead of `in_definition_order`, - // which's more efficient - cx.tcx - .associated_items(did) - .in_definition_order() - .filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn)) - .map(|assoc_item| assoc_item.name) - .collect() - } else { - BTreeSet::new() - }; + let mut methods_in_trait: BTreeSet<Symbol> = + if let Some(Node::TraitRef(TraitRef { path, .. })) = + cx.tcx.opt_hir_node(trait_ref.hir_ref_id) + && let Res::Def(DefKind::Trait, did) = path.res + { + // FIXME: if + // `rustc_middle::ty::assoc::AssocItems::items` is public, + // we can iterate its keys instead of `in_definition_order`, + // which's more efficient + cx.tcx + .associated_items(did) + .in_definition_order() + .filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn)) + .map(|assoc_item| assoc_item.name) + .collect() + } else { + BTreeSet::new() + }; let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { if let Some((impl_span, hir_id)) = existing_name.impl_methods.get(&method_name) { diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs index 396d2717a13..8e181c3ccc7 100644 --- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs +++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs @@ -72,8 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for SingleCallFn { ) { if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) || in_external_macro(cx.sess(), span) - || is_from_proc_macro(cx, &(&kind, body, cx.tcx.local_def_id_to_hir_id(def_id), span)) || is_in_test_function(cx.tcx, body.value.hir_id) + || is_from_proc_macro(cx, &(&kind, body, cx.tcx.local_def_id_to_hir_id(def_id), span)) { return; } diff --git a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs new file mode 100644 index 00000000000..d41576cadad --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs @@ -0,0 +1,84 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnRetTy, TyKind, UnOp}; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::declare_lint_pass; +use rustc_span::def_id::LocalDefId; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// It detects references to uninhabited types, such as `!` and + /// warns when those are either dereferenced or returned from a function. + /// + /// ### Why is this bad? + /// Dereferencing a reference to an uninhabited type would create + /// an instance of such a type, which cannot exist. This constitutes + /// undefined behaviour. Such a reference could have been created + /// by `unsafe` code. + /// + /// ### Example + /// The following function can return a reference to an uninhabited type + /// (`Infallible`) because it uses `unsafe` code to create it. However, + /// the user of such a function could dereference the return value and + /// trigger an undefined behavior from safe code. + /// + /// ```no_run + /// fn create_ref() -> &'static std::convert::Infallible { + /// unsafe { std::mem::transmute(&()) } + /// } + /// ``` + #[clippy::version = "1.76.0"] + pub UNINHABITED_REFERENCES, + suspicious, + "reference to uninhabited type" +} + +declare_lint_pass!(UninhabitedReferences => [UNINHABITED_REFERENCES]); + +impl LateLintPass<'_> for UninhabitedReferences { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if in_external_macro(cx.tcx.sess, expr.span) { + return; + } + + if let ExprKind::Unary(UnOp::Deref, _) = expr.kind { + let ty = cx.typeck_results().expr_ty_adjusted(expr); + if ty.is_privately_uninhabited(cx.tcx, cx.param_env) { + span_lint( + cx, + UNINHABITED_REFERENCES, + expr.span, + "dereferencing a reference to an uninhabited type is undefined behavior", + ); + } + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'_>, + kind: FnKind<'_>, + fndecl: &'_ FnDecl<'_>, + _: &'_ Body<'_>, + span: Span, + _: LocalDefId, + ) { + if in_external_macro(cx.tcx.sess, span) || matches!(kind, FnKind::Closure) { + return; + } + if let FnRetTy::Return(hir_ty) = fndecl.output + && let TyKind::Ref(_, mut_ty) = hir_ty.kind + && hir_ty_to_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.param_env) + { + span_lint( + cx, + UNINHABITED_REFERENCES, + hir_ty.span, + "dereferencing a reference to an uninhabited type would be undefined behavior", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs index 9979e02297e..2b0d2d61d20 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { }; match constructor_symbol { sym::Some | sym::Ok if path.ident.name == rustc_span::sym::map => (), - sym::Err if path.ident.name == sym!(map_err) => (), + sym::Err if path.ident.name == sym::map_err => (), _ => return, } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 373b076f92c..fae1b90ace2 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -28,12 +28,12 @@ use rustc_span::{sym, Loc, Span, Symbol}; use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; use std::collections::{BTreeSet, BinaryHeap}; -use std::fmt; use std::fmt::Write as _; use std::fs::{self, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::process::Command; +use std::{env, fmt}; /// This is the json output file of the lint collector. const JSON_OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; @@ -415,7 +415,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}"); - let mut cmd = Command::new("cargo"); + let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into())); cmd.current_dir(clippy_project_root) .env("CARGO_INCREMENTAL", "0") diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 31a81a52497..6e449dc9806 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -218,7 +218,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { let parent_id = cx.tcx.hir().parent_id(hir_id); - if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir_node(parent_id) { + if let Node::Local(Local { init: Some(init), .. }) = cx.tcx.hir_node(parent_id) { path_to_matched_type(cx, init) } else { None @@ -246,7 +246,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> { let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { let &alloc = alloc.provenance().ptrs().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { (alloc.inner(), ty) } else { return None; @@ -264,7 +264,7 @@ fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation .ptrs() .values() .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { let alloc = alloc.inner(); str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) .ok() diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index ba958c5b392..5e13c73f035 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -1,25 +1,27 @@ +use std::collections::BTreeMap; use std::ops::ControlFlow; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, higher, is_trait_method}; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Node, PatKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{sym, DesugaringKind, Span}; #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct UselessVec { pub too_large_for_stack: u64, pub msrv: Msrv, + pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>, } declare_clippy_lint! { @@ -69,64 +71,88 @@ pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]` - if adjusts_to_slice(cx, expr) - && let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) - { - let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind { - // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.) - (SuggestedType::SliceRef(mutability), expr.span) - } else { - // `expr` is the `vec![_]` expansion, so suggest `[_]` - // and also use the span of the actual `vec![_]` expression - (SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site) - }; - - self.check_vec_macro(cx, &vec_args, span, suggest_slice); - } + if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) { + // search for `let foo = vec![_]` expressions where all uses of `foo` + // adjust to slices or call a method that exist on slices (e.g. len) + if let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id) + // for now ignore locals with type annotations. + // this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..]; + && local.ty.is_none() + && let PatKind::Binding(_, id, ..) = local.pat.kind + && is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(expr.peel_borrows()))) + { + let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| { + // allow indexing into a vec and some set of allowed method calls that exist on slices, too + if let Some(parent) = get_parent_expr(cx, expr) + && (adjusts_to_slice(cx, expr) + || matches!(parent.kind, ExprKind::Index(..)) + || is_allowed_vec_method(cx, parent)) + { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(()) + } + }) + .is_continue(); - // search for `let foo = vec![_]` expressions where all uses of `foo` - // adjust to slices or call a method that exist on slices (e.g. len) - if let Some(vec_args) = higher::VecArgs::hir(cx, expr) - && let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id) - // for now ignore locals with type annotations. - // this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..]; - && local.ty.is_none() - && let PatKind::Binding(_, id, ..) = local.pat.kind - && is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(expr))) - { - let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| { - // allow indexing into a vec and some set of allowed method calls that exist on slices, too - if let Some(parent) = get_parent_expr(cx, expr) - && (adjusts_to_slice(cx, expr) - || matches!(parent.kind, ExprKind::Index(..)) - || is_allowed_vec_method(cx, parent)) - { - ControlFlow::Continue(()) + let span = expr.span.ctxt().outer_expn_data().call_site; + if only_slice_uses { + self.check_vec_macro(cx, &vec_args, span, expr.hir_id, SuggestedType::Array); } else { - ControlFlow::Break(()) + self.span_to_lint_map.insert(span, None); + } + } + // if the local pattern has a specified type, do not lint. + else if let Some(_) = higher::VecArgs::hir(cx, expr) + && let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id) + && local.ty.is_some() + { + let span = expr.span.ctxt().outer_expn_data().call_site; + self.span_to_lint_map.insert(span, None); + } + // search for `for _ in vec![...]` + else if let Some(parent) = get_parent_expr(cx, expr) + && parent.span.is_desugaring(DesugaringKind::ForLoop) + && self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR) + { + // report the error around the `vec!` not inside `<std macros>:` + let span = expr.span.ctxt().outer_expn_data().call_site; + self.check_vec_macro(cx, &vec_args, span, expr.hir_id, SuggestedType::Array); + } + // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]` + else { + let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind { + // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.) + (SuggestedType::SliceRef(mutability), expr.span) + } else { + // `expr` is the `vec![_]` expansion, so suggest `[_]` + // and also use the span of the actual `vec![_]` expression + (SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site) + }; + + if adjusts_to_slice(cx, expr) { + self.check_vec_macro(cx, &vec_args, span, expr.hir_id, suggest_slice); + } else { + self.span_to_lint_map.insert(span, None); } - }) - .is_continue(); - - if only_slice_uses { - self.check_vec_macro( - cx, - &vec_args, - expr.span.ctxt().outer_expn_data().call_site, - SuggestedType::Array, - ); } } + } - // search for `for _ in vec![…]` - if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) - && let Some(vec_args) = higher::VecArgs::hir(cx, arg) - && self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR) - { - // report the error around the `vec!` not inside `<std macros>:` - let span = arg.span.ctxt().outer_expn_data().call_site; - self.check_vec_macro(cx, &vec_args, span, SuggestedType::Array); + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for (span, lint_opt) in &self.span_to_lint_map { + if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt { + let help_msg = format!( + "you can use {} directly", + match suggest_slice { + SuggestedType::SliceRef(_) => "a slice", + SuggestedType::Array => "an array", + } + ); + span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| { + diag.span_suggestion(*span, help_msg, snippet, *applicability); + }); + } } } @@ -134,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { } #[derive(Copy, Clone)] -enum SuggestedType { +pub(crate) enum SuggestedType { /// Suggest using a slice `&[..]` / `&mut [..]` SliceRef(Mutability), /// Suggest using an array: `[..]` @@ -147,6 +173,7 @@ impl UselessVec { cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span, + hir_id: HirId, suggest_slice: SuggestedType, ) { if span.from_expansion() { @@ -204,21 +231,9 @@ impl UselessVec { }, }; - span_lint_and_sugg( - cx, - USELESS_VEC, - span, - "useless use of `vec!`", - &format!( - "you can use {} directly", - match suggest_slice { - SuggestedType::SliceRef(_) => "a slice", - SuggestedType::Array => "an array", - } - ), - snippet, - applicability, - ); + self.span_to_lint_map + .entry(span) + .or_insert(Some((hir_id, suggest_slice, snippet, applicability))); } } diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 47237de4fef..e36f2fa87a7 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -206,7 +206,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { ) => { eq_closure_binder(lb, rb) && lc == rc - && la.map_or(false, |la| la.is_async()) == ra.map_or(false, |ra| ra.is_async()) + && la.map_or(false, CoroutineKind::is_async) == ra.map_or(false, CoroutineKind::is_async) && lm == rm && eq_fn_decl(lf, rf) && eq_expr(le, re) @@ -564,13 +564,16 @@ pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool { } fn eq_opt_coroutine_kind(l: Option<CoroutineKind>, r: Option<CoroutineKind>) -> bool { - match (l, r) { + matches!( + (l, r), (Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. })) - | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) - | (Some(CoroutineKind::AsyncGen { .. }), Some(CoroutineKind::AsyncGen { .. })) => true, - (None, None) => true, - _ => false, - } + | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) + | ( + Some(CoroutineKind::AsyncGen { .. }), + Some(CoroutineKind::AsyncGen { .. }) + ) + | (None, None) + ) } pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 3f68b6d75b5..470d31fa3e1 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -12,14 +12,14 @@ //! code was written, and check if the span contains that text. Note this will only work correctly //! if the span is not from a `macro_rules` based macro. -use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, UintTy}; +use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy}; use rustc_ast::token::CommentKind; use rustc_ast::AttrStyle; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId, Impl, ImplItem, - ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem, TraitItemKind, Ty, - TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource, + Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, + ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem, + TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource, }; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::TyCtxt; @@ -33,8 +33,6 @@ use rustc_target::spec::abi::Abi; pub enum Pat { /// A single string. Str(&'static str), - /// A single string. - OwnedStr(String), /// Any of the given strings. MultiStr(&'static [&'static str]), /// Any of the given strings. @@ -59,14 +57,12 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ','); (match start_pat { Pat::Str(text) => start_str.starts_with(text), - Pat::OwnedStr(text) => start_str.starts_with(&text), Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)), Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)), Pat::Sym(sym) => start_str.starts_with(sym.as_str()), Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit), } && match end_pat { Pat::Str(text) => end_str.ends_with(text), - Pat::OwnedStr(text) => end_str.starts_with(&text), Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)), Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)), Pat::Sym(sym) => end_str.ends_with(sym.as_str()), @@ -125,6 +121,8 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) { fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) { match e.kind { ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")), + // Parenthesis are trimmed from the text before the search patterns are matched. + // See: `span_matches_pat` ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1), ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat(tcx, e).1), @@ -286,23 +284,17 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) { match attr.kind { AttrKind::Normal(..) => { - let mut pat = if matches!(attr.style, AttrStyle::Outer) { - (Pat::Str("#["), Pat::Str("]")) - } else { - (Pat::Str("#!["), Pat::Str("]")) - }; - - if let Some(ident) = attr.ident() - && let Pat::Str(old_pat) = pat.0 - { + if let Some(ident) = attr.ident() { // TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of // refactoring // NOTE: This will likely have false positives, like `allow = 1` - pat.0 = Pat::OwnedMultiStr(vec![ident.to_string(), old_pat.to_owned()]); - pat.1 = Pat::Str(""); + ( + Pat::OwnedMultiStr(vec![ident.to_string(), "#".to_owned()]), + Pat::Str(""), + ) + } else { + (Pat::Str("#"), Pat::Str("]")) } - - pat }, AttrKind::DocComment(_kind @ CommentKind::Line, ..) => { if matches!(attr.style, AttrStyle::Outer) { @@ -324,32 +316,42 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) { fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { match ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), - TyKind::Ptr(MutTy { mutbl, ty }) => ( - if mutbl.is_mut() { - Pat::Str("*const") - } else { - Pat::Str("*mut") - }, - ty_search_pat(ty).1, - ), + TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1), TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1), TyKind::BareFn(bare_fn) => ( - Pat::OwnedStr(format!("{}{} fn", bare_fn.unsafety.prefix_str(), bare_fn.abi.name())), - ty_search_pat(ty).1, + if bare_fn.unsafety == Unsafety::Unsafe { + Pat::Str("unsafe") + } else if bare_fn.abi != Abi::Rust { + Pat::Str("extern") + } else { + Pat::MultiStr(&["fn", "extern"]) + }, + match bare_fn.decl.output { + FnRetTy::DefaultReturn(_) => { + if let [.., ty] = bare_fn.decl.inputs { + ty_search_pat(ty).1 + } else { + Pat::Str("(") + } + }, + FnRetTy::Return(ty) => ty_search_pat(ty).1, + }, ), - TyKind::Never => (Pat::Str("!"), Pat::Str("")), - TyKind::Tup(..) => (Pat::Str("("), Pat::Str(")")), + TyKind::Never => (Pat::Str("!"), Pat::Str("!")), + // Parenthesis are trimmed from the text before the search patterns are matched. + // See: `span_matches_pat` + TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), + TyKind::Tup([ty]) => ty_search_pat(ty), + TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1), TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), - // NOTE: This is missing `TraitObject`. It will always return true then. + TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), + TyKind::TraitObject(_, _, TraitObjectSyntax::Dyn) => (Pat::Str("dyn"), Pat::Str("")), + // NOTE: `TraitObject` is incomplete. It will always return true then. _ => (Pat::Str(""), Pat::Str("")), } } -fn ident_search_pat(ident: Ident) -> (Pat, Pat) { - (Pat::OwnedStr(ident.name.as_str().to_owned()), Pat::Str("")) -} - pub trait WithSearchPat<'cx> { type Context: LintContext; fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat); @@ -408,7 +410,7 @@ impl<'cx> WithSearchPat<'cx> for Ident { type Context = LateContext<'cx>; fn search_pat(&self, _cx: &Self::Context) -> (Pat, Pat) { - ident_search_pat(*self) + (Pat::Sym(self.name), Pat::Sym(self.name)) } fn span(&self) -> Span { diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 868c92f2f54..61d0663aa83 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -214,8 +214,17 @@ pub fn implements_trait<'tcx>( trait_id: DefId, args: &[GenericArg<'tcx>], ) -> bool { - let callee_id = cx.enclosing_body.map(|body| cx.tcx.hir().body_owner(body).owner.to_def_id()); - implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, callee_id, args.iter().map(|&x| Some(x))) + let callee_id = cx + .enclosing_body + .map(|body| cx.tcx.hir().body_owner(body).owner.to_def_id()); + implements_trait_with_env_from_iter( + cx.tcx, + cx.param_env, + ty, + trait_id, + callee_id, + args.iter().map(|&x| Some(x)), + ) } /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. @@ -227,7 +236,14 @@ pub fn implements_trait_with_env<'tcx>( callee_id: DefId, args: &[GenericArg<'tcx>], ) -> bool { - implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, Some(callee_id), args.iter().map(|&x| Some(x))) + implements_trait_with_env_from_iter( + tcx, + param_env, + ty, + trait_id, + Some(callee_id), + args.iter().map(|&x| Some(x)), + ) } /// Same as `implements_trait_from_env` but takes the arguments as an iterator. @@ -248,19 +264,28 @@ pub fn implements_trait_with_env_from_iter<'tcx>( } let infcx = tcx.infer_ctxt().build(); - let args = args.into_iter().map(|arg| { - arg.into().unwrap_or_else(|| { - let orig = TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }; - infcx.next_ty_var(orig).into() + let args = args + .into_iter() + .map(|arg| { + arg.into().unwrap_or_else(|| { + let orig = TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }; + infcx.next_ty_var(orig).into() + }) }) - }).collect::<Vec<_>>(); + .collect::<Vec<_>>(); // If an effect arg was not specified, we need to specify it. - let effect_arg = if tcx.generics_of(trait_id).host_effect_index.is_some_and(|x| args.get(x - 1).is_none()) { - Some(GenericArg::from(callee_id.map(|def_id| tcx.expected_host_effect_param_for_body(def_id)).unwrap_or(tcx.consts.true_))) + let effect_arg = if tcx + .generics_of(trait_id) + .host_effect_index + .is_some_and(|x| args.get(x - 1).is_none()) + { + Some(GenericArg::from(callee_id.map_or(tcx.consts.true_, |def_id| { + tcx.expected_host_effect_param_for_body(def_id) + }))) } else { None }; @@ -268,9 +293,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( let trait_ref = TraitRef::new( tcx, trait_id, - Some(GenericArg::from(ty)) - .into_iter() - .chain(args).chain(effect_arg), + Some(GenericArg::from(ty)).into_iter().chain(args).chain(effect_arg), ); debug_assert_matches!( diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 841b605f5fb..88966b41f69 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -367,7 +367,7 @@ impl Crate { // // The wrapper is set to the `lintcheck` so we can force enable linting and ignore certain crates // (see `crate::driver`) - let status = Command::new("cargo") + let status = Command::new(env::var("CARGO").unwrap_or("cargo".into())) .arg("check") .arg("--quiet") .current_dir(&self.path) @@ -441,7 +441,7 @@ impl Crate { /// Builds clippy inside the repo to make sure we have a clippy executable we can use. fn build_clippy() { - let status = Command::new("cargo") + let status = Command::new(env::var("CARGO").unwrap_or("cargo".into())) .arg("build") .status() .expect("Failed to build clippy!"); @@ -816,7 +816,7 @@ fn lintcheck_test() { "--crates-toml", "lintcheck/test_sources.toml", ]; - let status = std::process::Command::new("cargo") + let status = std::process::Command::new(env::var("CARGO").unwrap_or("cargo".into())) .args(args) .current_dir("..") // repo root .status(); diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 684cf4574b9..d575da6dece 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-12-01" +channel = "nightly-2023-12-16" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs index bbf7d22c850..dffa854177b 100644 --- a/src/tools/clippy/src/main.rs +++ b/src/tools/clippy/src/main.rs @@ -105,7 +105,7 @@ impl ClippyCmd { } fn into_std_cmd(self) -> Command { - let mut cmd = Command::new("cargo"); + let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into())); let clippy_args: String = self .clippy_args .iter() diff --git a/src/tools/clippy/tests/headers.rs b/src/tools/clippy/tests/headers.rs index 7eec9a9cdd2..d1f986ef526 100644 --- a/src/tools/clippy/tests/headers.rs +++ b/src/tools/clippy/tests/headers.rs @@ -12,7 +12,12 @@ fn old_test_headers() { for entry in WalkDir::new("tests") { let entry = entry.unwrap(); - if !entry.file_type().is_file() { + let is_hidden_file = entry + .file_name() + .to_str() + .expect("non-UTF-8 file name") + .starts_with('.'); + if is_hidden_file || !entry.file_type().is_file() { continue; } diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr index d776feb7f2e..bdceb752608 100644 --- a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr @@ -8,4 +8,4 @@ error: `mod.rs` files are required, found `src/bad.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` -error: could not compile `fail-mod-remap` (bin "fail-mod-remap") due to previous error +error: could not compile `fail-mod-remap` (bin "fail-mod-remap") due to 1 previous error diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr index 22558bc4ce8..06eaa071e82 100644 --- a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr @@ -8,4 +8,4 @@ error: `mod.rs` files are not allowed, found `src/bad/mod.rs` = note: `-D clippy::mod-module-files` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mod_module_files)]` -error: could not compile `fail-no-mod` (bin "fail-no-mod") due to previous error +error: could not compile `fail-no-mod` (bin "fail-no-mod") due to 1 previous error diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr index 4beedc10830..39f7176ade2 100644 --- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr @@ -3,4 +3,4 @@ error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9 = note: `-D clippy::multiple-crate-versions` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::multiple_crate_versions)]` -error: could not compile `multiple_crate_versions` (bin "multiple_crate_versions") due to previous error +error: could not compile `multiple_crate_versions` (bin "multiple_crate_versions") due to 1 previous error diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr index 65a19bb0718..a3539051b4d 100644 --- a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr @@ -3,4 +3,4 @@ error: wildcard dependency for `regex` = note: `-D clippy::wildcard-dependencies` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::wildcard_dependencies)]` -error: could not compile `wildcard_dependencies` (bin "wildcard_dependencies") due to previous error +error: could not compile `wildcard_dependencies` (bin "wildcard_dependencies") due to 1 previous error diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs index d737a832dd1..4375f324aca 100644 --- a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs +++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs @@ -9,7 +9,7 @@ #![allow(clippy::never_loop)] #![allow(clippy::needless_if)] #![warn(clippy::excessive_nesting)] -#![allow(clippy::collapsible_if)] +#![allow(clippy::collapsible_if, clippy::blocks_in_conditions)] #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed index 8cbadc67d71..8c77039b3c0 100644 --- a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed +++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed @@ -1,5 +1,5 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::blocks_in_conditions)] pub fn main() { let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs index 91d9d11e3c1..44257f3a469 100644 --- a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs +++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs @@ -1,5 +1,5 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::blocks_in_conditions)] pub fn main() { let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index f89c465047e..2ab441bbd0c 100644 --- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::blocks_in_conditions)] #![allow(unused, clippy::let_and_return, clippy::needless_if)] #![warn(clippy::nonminimal_bool)] @@ -21,6 +21,7 @@ fn macro_if() { fn condition_has_block() -> i32 { let res = { + //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` let x = 3; x == 3 }; if res { @@ -32,6 +33,7 @@ fn condition_has_block() -> i32 { fn condition_has_block_with_single_expression() -> i32 { if true { 6 } else { 10 } + //~^ ERROR: omit braces around single expression condition } fn condition_is_normal() -> i32 { @@ -61,4 +63,26 @@ fn block_in_assert() { ); } +// issue #11814 +fn block_in_match_expr(num: i32) -> i32 { + let res = { + //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + let opt = Some(2); + opt + }; match res { + Some(0) => 1, + Some(n) => num * 2, + None => 0, + }; + + match unsafe { + let hearty_hearty_hearty = vec![240, 159, 146, 150]; + String::from_utf8_unchecked(hearty_hearty_hearty).as_str() + } { + "💖" => 1, + "what" => 2, + _ => 3, + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index 34febc5fa2c..dd5ae4fb486 100644 --- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -1,4 +1,4 @@ -#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::blocks_in_conditions)] #![allow(unused, clippy::let_and_return, clippy::needless_if)] #![warn(clippy::nonminimal_bool)] @@ -21,6 +21,7 @@ fn macro_if() { fn condition_has_block() -> i32 { if { + //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` let x = 3; x == 3 } { @@ -32,6 +33,7 @@ fn condition_has_block() -> i32 { fn condition_has_block_with_single_expression() -> i32 { if { true } { 6 } else { 10 } + //~^ ERROR: omit braces around single expression condition } fn condition_is_normal() -> i32 { @@ -61,4 +63,26 @@ fn block_in_assert() { ); } +// issue #11814 +fn block_in_match_expr(num: i32) -> i32 { + match { + //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + let opt = Some(2); + opt + } { + Some(0) => 1, + Some(n) => num * 2, + None => 0, + }; + + match unsafe { + let hearty_hearty_hearty = vec![240, 159, 146, 150]; + String::from_utf8_unchecked(hearty_hearty_hearty).as_str() + } { + "💖" => 1, + "what" => 2, + _ => 3, + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr index d80ef9c0fd8..b00fe2f632c 100644 --- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr @@ -1,30 +1,32 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/blocks_in_if_conditions.rs:23:5 + --> $DIR/blocks_in_conditions.rs:23:5 | LL | / if { +LL | | LL | | let x = 3; LL | | x == 3 LL | | } { | |_____^ | - = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::blocks_in_if_conditions)]` + = note: `-D clippy::blocks-in-conditions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]` help: try | LL ~ let res = { +LL + LL + let x = 3; LL + x == 3 LL ~ }; if res { | error: omit braces around single expression condition - --> $DIR/blocks_in_if_conditions.rs:34:8 + --> $DIR/blocks_in_conditions.rs:35:8 | LL | if { true } { 6 } else { 10 } | ^^^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> $DIR/blocks_in_if_conditions.rs:39:8 + --> $DIR/blocks_in_conditions.rs:41:8 | LL | if true && x == 3 { 6 } else { 10 } | ^^^^^^^^^^^^^^ help: try: `x == 3` @@ -32,5 +34,24 @@ LL | if true && x == 3 { 6 } else { 10 } = note: `-D clippy::nonminimal-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` -error: aborting due to 3 previous errors +error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/blocks_in_conditions.rs:68:5 + | +LL | / match { +LL | | +LL | | let opt = Some(2); +LL | | opt +LL | | } { + | |_____^ + | +help: try + | +LL ~ let res = { +LL + +LL + let opt = Some(2); +LL + opt +LL ~ }; match res { + | + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs index 539f2df15bd..db31e4ae1a9 100644 --- a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs @@ -1,4 +1,4 @@ -#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::blocks_in_conditions)] #![allow( unused, clippy::let_and_return, @@ -22,7 +22,7 @@ fn pred_test() { && predicate( |x| { //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks - //~| NOTE: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` + //~| NOTE: `-D clippy::blocks-in-conditions` implied by `-D warnings` let target = 3; x == target }, @@ -60,6 +60,23 @@ fn function_with_empty_closure() { if closure(|| {}) {} } +// issue #11814 +fn match_with_pred() { + let v = 3; + match Some(predicate( + |x| { + //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks + let target = 3; + x == target + }, + v, + )) { + Some(true) => 1, + Some(false) => 2, + None => 3, + }; +} + #[rustfmt::skip] fn main() { let mut range = 0..10; diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr index ab68997d477..08b98f1b4fc 100644 --- a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr +++ b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/blocks_in_if_conditions_closure.rs:23:17 + --> $DIR/blocks_in_conditions_closure.rs:23:17 | LL | |x| { | _________________^ @@ -10,11 +10,11 @@ LL | | x == target LL | | }, | |_____________^ | - = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::blocks_in_if_conditions)]` + = note: `-D clippy::blocks-in-conditions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]` error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/blocks_in_if_conditions_closure.rs:34:13 + --> $DIR/blocks_in_conditions_closure.rs:34:13 | LL | |x| { | _____________^ @@ -24,5 +24,16 @@ LL | | x == target LL | | }, | |_________^ -error: aborting due to 2 previous errors +error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/blocks_in_conditions_closure.rs:67:13 + | +LL | |x| { + | _____________^ +LL | | +LL | | let target = 3; +LL | | x == target +LL | | }, + | |_________^ + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index aee89719728..708ac666675 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -227,3 +227,6 @@ where [(); N.checked_next_power_of_two().unwrap()]: { /// this checks if the lowerCamelCase issue is fixed fn issue_11568() {} + +/// There is no try (`do()` or `do_not()`). +fn parenthesized_word() {} diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index b6346b881ad..040d6352c52 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -227,3 +227,6 @@ where [(); N.checked_next_power_of_two().unwrap()]: { /// this checks if the lowerCamelCase issue is fixed fn issue_11568() {} + +/// There is no try (do() or do_not()). +fn parenthesized_word() {} diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr index 4c9ff41d918..033604e030a 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr @@ -319,5 +319,27 @@ help: try LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | ~~~~~~~~~~~~~~~~~ -error: aborting due to 29 previous errors +error: item in documentation is missing backticks + --> $DIR/doc-fixable.rs:231:22 + | +LL | /// There is no try (do() or do_not()). + | ^^^^ + | +help: try + | +LL | /// There is no try (`do()` or do_not()). + | ~~~~~~ + +error: item in documentation is missing backticks + --> $DIR/doc-fixable.rs:231:30 + | +LL | /// There is no try (do() or do_not()). + | ^^^^^^^^ + | +help: try + | +LL | /// There is no try (do() or `do_not()`). + | ~~~~~~~~~~ + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs index 0c8eac5ccff..f7f41c915e3 100644 --- a/src/tools/clippy/tests/ui/doc_unsafe.rs +++ b/src/tools/clippy/tests/ui/doc_unsafe.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs -#![allow(clippy::let_unit_value)] +#![allow(clippy::let_unit_value, clippy::needless_pass_by_ref_mut)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/ineffective_open_options.fixed b/src/tools/clippy/tests/ui/ineffective_open_options.fixed new file mode 100644 index 00000000000..3af8f3c5aaa --- /dev/null +++ b/src/tools/clippy/tests/ui/ineffective_open_options.fixed @@ -0,0 +1,41 @@ +#![warn(clippy::ineffective_open_options)] + +use std::fs::OpenOptions; + +fn main() { + let file = OpenOptions::new() + .create(true) + //~ ERROR: unnecessary use of `.write(true)` + .append(true) + .open("dump.json") + .unwrap(); + + let file = OpenOptions::new() + .create(true) + .append(true) + //~ ERROR: unnecessary use of `.write(true)` + .open("dump.json") + .unwrap(); + + // All the next calls are ok. + let file = OpenOptions::new() + .create(true) + .write(false) + .append(true) + .open("dump.json") + .unwrap(); + let file = OpenOptions::new() + .create(true) + .write(true) + .append(false) + .open("dump.json") + .unwrap(); + let file = OpenOptions::new() + .create(true) + .write(false) + .append(false) + .open("dump.json") + .unwrap(); + let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap(); + let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap(); +} diff --git a/src/tools/clippy/tests/ui/ineffective_open_options.rs b/src/tools/clippy/tests/ui/ineffective_open_options.rs new file mode 100644 index 00000000000..4eaf6293c40 --- /dev/null +++ b/src/tools/clippy/tests/ui/ineffective_open_options.rs @@ -0,0 +1,41 @@ +#![warn(clippy::ineffective_open_options)] + +use std::fs::OpenOptions; + +fn main() { + let file = OpenOptions::new() + .create(true) + .write(true) //~ ERROR: unnecessary use of `.write(true)` + .append(true) + .open("dump.json") + .unwrap(); + + let file = OpenOptions::new() + .create(true) + .append(true) + .write(true) //~ ERROR: unnecessary use of `.write(true)` + .open("dump.json") + .unwrap(); + + // All the next calls are ok. + let file = OpenOptions::new() + .create(true) + .write(false) + .append(true) + .open("dump.json") + .unwrap(); + let file = OpenOptions::new() + .create(true) + .write(true) + .append(false) + .open("dump.json") + .unwrap(); + let file = OpenOptions::new() + .create(true) + .write(false) + .append(false) + .open("dump.json") + .unwrap(); + let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap(); + let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap(); +} diff --git a/src/tools/clippy/tests/ui/ineffective_open_options.stderr b/src/tools/clippy/tests/ui/ineffective_open_options.stderr new file mode 100644 index 00000000000..7dc5322232c --- /dev/null +++ b/src/tools/clippy/tests/ui/ineffective_open_options.stderr @@ -0,0 +1,17 @@ +error: unnecessary use of `.write(true)` because there is `.append(true)` + --> $DIR/ineffective_open_options.rs:8:9 + | +LL | .write(true) + | ^^^^^^^^^^^^ help: remove `.write(true)` + | + = note: `-D clippy::ineffective-open-options` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ineffective_open_options)]` + +error: unnecessary use of `.write(true)` because there is `.append(true)` + --> $DIR/ineffective_open_options.rs:16:9 + | +LL | .write(true) + | ^^^^^^^^^^^^ help: remove `.write(true)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed index 60304177b42..ecf1b14e5b6 100644 --- a/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed +++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed @@ -1,6 +1,6 @@ #![feature(exhaustive_patterns, never_type)] #![allow(dead_code, unreachable_code, unused_variables)] -#![allow(clippy::let_and_return)] +#![allow(clippy::let_and_return, clippy::uninhabited_references)] enum SingleVariantEnum { Variant(i32), diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.rs b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs index b77aac4a16c..7cc7cb9d687 100644 --- a/src/tools/clippy/tests/ui/infallible_destructuring_match.rs +++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs @@ -1,6 +1,6 @@ #![feature(exhaustive_patterns, never_type)] #![allow(dead_code, unreachable_code, unused_variables)] -#![allow(clippy::let_and_return)] +#![allow(clippy::let_and_return, clippy::uninhabited_references)] enum SingleVariantEnum { Variant(i32), diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs new file mode 100644 index 00000000000..646f1eca56d --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -0,0 +1,366 @@ +//@no-rustfix +#![allow(clippy::never_loop)] +#![warn(clippy::infinite_loop)] + +fn do_something() {} + +fn no_break() { + loop { + //~^ ERROR: infinite loop detected + do_something(); + } +} + +fn all_inf() { + loop { + //~^ ERROR: infinite loop detected + loop { + //~^ ERROR: infinite loop detected + loop { + //~^ ERROR: infinite loop detected + do_something(); + } + } + do_something(); + } +} + +fn no_break_return_some_ty() -> Option<u8> { + loop { + do_something(); + return None; + } + loop { + //~^ ERROR: infinite loop detected + do_something(); + } +} + +fn no_break_never_ret() -> ! { + loop { + do_something(); + } +} + +fn no_break_never_ret_noise() { + loop { + fn inner_fn() -> ! { + std::process::exit(0); + } + do_something(); + } +} + +fn has_direct_break_1() { + loop { + do_something(); + break; + } +} + +fn has_direct_break_2() { + 'outer: loop { + do_something(); + break 'outer; + } +} + +fn has_indirect_break_1(cond: bool) { + 'outer: loop { + loop { + if cond { + break 'outer; + } + } + } +} + +fn has_indirect_break_2(stop_num: i32) { + 'outer: loop { + for x in 0..5 { + if x == stop_num { + break 'outer; + } + } + } +} + +fn break_inner_but_not_outer_1(cond: bool) { + loop { + //~^ ERROR: infinite loop detected + loop { + if cond { + break; + } + } + } +} + +fn break_inner_but_not_outer_2(cond: bool) { + loop { + //~^ ERROR: infinite loop detected + 'inner: loop { + loop { + if cond { + break 'inner; + } + } + } + } +} + +fn break_outer_but_not_inner() { + loop { + loop { + //~^ ERROR: infinite loop detected + do_something(); + } + break; + } +} + +fn can_break_both_inner_and_outer(cond: bool) { + 'outer: loop { + loop { + if cond { + break 'outer; + } else { + break; + } + } + } +} + +fn break_wrong_loop(cond: bool) { + // 'inner has statement to break 'outer loop, but it was breaked early by a labeled child loop + 'outer: loop { + loop { + //~^ ERROR: infinite loop detected + 'inner: loop { + loop { + loop { + break 'inner; + } + break 'outer; + } + } + } + } +} + +fn has_direct_return(cond: bool) { + loop { + if cond { + return; + } + } +} + +fn ret_in_inner(cond: bool) { + loop { + loop { + if cond { + return; + } + } + } +} + +enum Foo { + A, + B, + C, +} + +fn match_like() { + let opt: Option<u8> = Some(1); + loop { + //~^ ERROR: infinite loop detected + match opt { + Some(v) => { + println!("{v}"); + }, + None => { + do_something(); + }, + } + } + + loop { + match opt { + Some(v) => { + println!("{v}"); + }, + None => { + do_something(); + break; + }, + } + } + + let result: Result<u8, u16> = Ok(1); + loop { + let _val = match result { + Ok(1) => 1 + 1, + Ok(v) => v / 2, + Err(_) => return, + }; + } + + loop { + let Ok(_val) = result else { return }; + } + + loop { + let Ok(_val) = result.map(|v| 10) else { break }; + } + + loop { + //~^ ERROR: infinite loop detected + let _x = matches!(result, Ok(v) if v != 0).then_some(0); + } + + loop { + //~^ ERROR: infinite loop detected + // This `return` does not return the function, so it doesn't count + let _x = matches!(result, Ok(v) if v != 0).then(|| { + if true { + return; + } + do_something(); + }); + } + + let mut val = 0; + let mut fooc = Foo::C; + + loop { + val = match fooc { + Foo::A => 0, + Foo::B => { + fooc = Foo::C; + 1 + }, + Foo::C => break, + }; + } + + loop { + val = match fooc { + Foo::A => 0, + Foo::B => 1, + Foo::C => { + break; + }, + }; + } +} + +macro_rules! set_or_ret { + ($opt:expr, $a:expr) => {{ + match $opt { + Some(val) => $a = val, + None => return, + } + }}; +} + +fn ret_in_macro(opt: Option<u8>) { + let opt: Option<u8> = Some(1); + let mut a: u8 = 0; + loop { + set_or_ret!(opt, a); + } + + let res: Result<bool, u8> = Ok(true); + loop { + match res { + Ok(true) => set_or_ret!(opt, a), + _ => do_something(), + } + } +} + +fn panic_like_macros_1() { + loop { + do_something(); + panic!(); + } +} + +fn panic_like_macros_2() { + let mut x = 0; + + loop { + do_something(); + if true { + todo!(); + } + } + loop { + do_something(); + x += 1; + assert_eq!(x, 0); + } + loop { + do_something(); + assert!(x % 2 == 0); + } + loop { + do_something(); + match Some(1) { + Some(n) => println!("{n}"), + None => unreachable!("It won't happen"), + } + } +} + +fn exit_directly(cond: bool) { + loop { + if cond { + std::process::exit(0); + } + } +} + +trait MyTrait { + fn problematic_trait_method() { + loop { + //~^ ERROR: infinite loop detected + do_something(); + } + } + fn could_be_problematic(); +} + +impl MyTrait for String { + fn could_be_problematic() { + loop { + //~^ ERROR: infinite loop detected + do_something(); + } + } +} + +fn inf_loop_in_closure() { + let _loop_forever = || { + loop { + //~^ ERROR: infinite loop detected + do_something(); + } + }; + + let _somehow_ok = || -> ! { + loop { + do_something(); + } + }; +} + +fn inf_loop_in_res() -> Result<(), i32> { + Ok(loop { + do_something() + }) +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/infinite_loops.stderr b/src/tools/clippy/tests/ui/infinite_loops.stderr new file mode 100644 index 00000000000..f58b3cebbc3 --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_loops.stderr @@ -0,0 +1,259 @@ +error: infinite loop detected + --> $DIR/infinite_loops.rs:8:5 + | +LL | / loop { +LL | | +LL | | do_something(); +LL | | } + | |_____^ + | + = note: `-D clippy::infinite-loop` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::infinite_loop)]` +help: if this is intentional, consider specifing `!` as function return + | +LL | fn no_break() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:15:5 + | +LL | / loop { +LL | | +LL | | loop { +LL | | +... | +LL | | do_something(); +LL | | } + | |_____^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn all_inf() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:17:9 + | +LL | / loop { +LL | | +LL | | loop { +LL | | +LL | | do_something(); +LL | | } +LL | | } + | |_________^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn all_inf() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:19:13 + | +LL | / loop { +LL | | +LL | | do_something(); +LL | | } + | |_____________^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn all_inf() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:33:5 + | +LL | / loop { +LL | | +LL | | do_something(); +LL | | } + | |_____^ + | + = help: if this is not intended, try adding a `break` or `return` condition in the loop + +error: infinite loop detected + --> $DIR/infinite_loops.rs:46:5 + | +LL | / loop { +LL | | fn inner_fn() -> ! { +LL | | std::process::exit(0); +LL | | } +LL | | do_something(); +LL | | } + | |_____^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn no_break_never_ret_noise() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:89:5 + | +LL | / loop { +LL | | +LL | | loop { +LL | | if cond { +... | +LL | | } +LL | | } + | |_____^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn break_inner_but_not_outer_1(cond: bool) -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:100:5 + | +LL | / loop { +LL | | +LL | | 'inner: loop { +LL | | loop { +... | +LL | | } +LL | | } + | |_____^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn break_inner_but_not_outer_2(cond: bool) -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:114:9 + | +LL | / loop { +LL | | +LL | | do_something(); +LL | | } + | |_________^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn break_outer_but_not_inner() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:137:9 + | +LL | / loop { +LL | | +LL | | 'inner: loop { +LL | | loop { +... | +LL | | } +LL | | } + | |_________^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn break_wrong_loop(cond: bool) -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:177:5 + | +LL | / loop { +LL | | +LL | | match opt { +LL | | Some(v) => { +... | +LL | | } +LL | | } + | |_____^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn match_like() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:218:5 + | +LL | / loop { +LL | | +LL | | let _x = matches!(result, Ok(v) if v != 0).then_some(0); +LL | | } + | |_____^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn match_like() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:223:5 + | +LL | / loop { +LL | | +LL | | // This `return` does not return the function, so it doesn't count +LL | | let _x = matches!(result, Ok(v) if v != 0).then(|| { +... | +LL | | }); +LL | | } + | |_____^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn match_like() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:328:9 + | +LL | / loop { +LL | | +LL | | do_something(); +LL | | } + | |_________^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn problematic_trait_method() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:338:9 + | +LL | / loop { +LL | | +LL | | do_something(); +LL | | } + | |_________^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | fn could_be_problematic() -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:347:9 + | +LL | / loop { +LL | | +LL | | do_something(); +LL | | } + | |_________^ + | +help: if this is intentional, consider specifing `!` as function return + | +LL | let _loop_forever = || -> ! { + | ++++ + +error: infinite loop detected + --> $DIR/infinite_loops.rs:361:8 + | +LL | Ok(loop { + | ________^ +LL | | do_something() +LL | | }) + | |_____^ + | + = help: if this is not intended, try adding a `break` or `return` condition in the loop + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_filter.fixed b/src/tools/clippy/tests/ui/manual_filter.fixed index c1bc4aae92e..a0fb0e32d60 100644 --- a/src/tools/clippy/tests/ui/manual_filter.fixed +++ b/src/tools/clippy/tests/ui/manual_filter.fixed @@ -40,7 +40,7 @@ fn main() { }; } - #[allow(clippy::blocks_in_if_conditions)] + #[allow(clippy::blocks_in_conditions)] Some(11).filter(|&x| { println!("foo"); x > 10 && x < 100 diff --git a/src/tools/clippy/tests/ui/manual_filter.rs b/src/tools/clippy/tests/ui/manual_filter.rs index ee44909f37e..0ac6cbefc4e 100644 --- a/src/tools/clippy/tests/ui/manual_filter.rs +++ b/src/tools/clippy/tests/ui/manual_filter.rs @@ -135,7 +135,7 @@ fn main() { }; } - #[allow(clippy::blocks_in_if_conditions)] + #[allow(clippy::blocks_in_conditions)] match Some(11) { // Lint, statement is preserved by `.filter` Some(x) => { diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed index a96827259f5..ac44a6f3fdb 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed @@ -118,4 +118,19 @@ fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) { let _ = v1[0] + v2[1]; } +fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { + assert!(v1.len() == 3); + assert!(v2.len() == 4); + assert!(v3.len() == 3); + assert!(4 == v4.len()); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[0] + v2[1] + v2[2]; + + let _ = v3[0] + v3[1] + v3[2]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v4[0] + v4[1] + v4[2]; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs index 0b4b883acf8..f05d5fea57d 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs @@ -118,4 +118,19 @@ fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) { let _ = v1[0] + v2[1]; } +fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { + assert!(v1.len() == 2); + assert!(v2.len() == 4); + assert!(2 == v3.len()); + assert!(4 == v4.len()); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[0] + v2[1] + v2[2]; + + let _ = v3[0] + v3[1] + v3[2]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v4[0] + v4[1] + v4[2]; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr index a3e66d7958e..61dce6ccc6c 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr @@ -249,5 +249,57 @@ LL | let _ = v1[0] + v1[12]; | ^^^^^^ = note: asserting the length before indexing will elide bounds checks -error: aborting due to 9 previous errors +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:127:13 + | +LL | assert!(v1.len() == 2); + | ---------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)` +... +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:127:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:127:21 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:127:29 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:131:13 + | +LL | assert!(2 == v3.len()); + | ---------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)` +... +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:131:13 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:131:21 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:131:29 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed index 2a335516f51..bd7a9a0b984 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed @@ -284,4 +284,19 @@ fn main() { { } } + // address of field when operand impl Drop + { + struct CustomDrop(String); + + impl Drop for CustomDrop { + fn drop(&mut self) {} + } + + fn check_str<P: AsRef<str>>(_to: P) {} + + fn test() { + let owner = CustomDrop(String::default()); + check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop + } + } } diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs index f0567f486ac..5cfd4ce30cc 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs @@ -284,4 +284,19 @@ fn main() { { } } + // address of field when operand impl Drop + { + struct CustomDrop(String); + + impl Drop for CustomDrop { + fn drop(&mut self) {} + } + + fn check_str<P: AsRef<str>>(_to: P) {} + + fn test() { + let owner = CustomDrop(String::default()); + check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop + } + } } diff --git a/src/tools/clippy/tests/ui/needless_if.fixed b/src/tools/clippy/tests/ui/needless_if.fixed index be35dcddbe6..1086ae2c984 100644 --- a/src/tools/clippy/tests/ui/needless_if.fixed +++ b/src/tools/clippy/tests/ui/needless_if.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![feature(let_chains)] #![allow( - clippy::blocks_in_if_conditions, + clippy::blocks_in_conditions, clippy::if_same_then_else, clippy::ifs_same_cond, clippy::let_unit_value, diff --git a/src/tools/clippy/tests/ui/needless_if.rs b/src/tools/clippy/tests/ui/needless_if.rs index e2ad17e69a8..131cceaf712 100644 --- a/src/tools/clippy/tests/ui/needless_if.rs +++ b/src/tools/clippy/tests/ui/needless_if.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![feature(let_chains)] #![allow( - clippy::blocks_in_if_conditions, + clippy::blocks_in_conditions, clippy::if_same_then_else, clippy::ifs_same_cond, clippy::let_unit_value, diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed index 891b2b01437..6db87049044 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init.fixed @@ -3,7 +3,7 @@ #![allow(unused)] #![allow( clippy::assign_op_pattern, - clippy::blocks_in_if_conditions, + clippy::blocks_in_conditions, clippy::let_and_return, clippy::let_unit_value, clippy::nonminimal_bool, diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index 55399511639..c1e86212a08 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -3,7 +3,7 @@ #![allow(unused)] #![allow( clippy::assign_op_pattern, - clippy::blocks_in_if_conditions, + clippy::blocks_in_conditions, clippy::let_and_return, clippy::let_unit_value, clippy::nonminimal_bool, diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs index 25a02bdd2f2..a92197fb0af 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs @@ -307,6 +307,19 @@ fn filter_copy<T: Copy>(predicate: &mut impl FnMut(T) -> bool) -> impl FnMut(&T) move |&item| predicate(item) } +// `is_from_proc_macro` stress tests +fn _empty_tup(x: &mut (())) {} +fn _single_tup(x: &mut ((i32,))) {} +fn _multi_tup(x: &mut ((i32, u32))) {} +fn _fn(x: &mut (fn())) {} +#[rustfmt::skip] +fn _extern_rust_fn(x: &mut extern "Rust" fn()) {} +fn _extern_c_fn(x: &mut extern "C" fn()) {} +fn _unsafe_fn(x: &mut unsafe fn()) {} +fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {} +fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {} +fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {} + fn main() { let mut u = 0; let mut v = vec![0]; diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr index 92b753276ac..5d1e9515de1 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr @@ -139,5 +139,65 @@ LL | pub async fn closure4(n: &mut usize) { | = warning: changing this function will impact semver compatibility -error: aborting due to 21 previous errors +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:311:18 + | +LL | fn _empty_tup(x: &mut (())) {} + | ^^^^^^^^^ help: consider changing to: `&()` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:312:19 + | +LL | fn _single_tup(x: &mut ((i32,))) {} + | ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:313:18 + | +LL | fn _multi_tup(x: &mut ((i32, u32))) {} + | ^^^^^^^^^^^^^^^^^ help: consider changing to: `&(i32, u32)` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:314:11 + | +LL | fn _fn(x: &mut (fn())) {} + | ^^^^^^^^^^^ help: consider changing to: `&fn()` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:316:23 + | +LL | fn _extern_rust_fn(x: &mut extern "Rust" fn()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "Rust" fn()` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:317:20 + | +LL | fn _extern_c_fn(x: &mut extern "C" fn()) {} + | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "C" fn()` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:318:18 + | +LL | fn _unsafe_fn(x: &mut unsafe fn()) {} + | ^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe fn()` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:319:25 + | +LL | fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn()` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:320:20 + | +LL | fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn(i32)` + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:321:20 + | +LL | fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn() -> (i32)` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index c52f4389192..777b1e52c2d 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -9,6 +9,30 @@ clippy::useless_vec )] +use std::fmt::Display; +use std::ops::{Neg, Shl}; + +struct Cout; + +impl<T> Shl<T> for Cout +where + T: Display, +{ + type Output = Self; + fn shl(self, rhs: T) -> Self::Output { + println!("{}", rhs); + self + } +} + +impl Neg for Cout { + type Output = Self; + fn neg(self) -> Self::Output { + println!("hello world"); + self + } +} + struct Unit; struct Tuple(i32); struct Struct { @@ -174,4 +198,11 @@ fn main() { GreetStruct1("world"); GreetStruct2()("world"); GreetStruct3 {}("world"); + + fn n() -> i32 { + 42 + } + + Cout << 142; + -Cout; } diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr index feba35697f5..f5ba234b4cb 100644 --- a/src/tools/clippy/tests/ui/no_effect.stderr +++ b/src/tools/clippy/tests/ui/no_effect.stderr @@ -1,5 +1,5 @@ error: statement with no effect - --> $DIR/no_effect.rs:98:5 + --> $DIR/no_effect.rs:122:5 | LL | 0; | ^^ @@ -8,151 +8,151 @@ LL | 0; = help: to override `-D warnings` add `#[allow(clippy::no_effect)]` error: statement with no effect - --> $DIR/no_effect.rs:101:5 + --> $DIR/no_effect.rs:125:5 | LL | s2; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:103:5 + --> $DIR/no_effect.rs:127:5 | LL | Unit; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:105:5 + --> $DIR/no_effect.rs:129:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:107:5 + --> $DIR/no_effect.rs:131:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:109:5 + --> $DIR/no_effect.rs:133:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:111:5 + --> $DIR/no_effect.rs:135:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:113:5 + --> $DIR/no_effect.rs:137:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:115:5 + --> $DIR/no_effect.rs:139:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:117:5 + --> $DIR/no_effect.rs:141:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:119:5 + --> $DIR/no_effect.rs:143:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:121:5 + --> $DIR/no_effect.rs:145:5 | LL | &6; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:123:5 + --> $DIR/no_effect.rs:147:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:125:5 + --> $DIR/no_effect.rs:149:5 | LL | ..; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:127:5 + --> $DIR/no_effect.rs:151:5 | LL | 5..; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:129:5 + --> $DIR/no_effect.rs:153:5 | LL | ..5; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:131:5 + --> $DIR/no_effect.rs:155:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:133:5 + --> $DIR/no_effect.rs:157:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:135:5 + --> $DIR/no_effect.rs:159:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:137:5 + --> $DIR/no_effect.rs:161:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:139:5 + --> $DIR/no_effect.rs:163:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:141:5 + --> $DIR/no_effect.rs:165:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:143:5 + --> $DIR/no_effect.rs:167:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:146:5 + --> $DIR/no_effect.rs:170:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:149:5 + --> $DIR/no_effect.rs:173:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:151:5 + --> $DIR/no_effect.rs:175:5 | LL | let _unused = 1; | ^^^^^^^^^^^^^^^^ @@ -161,19 +161,19 @@ LL | let _unused = 1; = help: to override `-D warnings` add `#[allow(clippy::no_effect_underscore_binding)]` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:154:5 + --> $DIR/no_effect.rs:178:5 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:156:5 + --> $DIR/no_effect.rs:180:5 | LL | let _duck = Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:158:5 + --> $DIR/no_effect.rs:182:5 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed index ca13b52ae3b..fa15c323540 100644 --- a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed @@ -71,3 +71,118 @@ fn _msrv_1_38() { let _ = ptr.cast::<i32>(); let _ = mut_ptr.cast::<i32>(); } + +#[allow(clippy::unnecessary_cast)] +mod null { + fn use_path_mut() -> *mut u32 { + use std::ptr; + ptr::null_mut::<u32>() + } + + fn full_path_mut() -> *mut u32 { + std::ptr::null_mut::<u32>() + } + + fn core_path_mut() -> *mut u32 { + use core::ptr; + ptr::null_mut::<u32>() + } + + fn full_core_path_mut() -> *mut u32 { + core::ptr::null_mut::<u32>() + } + + fn use_path() -> *const u32 { + use std::ptr; + ptr::null::<u32>() + } + + fn full_path() -> *const u32 { + std::ptr::null::<u32>() + } + + fn core_path() -> *const u32 { + use core::ptr; + ptr::null::<u32>() + } + + fn full_core_path() -> *const u32 { + core::ptr::null::<u32>() + } +} + +mod null_ptr_infer { + fn use_path_mut() -> *mut u32 { + use std::ptr; + ptr::null_mut() + } + + fn full_path_mut() -> *mut u32 { + std::ptr::null_mut() + } + + fn core_path_mut() -> *mut u32 { + use core::ptr; + ptr::null_mut() + } + + fn full_core_path_mut() -> *mut u32 { + core::ptr::null_mut() + } + + fn use_path() -> *const u32 { + use std::ptr; + ptr::null() + } + + fn full_path() -> *const u32 { + std::ptr::null() + } + + fn core_path() -> *const u32 { + use core::ptr; + ptr::null() + } + + fn full_core_path() -> *const u32 { + core::ptr::null() + } +} + +mod null_entire_infer { + fn use_path_mut() -> *mut u32 { + use std::ptr; + ptr::null_mut() + } + + fn full_path_mut() -> *mut u32 { + std::ptr::null_mut() + } + + fn core_path_mut() -> *mut u32 { + use core::ptr; + ptr::null_mut() + } + + fn full_core_path_mut() -> *mut u32 { + core::ptr::null_mut() + } + + fn use_path() -> *const u32 { + use std::ptr; + ptr::null() + } + + fn full_path() -> *const u32 { + std::ptr::null() + } + + fn core_path() -> *const u32 { + use core::ptr; + ptr::null() + } + + fn full_core_path() -> *const u32 { + core::ptr::null() + } +} diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs index 942c8734444..7ab52e63da5 100644 --- a/src/tools/clippy/tests/ui/ptr_as_ptr.rs +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs @@ -71,3 +71,118 @@ fn _msrv_1_38() { let _ = ptr as *const i32; let _ = mut_ptr as *mut i32; } + +#[allow(clippy::unnecessary_cast)] +mod null { + fn use_path_mut() -> *mut u32 { + use std::ptr; + ptr::null_mut() as *mut u32 + } + + fn full_path_mut() -> *mut u32 { + std::ptr::null_mut() as *mut u32 + } + + fn core_path_mut() -> *mut u32 { + use core::ptr; + ptr::null_mut() as *mut u32 + } + + fn full_core_path_mut() -> *mut u32 { + core::ptr::null_mut() as *mut u32 + } + + fn use_path() -> *const u32 { + use std::ptr; + ptr::null() as *const u32 + } + + fn full_path() -> *const u32 { + std::ptr::null() as *const u32 + } + + fn core_path() -> *const u32 { + use core::ptr; + ptr::null() as *const u32 + } + + fn full_core_path() -> *const u32 { + core::ptr::null() as *const u32 + } +} + +mod null_ptr_infer { + fn use_path_mut() -> *mut u32 { + use std::ptr; + ptr::null_mut() as *mut _ + } + + fn full_path_mut() -> *mut u32 { + std::ptr::null_mut() as *mut _ + } + + fn core_path_mut() -> *mut u32 { + use core::ptr; + ptr::null_mut() as *mut _ + } + + fn full_core_path_mut() -> *mut u32 { + core::ptr::null_mut() as *mut _ + } + + fn use_path() -> *const u32 { + use std::ptr; + ptr::null() as *const _ + } + + fn full_path() -> *const u32 { + std::ptr::null() as *const _ + } + + fn core_path() -> *const u32 { + use core::ptr; + ptr::null() as *const _ + } + + fn full_core_path() -> *const u32 { + core::ptr::null() as *const _ + } +} + +mod null_entire_infer { + fn use_path_mut() -> *mut u32 { + use std::ptr; + ptr::null_mut() as _ + } + + fn full_path_mut() -> *mut u32 { + std::ptr::null_mut() as _ + } + + fn core_path_mut() -> *mut u32 { + use core::ptr; + ptr::null_mut() as _ + } + + fn full_core_path_mut() -> *mut u32 { + core::ptr::null_mut() as _ + } + + fn use_path() -> *const u32 { + use std::ptr; + ptr::null() as _ + } + + fn full_path() -> *const u32 { + std::ptr::null() as _ + } + + fn core_path() -> *const u32 { + use core::ptr; + ptr::null() as _ + } + + fn full_core_path() -> *const u32 { + core::ptr::null() as _ + } +} diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr index c0ce69b4357..ef64347e935 100644 --- a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr @@ -57,5 +57,149 @@ error: `as` casting between raw pointers without changing its mutability LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()` -error: aborting due to 9 previous errors +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:79:9 + | +LL | ptr::null_mut() as *mut u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:83:9 + | +LL | std::ptr::null_mut() as *mut u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut::<u32>()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:88:9 + | +LL | ptr::null_mut() as *mut u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:92:9 + | +LL | core::ptr::null_mut() as *mut u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut::<u32>()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:97:9 + | +LL | ptr::null() as *const u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:101:9 + | +LL | std::ptr::null() as *const u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::<u32>()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:106:9 + | +LL | ptr::null() as *const u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:110:9 + | +LL | core::ptr::null() as *const u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null::<u32>()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:117:9 + | +LL | ptr::null_mut() as *mut _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:121:9 + | +LL | std::ptr::null_mut() as *mut _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:126:9 + | +LL | ptr::null_mut() as *mut _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:130:9 + | +LL | core::ptr::null_mut() as *mut _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:135:9 + | +LL | ptr::null() as *const _ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:139:9 + | +LL | std::ptr::null() as *const _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:144:9 + | +LL | ptr::null() as *const _ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:148:9 + | +LL | core::ptr::null() as *const _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:155:9 + | +LL | ptr::null_mut() as _ + | ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:159:9 + | +LL | std::ptr::null_mut() as _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:164:9 + | +LL | ptr::null_mut() as _ + | ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:168:9 + | +LL | core::ptr::null_mut() as _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:173:9 + | +LL | ptr::null() as _ + | ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:177:9 + | +LL | std::ptr::null() as _ + | ^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:182:9 + | +LL | ptr::null() as _ + | ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:186:9 + | +LL | core::ptr::null() as _ + | ^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()` + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs index 094d9574ae9..1ea0d65bf1e 100644 --- a/src/tools/clippy/tests/ui/regex.rs +++ b/src/tools/clippy/tests/ui/regex.rs @@ -112,6 +112,10 @@ fn trivial_regex() { // #6005: unicode classes in bytes::Regex let a_byte_of_unicode = BRegex::new(r"\p{C}"); + + // start and end word boundry, introduced in regex 0.10 + let _ = BRegex::new(r"\<word\>"); + let _ = BRegex::new(r"\b{start}word\b{end}"); } fn main() { diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 31f1ee6a86d..f4ff0f0b88b 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -4,7 +4,7 @@ #![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] -#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] @@ -54,8 +54,9 @@ #![allow(ambiguous_wide_pointer_comparisons)] #![warn(clippy::almost_complete_range)] #![warn(clippy::disallowed_names)] -#![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::blocks_in_conditions)] +#![warn(clippy::blocks_in_conditions)] +#![warn(clippy::blocks_in_conditions)] #![warn(clippy::box_collection)] #![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 325bc356c15..0df1098f5fb 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -4,7 +4,7 @@ #![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] -#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] @@ -56,6 +56,7 @@ #![warn(clippy::blacklisted_name)] #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::box_vec)] #![warn(clippy::const_static_lifetime)] #![warn(clippy::cyclomatic_complexity)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index a6164338c20..f63ad82a757 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -13,335 +13,341 @@ error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_n LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` -error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` +error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::block_in_if_condition_expr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` -error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` +error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` -error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` +error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` --> $DIR/rename.rs:59:9 | +LL | #![warn(clippy::blocks_in_if_conditions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` + +error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` + --> $DIR/rename.rs:60:9 + | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:92:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> $DIR/rename.rs:93:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:95:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:96:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:97:9 + --> $DIR/rename.rs:98:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:98:9 + --> $DIR/rename.rs:99:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:99:9 + --> $DIR/rename.rs:100:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:100:9 + --> $DIR/rename.rs:101:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:101:9 + --> $DIR/rename.rs:102:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> $DIR/rename.rs:102:9 + --> $DIR/rename.rs:103:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:103:9 + --> $DIR/rename.rs:104:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:104:9 + --> $DIR/rename.rs:105:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:105:9 + --> $DIR/rename.rs:106:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:106:9 + --> $DIR/rename.rs:107:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:107:9 + --> $DIR/rename.rs:108:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> $DIR/rename.rs:108:9 + --> $DIR/rename.rs:109:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:109:9 + --> $DIR/rename.rs:110:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:110:9 + --> $DIR/rename.rs:111:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> $DIR/rename.rs:111:9 + --> $DIR/rename.rs:112:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: aborting due to 57 previous errors +error: aborting due to 58 previous errors diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed new file mode 100644 index 00000000000..2afe2f43325 --- /dev/null +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed @@ -0,0 +1,38 @@ +#![warn(clippy::repeat_vec_with_capacity)] + +fn main() { + { + (0..123).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>(); + //~^ ERROR: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity + } + + { + let n = 123; + (0..n).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>(); + //~^ ERROR: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity + } + + { + macro_rules! from_macro { + ($x:expr) => { + vec![$x; 123]; + }; + } + // vec expansion is from another macro, don't lint + from_macro!(Vec::<()>::with_capacity(42)); + } + + { + std::iter::repeat_with(|| Vec::<()>::with_capacity(42)); + //~^ ERROR: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity + } + + { + macro_rules! from_macro { + ($x:expr) => { + std::iter::repeat($x) + }; + } + from_macro!(Vec::<()>::with_capacity(42)); + } +} diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs new file mode 100644 index 00000000000..659f2a3953d --- /dev/null +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs @@ -0,0 +1,38 @@ +#![warn(clippy::repeat_vec_with_capacity)] + +fn main() { + { + vec![Vec::<()>::with_capacity(42); 123]; + //~^ ERROR: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity + } + + { + let n = 123; + vec![Vec::<()>::with_capacity(42); n]; + //~^ ERROR: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity + } + + { + macro_rules! from_macro { + ($x:expr) => { + vec![$x; 123]; + }; + } + // vec expansion is from another macro, don't lint + from_macro!(Vec::<()>::with_capacity(42)); + } + + { + std::iter::repeat(Vec::<()>::with_capacity(42)); + //~^ ERROR: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity + } + + { + macro_rules! from_macro { + ($x:expr) => { + std::iter::repeat($x) + }; + } + from_macro!(Vec::<()>::with_capacity(42)); + } +} diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr new file mode 100644 index 00000000000..10b5f121420 --- /dev/null +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr @@ -0,0 +1,40 @@ +error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity + --> $DIR/repeat_vec_with_capacity.rs:5:9 + | +LL | vec![Vec::<()>::with_capacity(42); 123]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: only the last `Vec` will have the capacity + = note: `-D clippy::repeat-vec-with-capacity` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::repeat_vec_with_capacity)]` +help: if you intended to initialize multiple `Vec`s with an initial capacity, try + | +LL | (0..123).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity + --> $DIR/repeat_vec_with_capacity.rs:11:9 + | +LL | vec![Vec::<()>::with_capacity(42); n]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: only the last `Vec` will have the capacity +help: if you intended to initialize multiple `Vec`s with an initial capacity, try + | +LL | (0..n).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity + --> $DIR/repeat_vec_with_capacity.rs:26:9 + | +LL | std::iter::repeat(Vec::<()>::with_capacity(42)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: none of the yielded `Vec`s will have the requested capacity +help: if you intended to create an iterator that yields `Vec`s with an initial capacity, try + | +LL | std::iter::repeat_with(|| Vec::<()>::with_capacity(42)); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/uninhabited_references.rs b/src/tools/clippy/tests/ui/uninhabited_references.rs new file mode 100644 index 00000000000..cd07b590a61 --- /dev/null +++ b/src/tools/clippy/tests/ui/uninhabited_references.rs @@ -0,0 +1,22 @@ +#![warn(clippy::uninhabited_references)] +#![feature(never_type)] + +fn ret_uninh_ref() -> &'static std::convert::Infallible { + unsafe { std::mem::transmute(&()) } +} + +macro_rules! ret_something { + ($name:ident, $ty:ty) => { + fn $name(x: &$ty) -> &$ty { + &*x + } + }; +} + +ret_something!(id_u32, u32); +ret_something!(id_never, !); + +fn main() { + let x = ret_uninh_ref(); + let _ = *x; +} diff --git a/src/tools/clippy/tests/ui/uninhabited_references.stderr b/src/tools/clippy/tests/ui/uninhabited_references.stderr new file mode 100644 index 00000000000..2cdf320b809 --- /dev/null +++ b/src/tools/clippy/tests/ui/uninhabited_references.stderr @@ -0,0 +1,39 @@ +error: dereferencing a reference to an uninhabited type would be undefined behavior + --> $DIR/uninhabited_references.rs:4:23 + | +LL | fn ret_uninh_ref() -> &'static std::convert::Infallible { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninhabited-references` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::uninhabited_references)]` + +error: dereferencing a reference to an uninhabited type would be undefined behavior + --> $DIR/uninhabited_references.rs:10:30 + | +LL | fn $name(x: &$ty) -> &$ty { + | ^^^^ +... +LL | ret_something!(id_never, !); + | --------------------------- in this macro invocation + | + = note: this error originates in the macro `ret_something` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: dereferencing a reference to an uninhabited type is undefined behavior + --> $DIR/uninhabited_references.rs:11:14 + | +LL | &*x + | ^^ +... +LL | ret_something!(id_never, !); + | --------------------------- in this macro invocation + | + = note: this error originates in the macro `ret_something` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: dereferencing a reference to an uninhabited type is undefined behavior + --> $DIR/uninhabited_references.rs:21:13 + | +LL | let _ = *x; + | ^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed index d0c0298ef4c..463412daec0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed @@ -7,6 +7,9 @@ )] #![warn(clippy::unnecessary_operation)] +use std::fmt::Display; +use std::ops::Shl; + struct Tuple(i32); struct Struct { field: i32, @@ -50,6 +53,19 @@ fn get_drop_struct() -> DropStruct { DropStruct { field: 0 } } +struct Cout; + +impl<T> Shl<T> for Cout +where + T: Display, +{ + type Output = Self; + fn shl(self, rhs: T) -> Self::Output { + println!("{}", rhs); + self + } +} + fn main() { get_number(); get_number(); @@ -87,4 +103,7 @@ fn main() { ($($e:expr),*) => {{ $($e;)* }} } use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one()); + + // Issue #11885 + Cout << 16; } diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs index e8e3a2d5657..f0d28e28902 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.rs +++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs @@ -7,6 +7,9 @@ )] #![warn(clippy::unnecessary_operation)] +use std::fmt::Display; +use std::ops::Shl; + struct Tuple(i32); struct Struct { field: i32, @@ -50,6 +53,19 @@ fn get_drop_struct() -> DropStruct { DropStruct { field: 0 } } +struct Cout; + +impl<T> Shl<T> for Cout +where + T: Display, +{ + type Output = Self; + fn shl(self, rhs: T) -> Self::Output { + println!("{}", rhs); + self + } +} + fn main() { Tuple(get_number()); Struct { field: get_number() }; @@ -91,4 +107,7 @@ fn main() { ($($e:expr),*) => {{ $($e;)* }} } use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one()); + + // Issue #11885 + Cout << 16; } diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr index fbe495f518f..eeee9ad6006 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr @@ -1,5 +1,5 @@ error: unnecessary operation - --> $DIR/unnecessary_operation.rs:54:5 + --> $DIR/unnecessary_operation.rs:70:5 | LL | Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` @@ -8,103 +8,103 @@ LL | Tuple(get_number()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_operation)]` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:55:5 + --> $DIR/unnecessary_operation.rs:71:5 | LL | 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:72:5 | LL | Struct { ..get_struct() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:57:5 + --> $DIR/unnecessary_operation.rs:73:5 | LL | Enum::Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:58:5 + --> $DIR/unnecessary_operation.rs:74:5 | LL | Enum::Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:59:5 + --> $DIR/unnecessary_operation.rs:75:5 | LL | 5 + get_number(); | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:60:5 + --> $DIR/unnecessary_operation.rs:76:5 | LL | *&get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:61:5 + --> $DIR/unnecessary_operation.rs:77: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:78:5 | LL | (5, 6, get_number()); | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:63:5 + --> $DIR/unnecessary_operation.rs:79:5 | LL | get_number()..; | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:64:5 + --> $DIR/unnecessary_operation.rs:80:5 | LL | ..get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:65:5 + --> $DIR/unnecessary_operation.rs:81:5 | LL | 5..get_number(); | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:66:5 + --> $DIR/unnecessary_operation.rs:82:5 | LL | [42, get_number()]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:67:5 + --> $DIR/unnecessary_operation.rs:83: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:68:5 + --> $DIR/unnecessary_operation.rs:84:5 | LL | (42, get_number()).1; | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:69:5 + --> $DIR/unnecessary_operation.rs:85:5 | LL | [get_number(); 55]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:70:5 + --> $DIR/unnecessary_operation.rs:86: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:71:5 + --> $DIR/unnecessary_operation.rs:87:5 | LL | / { LL | | get_number() @@ -112,7 +112,7 @@ LL | | }; | |______^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:74:5 + --> $DIR/unnecessary_operation.rs:90:5 | LL | / FooString { LL | | s: String::from("blah"), diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed index 67faabc53cb..2dd1d746626 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -1,4 +1,10 @@ -#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)] +#![allow( + clippy::needless_borrow, + clippy::needless_borrows_for_generic_args, + clippy::ptr_arg, + clippy::manual_async_fn, + clippy::needless_lifetimes +)] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] use std::borrow::Cow; @@ -506,3 +512,18 @@ mod issue_10033 { } } } + +mod issue_11952 { + use core::future::{Future, IntoFuture}; + + fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future<Output = Result<(), ()>> { + async move { + let _y = y; + Ok(()) + } + } + + fn bar() { + IntoFuture::into_future(foo([], &0)); + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 99f9136427d..17fad33402b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -1,4 +1,10 @@ -#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)] +#![allow( + clippy::needless_borrow, + clippy::needless_borrows_for_generic_args, + clippy::ptr_arg, + clippy::manual_async_fn, + clippy::needless_lifetimes +)] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] use std::borrow::Cow; @@ -506,3 +512,18 @@ mod issue_10033 { } } } + +mod issue_11952 { + use core::future::{Future, IntoFuture}; + + fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future<Output = Result<(), ()>> { + async move { + let _y = y; + Ok(()) + } + } + + fn bar() { + IntoFuture::into_future(foo([].to_vec(), &0)); + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr index d8971b51dca..ad6fa422b8c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:148:64 + --> $DIR/unnecessary_to_owned.rs:154:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:148:20 + --> $DIR/unnecessary_to_owned.rs:154:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,55 +13,55 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> $DIR/unnecessary_to_owned.rs:149:40 + --> $DIR/unnecessary_to_owned.rs:155:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:149:21 + --> $DIR/unnecessary_to_owned.rs:155:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:150:48 + --> $DIR/unnecessary_to_owned.rs:156:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:150:19 + --> $DIR/unnecessary_to_owned.rs:156:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:151:35 + --> $DIR/unnecessary_to_owned.rs:157:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:151:18 + --> $DIR/unnecessary_to_owned.rs:157:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:152:39 + --> $DIR/unnecessary_to_owned.rs:158:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:152:20 + --> $DIR/unnecessary_to_owned.rs:158:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:57:36 + --> $DIR/unnecessary_to_owned.rs:63:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -70,415 +70,415 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:58:19 + --> $DIR/unnecessary_to_owned.rs:64:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> $DIR/unnecessary_to_owned.rs:60:20 + --> $DIR/unnecessary_to_owned.rs:66:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:61:38 + --> $DIR/unnecessary_to_owned.rs:67:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:62:20 + --> $DIR/unnecessary_to_owned.rs:68:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> $DIR/unnecessary_to_owned.rs:64:18 + --> $DIR/unnecessary_to_owned.rs:70:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:65:34 + --> $DIR/unnecessary_to_owned.rs:71:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:66:18 + --> $DIR/unnecessary_to_owned.rs:72:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:68:17 + --> $DIR/unnecessary_to_owned.rs:74:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:69:30 + --> $DIR/unnecessary_to_owned.rs:75:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:70:17 + --> $DIR/unnecessary_to_owned.rs:76:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:71:17 + --> $DIR/unnecessary_to_owned.rs:77:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:73:19 + --> $DIR/unnecessary_to_owned.rs:79:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:74:36 + --> $DIR/unnecessary_to_owned.rs:80:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:75:19 + --> $DIR/unnecessary_to_owned.rs:81:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:76:19 + --> $DIR/unnecessary_to_owned.rs:82:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:77:19 + --> $DIR/unnecessary_to_owned.rs:83:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:80:42 + --> $DIR/unnecessary_to_owned.rs:86:42 | LL | require_x(&Cow::<X>::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:83:25 + --> $DIR/unnecessary_to_owned.rs:89:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:84:26 + --> $DIR/unnecessary_to_owned.rs:90:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:85:24 + --> $DIR/unnecessary_to_owned.rs:91:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:86:23 + --> $DIR/unnecessary_to_owned.rs:92:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:87:25 + --> $DIR/unnecessary_to_owned.rs:93:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:89:30 + --> $DIR/unnecessary_to_owned.rs:95:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:90:31 + --> $DIR/unnecessary_to_owned.rs:96:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:91:29 + --> $DIR/unnecessary_to_owned.rs:97:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:92:28 + --> $DIR/unnecessary_to_owned.rs:98:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:93:30 + --> $DIR/unnecessary_to_owned.rs:99:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:29 + --> $DIR/unnecessary_to_owned.rs:101:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:43 + --> $DIR/unnecessary_to_owned.rs:101:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:96:29 + --> $DIR/unnecessary_to_owned.rs:102:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:96:47 + --> $DIR/unnecessary_to_owned.rs:102:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:26 + --> $DIR/unnecessary_to_owned.rs:104:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:99:27 + --> $DIR/unnecessary_to_owned.rs:105:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:100:25 + --> $DIR/unnecessary_to_owned.rs:106:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:24 + --> $DIR/unnecessary_to_owned.rs:107:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:24 + --> $DIR/unnecessary_to_owned.rs:108:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:103:26 + --> $DIR/unnecessary_to_owned.rs:109:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:104:26 + --> $DIR/unnecessary_to_owned.rs:110:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:105:26 + --> $DIR/unnecessary_to_owned.rs:111:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:107:31 + --> $DIR/unnecessary_to_owned.rs:113:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:108:32 + --> $DIR/unnecessary_to_owned.rs:114:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:109:30 + --> $DIR/unnecessary_to_owned.rs:115:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:110:29 + --> $DIR/unnecessary_to_owned.rs:116:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:111:29 + --> $DIR/unnecessary_to_owned.rs:117:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:112:31 + --> $DIR/unnecessary_to_owned.rs:118:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:113:31 + --> $DIR/unnecessary_to_owned.rs:119:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:114:31 + --> $DIR/unnecessary_to_owned.rs:120:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:30 + --> $DIR/unnecessary_to_owned.rs:122:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:44 + --> $DIR/unnecessary_to_owned.rs:122:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:117:30 + --> $DIR/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:117:44 + --> $DIR/unnecessary_to_owned.rs:123:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:30 + --> $DIR/unnecessary_to_owned.rs:124:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:44 + --> $DIR/unnecessary_to_owned.rs:124:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:30 + --> $DIR/unnecessary_to_owned.rs:125:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:48 + --> $DIR/unnecessary_to_owned.rs:125:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:30 + --> $DIR/unnecessary_to_owned.rs:126:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:52 + --> $DIR/unnecessary_to_owned.rs:126:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:30 + --> $DIR/unnecessary_to_owned.rs:127:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:48 + --> $DIR/unnecessary_to_owned.rs:127:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:123:20 + --> $DIR/unnecessary_to_owned.rs:129:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:125:13 + --> $DIR/unnecessary_to_owned.rs:131:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:126:13 + --> $DIR/unnecessary_to_owned.rs:132:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:127:13 + --> $DIR/unnecessary_to_owned.rs:133:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:128:13 + --> $DIR/unnecessary_to_owned.rs:134:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:130:13 + --> $DIR/unnecessary_to_owned.rs:136:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:131:13 + --> $DIR/unnecessary_to_owned.rs:137:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:132:13 + --> $DIR/unnecessary_to_owned.rs:138:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:133:13 + --> $DIR/unnecessary_to_owned.rs:139:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:195:14 + --> $DIR/unnecessary_to_owned.rs:201:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -494,28 +494,34 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:218:14 + --> $DIR/unnecessary_to_owned.rs:224:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:223:14 + --> $DIR/unnecessary_to_owned.rs:229:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:270:24 + --> $DIR/unnecessary_to_owned.rs:276:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:378:12 + --> $DIR/unnecessary_to_owned.rs:384:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` -error: aborting due to 79 previous errors +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:527:37 + | +LL | IntoFuture::into_future(foo([].to_vec(), &0)); + | ^^^^^^^^^^^ help: use: `[]` + +error: aborting due to 80 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs index 373b18470f6..5ad117eb8db 100644 --- a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs +++ b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs -#![allow(clippy::let_unit_value)] +#![allow(clippy::let_unit_value, clippy::needless_pass_by_ref_mut)] #![warn(clippy::unnecessary_safety_doc)] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed index bcbca971a78..81b8bd7da77 100644 --- a/src/tools/clippy/tests/ui/vec.fixed +++ b/src/tools/clippy/tests/ui/vec.fixed @@ -176,3 +176,37 @@ fn below() { let _: String = a; } } + +fn func_needing_vec(_bar: usize, _baz: Vec<usize>) {} +fn func_not_needing_vec(_bar: usize, _baz: usize) {} + +fn issue11861() { + macro_rules! this_macro_needs_vec { + ($x:expr) => {{ + func_needing_vec($x.iter().sum(), $x); + for _ in $x {} + }}; + } + macro_rules! this_macro_doesnt_need_vec { + ($x:expr) => {{ func_not_needing_vec($x.iter().sum(), $x.iter().sum()) }}; + } + + // Do not lint the next line + this_macro_needs_vec!(vec![1]); + this_macro_doesnt_need_vec!([1]); //~ ERROR: useless use of `vec!` + + macro_rules! m { + ($x:expr) => { + fn f2() { + let _x: Vec<i32> = $x; + } + fn f() { + let _x = $x; + $x.starts_with(&[]); + } + }; + } + + // should not lint + m!(vec![1]); +} diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs index 087425585de..5aca9b2925c 100644 --- a/src/tools/clippy/tests/ui/vec.rs +++ b/src/tools/clippy/tests/ui/vec.rs @@ -176,3 +176,37 @@ fn below() { let _: String = a; } } + +fn func_needing_vec(_bar: usize, _baz: Vec<usize>) {} +fn func_not_needing_vec(_bar: usize, _baz: usize) {} + +fn issue11861() { + macro_rules! this_macro_needs_vec { + ($x:expr) => {{ + func_needing_vec($x.iter().sum(), $x); + for _ in $x {} + }}; + } + macro_rules! this_macro_doesnt_need_vec { + ($x:expr) => {{ func_not_needing_vec($x.iter().sum(), $x.iter().sum()) }}; + } + + // Do not lint the next line + this_macro_needs_vec!(vec![1]); + this_macro_doesnt_need_vec!(vec![1]); //~ ERROR: useless use of `vec!` + + macro_rules! m { + ($x:expr) => { + fn f2() { + let _x: Vec<i32> = $x; + } + fn f() { + let _x = $x; + $x.starts_with(&[]); + } + }; + } + + // should not lint + m!(vec![1]); +} diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr index fc261838fe3..c9018f94f9d 100644 --- a/src/tools/clippy/tests/ui/vec.stderr +++ b/src/tools/clippy/tests/ui/vec.stderr @@ -115,5 +115,11 @@ error: useless use of `vec!` LL | for a in vec![String::new(), String::new()] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]` -error: aborting due to 19 previous errors +error: useless use of `vec!` + --> $DIR/vec.rs:196:33 + | +LL | this_macro_doesnt_need_vec!(vec![1]); + | ^^^^^^^ help: you can use an array directly: `[1]` + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index ab2fb1a3291..96085bcf9ee 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -19,6 +19,7 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" +users_on_vacation = ["blyxyas"] [assign.owners] "/.github" = ["@flip1995"] |
