From f0f392781f2829aaf4368e28c2ba3e9bc0e6115f Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Tue, 2 Apr 2024 00:26:10 +0200 Subject: [PATCH 01/58] store the span of the nested part of the use tree in the ast --- clippy_lints/src/single_component_path_imports.rs | 8 ++++---- clippy_lints/src/unnecessary_self_imports.rs | 4 ++-- clippy_lints/src/unsafe_removed_from_name.rs | 4 ++-- clippy_utils/src/ast_utils.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 18fbbdb407911..acf44a9bb5ab4 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -201,8 +201,8 @@ impl SingleComponentPathImports { if segments.is_empty() { // keep track of `use {some_module, some_other_module};` usages - if let UseTreeKind::Nested(trees) = &use_tree.kind { - for tree in trees { + if let UseTreeKind::Nested { items, .. } = &use_tree.kind { + for tree in items { let segments = &tree.0.prefix.segments; if segments.len() == 1 { if let UseTreeKind::Simple(None) = tree.0.kind { @@ -229,8 +229,8 @@ impl SingleComponentPathImports { } // nested case such as `use self::{module1::Struct1, module2::Struct2}` - if let UseTreeKind::Nested(trees) = &use_tree.kind { - for tree in trees { + if let UseTreeKind::Nested { items, .. } = &use_tree.kind { + for tree in items { let segments = &tree.0.prefix.segments; if !segments.is_empty() { imports_reused_with_self.push(segments[0].ident.name); diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs index ddee06b59cae1..528a1dfcfc10f 100644 --- a/clippy_lints/src/unnecessary_self_imports.rs +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -36,8 +36,8 @@ declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]); impl EarlyLintPass for UnnecessarySelfImports { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if let ItemKind::Use(use_tree) = &item.kind - && let UseTreeKind::Nested(nodes) = &use_tree.kind - && let [(self_tree, _)] = &**nodes + && let UseTreeKind::Nested { items, .. } = &use_tree.kind + && let [(self_tree, _)] = &**items && let [self_seg] = &*self_tree.prefix.segments && self_seg.ident.name == kw::SelfLower && let Some(last_segment) = use_tree.prefix.segments.last() diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 51b3ea93b6dc9..309eaedac8d29 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -49,8 +49,8 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { unsafe_to_safe_check(old_name, new_name, cx, span); }, UseTreeKind::Simple(None) | UseTreeKind::Glob => {}, - UseTreeKind::Nested(ref nested_use_tree) => { - for (use_tree, _) in nested_use_tree { + UseTreeKind::Nested { ref items, .. } => { + for (use_tree, _) in items { check_use_tree(use_tree, cx, span); } }, diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index f594a40ff59ad..36d54bb41fa4d 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -637,7 +637,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { match (l, r) { (Glob, Glob) => true, (Simple(l), Simple(r)) => both(l, r, |l, r| eq_id(*l, *r)), - (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)), + (Nested { items: l, .. }, Nested { items: r, .. }) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)), _ => false, } } From b1a4f8725795b3e7be5f461a937dbb8b35641013 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 26 Apr 2024 12:57:02 +0000 Subject: [PATCH 02/58] put `hir::AnonConst` on the hir arena --- clippy_utils/src/hir_utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 07c443acb05f7..c921168df290b 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -224,7 +224,7 @@ impl HirEqInterExpr<'_, '_, '_> { }) } - pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool { + pub fn eq_array_length(&mut self, left: ArrayLen<'_>, right: ArrayLen<'_>) -> bool { match (left, right) { (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true, (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body), @@ -1116,7 +1116,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } - pub fn hash_array_length(&mut self, length: ArrayLen) { + pub fn hash_array_length(&mut self, length: ArrayLen<'_>) { match length { ArrayLen::Infer(..) => {}, ArrayLen::Body(anon_const) => self.hash_body(anon_const.body), From 1c9bf96b96f17ac177efaec22841e2ef3addd14c Mon Sep 17 00:00:00 2001 From: Roddy Rappaport Date: Fri, 26 Apr 2024 21:58:08 +0300 Subject: [PATCH 03/58] Fix website dash replacement #12718 Searching for a lint on the website will replace all of the dashes with underscores instead of only the first one. --- util/gh-pages/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index c63edd5bf7098..7fd779fe9a46e 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -406,7 +406,7 @@ } // Search by id - if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { + if (lint.id.indexOf(searchStr.replaceAll("-", "_")) !== -1) { return true; } From fa8f4b88cbf3cfb6bad3076c24d0d11625003295 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 28 Apr 2024 14:16:19 +0000 Subject: [PATCH 04/58] Lint direct priority conflicts in [lints.workspace] --- .../src/cargo/lint_groups_priority.rs | 13 +++++++- .../lint_groups_priority/fail/Cargo.stderr | 30 ++++++++++++++++++- .../lint_groups_priority/fail/Cargo.toml | 8 +++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs index a3291c9da1096..0d9eaac882f79 100644 --- a/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/clippy_lints/src/cargo/lint_groups_priority.rs @@ -49,7 +49,7 @@ impl LintConfig { type LintTable = BTreeMap, Spanned>; -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Default)] struct Lints { #[serde(default)] rust: LintTable, @@ -57,9 +57,18 @@ struct Lints { clippy: LintTable, } +#[derive(Deserialize, Debug, Default)] +struct Workspace { + #[serde(default)] + lints: Lints, +} + #[derive(Deserialize, Debug)] struct CargoToml { + #[serde(default)] lints: Lints, + #[serde(default)] + workspace: Workspace, } #[derive(Default, Debug)] @@ -164,5 +173,7 @@ pub fn check(cx: &LateContext<'_>) { check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file); check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file); + check_table(cx, cargo_toml.workspace.lints.rust, &rustc_groups, &file); + check_table(cx, cargo_toml.workspace.lints.clippy, &clippy_groups, &file); } } diff --git a/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr b/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr index 103e60d84844c..9177e99f8e6e6 100644 --- a/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr +++ b/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr @@ -42,4 +42,32 @@ help: to have lints override the group set `pedantic` to a lower priority 19 | pedantic = { level = "warn", priority = -2 } | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -error: could not compile `fail` (lib) due to 3 previous errors +error: lint group `rust_2018_idioms` has the same priority (0) as a lint + --> Cargo.toml:23:1 + | +23 | rust_2018_idioms = "warn" + | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 +24 | bare_trait_objects = "allow" + | ------------------ has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo +help: to have lints override the group set `rust_2018_idioms` to a lower priority + | +23 | rust_2018_idioms = { level = "warn", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: lint group `pedantic` has the same priority (0) as a lint + --> Cargo.toml:27:1 + | +27 | pedantic = "warn" + | ^^^^^^^^ ------ has an implicit priority of 0 +28 | similar_names = "allow" + | ------------- has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo +help: to have lints override the group set `pedantic` to a lower priority + | +27 | pedantic = { level = "warn", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: could not compile `fail` (lib) due to 5 previous errors diff --git a/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml b/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml index 4ce41f7817118..e4d4af9cd234a 100644 --- a/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml +++ b/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml @@ -18,3 +18,11 @@ deprecated = "allow" [lints.clippy] pedantic = { level = "warn", priority = -1 } similar_names = { level = "allow", priority = -1 } + +[workspace.lints.rust] +rust_2018_idioms = "warn" +bare_trait_objects = "allow" + +[workspace.lints.clippy] +pedantic = "warn" +similar_names = "allow" From c187bff864234e869dabcb41d2336639e29e2511 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 26 Mar 2024 17:16:01 +0000 Subject: [PATCH 05/58] Fix `FormatArgs` storage when `-Zthreads` > 1 --- clippy_lints/src/explicit_write.rs | 20 +++-- clippy_lints/src/format.rs | 19 ++++- clippy_lints/src/format_args.rs | 13 ++-- clippy_lints/src/format_impl.rs | 10 ++- clippy_lints/src/lib.rs | 42 +++++++--- clippy_lints/src/methods/expect_fun_call.rs | 7 +- clippy_lints/src/methods/mod.rs | 15 +++- .../src/utils/format_args_collector.rs | 27 ++++--- clippy_lints/src/write.rs | 14 ++-- clippy_utils/src/macros.rs | 78 +++++++++---------- 10 files changed, 151 insertions(+), 94 deletions(-) diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 33bd5a5a9d3a3..df11ee36fc027 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_args, format_args_inputs_span}; +use clippy_utils::macros::{format_args_inputs_span, FormatArgsStorage}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_expn_of, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::{sym, ExpnId}; declare_clippy_lint! { @@ -38,7 +38,17 @@ declare_clippy_lint! { "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work" } -declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]); +pub struct ExplicitWrite { + format_args: FormatArgsStorage, +} + +impl ExplicitWrite { + pub fn new(format_args: FormatArgsStorage) -> Self { + Self { format_args } + } +} + +impl_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]); impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -57,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { Some(sym::io_stderr) => ("stderr", "e"), _ => return, }; - let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) else { + let Some(format_args) = self.format_args.get(cx, write_arg, ExpnId::root()) else { return; }; @@ -83,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { }; let mut applicability = Applicability::MachineApplicable; let inputs_snippet = - snippet_with_applicability(cx, format_args_inputs_span(&format_args), "..", &mut applicability); + snippet_with_applicability(cx, format_args_inputs_span(format_args), "..", &mut applicability); span_lint_and_sugg( cx, EXPLICIT_WRITE, diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 8a0cd155d211c..0b248f784b762 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_arg_expr, find_format_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; @@ -7,7 +7,7 @@ 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_session::impl_lint_pass; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -39,13 +39,24 @@ declare_clippy_lint! { "useless use of `format!`" } -declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); +#[allow(clippy::module_name_repetitions)] +pub struct UselessFormat { + format_args: FormatArgsStorage, +} + +impl UselessFormat { + pub fn new(format_args: FormatArgsStorage) -> Self { + Self { format_args } + } +} + +impl_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) && cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) - && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { let mut applicability = Applicability::MachineApplicable; let call_site = macro_call.span; diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 003a9995c15f3..86115807aa4d1 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -3,8 +3,8 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; use clippy_utils::macros::{ - find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, - is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall, + find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro, + is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall, }; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; @@ -167,15 +167,18 @@ impl_lint_pass!(FormatArgs => [ UNUSED_FORMAT_SPECS, ]); +#[allow(clippy::struct_field_names)] pub struct FormatArgs { + format_args: FormatArgsStorage, msrv: Msrv, ignore_mixed: bool, } impl FormatArgs { #[must_use] - pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { + pub fn new(format_args: FormatArgsStorage, msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { Self { + format_args, msrv, ignore_mixed: allow_mixed_uninlined_format_args, } @@ -186,13 +189,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) && is_format_macro(cx, macro_call.def_id) - && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { let linter = FormatArgsExpr { cx, expr, macro_call: ¯o_call, - format_args: &format_args, + format_args, ignore_mixed: self.ignore_mixed, }; diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 0a52347940abb..09be7237b5ca3 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node}; +use clippy_utils::macros::{find_format_arg_expr, is_format_macro, root_macro_call_first_node, FormatArgsStorage}; use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; @@ -99,13 +99,15 @@ struct FormatTraitNames { #[derive(Default)] pub struct FormatImpl { + format_args: FormatArgsStorage, // Whether we are inside Display or Debug trait impl - None for neither format_trait_impl: Option, } impl FormatImpl { - pub fn new() -> Self { + pub fn new(format_args: FormatArgsStorage) -> Self { Self { + format_args, format_trait_impl: None, } } @@ -129,6 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl { if let Some(format_trait_impl) = self.format_trait_impl { let linter = FormatImplExpr { cx, + format_args: &self.format_args, expr, format_trait_impl, }; @@ -141,6 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl { struct FormatImplExpr<'a, 'tcx> { cx: &'a LateContext<'tcx>, + format_args: &'a FormatArgsStorage, expr: &'tcx Expr<'tcx>, format_trait_impl: FormatTraitNames, } @@ -175,7 +179,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> { if let Some(outer_macro) = root_macro_call_first_node(self.cx, self.expr) && let macro_def_id = outer_macro.def_id && is_format_macro(self.cx, macro_def_id) - && let Some(format_args) = find_format_args(self.cx, self.expr, outer_macro.expn) + && let Some(format_args) = self.format_args.get(self.cx, self.expr, outer_macro.expn) { for piece in &format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e2aac58bf9793..c3c2f63424d48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -60,11 +60,6 @@ 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}; - #[cfg(feature = "internal")] pub mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] @@ -384,6 +379,10 @@ mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` use clippy_config::{get_configuration_metadata, Conf}; +use clippy_utils::macros::FormatArgsStorage; +use rustc_data_structures::fx::FxHashSet; +use rustc_lint::{Lint, LintId}; +use std::collections::BTreeMap; /// Register all pre expansion lints /// @@ -614,6 +613,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { } } + let format_args_storage = FormatArgsStorage::default(); + let format_args = format_args_storage.clone(); + store.register_early_pass(move || { + Box::new(utils::format_args_collector::FormatArgsCollector::new( + format_args.clone(), + )) + }); + // all the internal lints #[cfg(feature = "internal")] { @@ -654,7 +661,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { .collect(), )) }); - store.register_early_pass(|| Box::::default()); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); store.register_late_pass(move |_| { @@ -696,6 +702,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); + let format_args = format_args_storage.clone(); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, @@ -703,6 +710,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_expect_in_tests, allow_unwrap_in_tests, allowed_dotfiles.clone(), + format_args.clone(), )) }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); @@ -766,7 +774,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); - store.register_late_pass(|_| Box::new(format::UselessFormat)); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); store.register_late_pass(|_| Box::::default()); @@ -790,7 +799,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold))); - store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite)); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone()))); store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)); store.register_late_pass(move |tcx| { Box::new(pass_by_ref_or_value::PassByRefOrValue::new( @@ -832,7 +842,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); - store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new())); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone()))); store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)); store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne)); @@ -958,8 +969,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { accept_comment_above_attributes, )) }); - store - .register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined_format_args))); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| { + Box::new(format_args::FormatArgs::new( + format_args.clone(), + msrv(), + allow_mixed_uninlined_format_args, + )) + }); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); @@ -970,7 +987,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); - store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(write::Write::new(format_args.clone(), allow_print_in_tests))); store.register_late_pass(move |_| { Box::new(cargo::Cargo { ignore_publish: cargo_ignore_publish, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index fba76852344f5..c9f56e1d98097 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_args, format_args_inputs_span, root_macro_call_first_node}; +use clippy_utils::macros::{format_args_inputs_span, root_macro_call_first_node, FormatArgsStorage}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; @@ -16,6 +16,7 @@ use super::EXPECT_FUN_CALL; #[allow(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, + format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, name: &str, @@ -134,9 +135,9 @@ pub(super) fn check<'tcx>( // Special handling for `format!` as arg_root if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) - && let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn) + && let Some(format_args) = format_args_storage.get(cx, arg_root, macro_call.expn) { - let span = format_args_inputs_span(&format_args); + let span = format_args_inputs_span(format_args); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 16e508bf4e1d1..1ced448c18b3f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -133,6 +133,7 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::macros::FormatArgsStorage; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; @@ -4087,12 +4088,14 @@ declare_clippy_lint! { suspicious, "is_empty() called on strings known at compile time" } + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, allowed_dotfiles: FxHashSet, + format_args: FormatArgsStorage, } impl Methods { @@ -4103,6 +4106,7 @@ impl Methods { allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, mut allowed_dotfiles: FxHashSet, + format_args: FormatArgsStorage, ) -> Self { allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string)); @@ -4112,6 +4116,7 @@ impl Methods { allow_expect_in_tests, allow_unwrap_in_tests, allowed_dotfiles, + format_args, } } } @@ -4281,7 +4286,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); - expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); + expect_fun_call::check( + cx, + &self.format_args, + expr, + method_span, + method_call.ident.as_str(), + receiver, + args, + ); clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args); diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index 58e66c9f9b951..5acfd35fd6ae6 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -1,4 +1,4 @@ -use clippy_utils::macros::AST_FORMAT_ARGS; +use clippy_utils::macros::FormatArgsStorage; use clippy_utils::source::snippet_opt; use itertools::Itertools; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; @@ -9,13 +9,20 @@ use rustc_session::impl_lint_pass; use rustc_span::{hygiene, Span}; use std::iter::once; use std::mem; -use std::rc::Rc; -/// Collects [`rustc_ast::FormatArgs`] so that future late passes can call -/// [`clippy_utils::macros::find_format_args`] -#[derive(Default)] +/// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes pub struct FormatArgsCollector { - format_args: FxHashMap>, + format_args: FxHashMap, + storage: FormatArgsStorage, +} + +impl FormatArgsCollector { + pub fn new(storage: FormatArgsStorage) -> Self { + Self { + format_args: FxHashMap::default(), + storage, + } + } } impl_lint_pass!(FormatArgsCollector => []); @@ -27,16 +34,12 @@ impl EarlyLintPass for FormatArgsCollector { return; } - self.format_args - .insert(expr.span.with_parent(None), Rc::new((**args).clone())); + self.format_args.insert(expr.span.with_parent(None), (**args).clone()); } } fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) { - AST_FORMAT_ARGS.with(|ast_format_args| { - let result = ast_format_args.set(mem::take(&mut self.format_args)); - debug_assert!(result.is_ok()); - }); + self.storage.set(mem::take(&mut self.format_args)); } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 26c6859233d53..ff6ee0d10ad56 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall}; +use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::token::LitKind; @@ -236,13 +236,15 @@ declare_clippy_lint! { #[derive(Default)] pub struct Write { + format_args: FormatArgsStorage, in_debug_impl: bool, allow_print_in_tests: bool, } impl Write { - pub fn new(allow_print_in_tests: bool) -> Self { + pub fn new(format_args: FormatArgsStorage, allow_print_in_tests: bool) -> Self { Self { + format_args, allow_print_in_tests, ..Default::default() } @@ -307,7 +309,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { _ => return, } - if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) { + if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { // ignore `writeln!(w)` and `write!(v, some_macro!())` if format_args.span.from_expansion() { return; @@ -315,15 +317,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { match diag_name { sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, &format_args, ¯o_call, name); + check_newline(cx, format_args, ¯o_call, name); }, sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, &format_args, ¯o_call, name); + check_empty_string(cx, format_args, ¯o_call, name); }, _ => {}, } - check_literal(cx, &format_args, name); + check_literal(cx, format_args, name); if !self.in_debug_impl { for piece in &format_args.template { diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 257dd76ab15cf..8daab9b0d92cf 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -5,15 +5,13 @@ use crate::visitors::{for_each_expr, Descend}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::{Lrc, OnceLock}; use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; use rustc_lint::LateContext; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; -use std::cell::OnceCell; use std::ops::ControlFlow; -use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ sym::assert_eq_macro, @@ -388,50 +386,44 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> } } -thread_local! { - /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be - /// able to access the many features of a [`LateContext`]. - /// - /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an - /// assumption that the early pass that populates the map and the later late passes will all be - /// running on the same thread. - #[doc(hidden)] - pub static AST_FORMAT_ARGS: OnceCell>> = { - static CALLED: AtomicBool = AtomicBool::new(false); - debug_assert!( - !CALLED.swap(true, Ordering::SeqCst), - "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread", - ); - - OnceCell::new() - }; -} +/// Stores AST [`FormatArgs`] nodes for use in late lint passes, as they are in a desugared form in +/// the HIR +#[derive(Default, Clone)] +pub struct FormatArgsStorage(Lrc>>); -/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of -/// `expn_id` -pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option> { - let format_args_expr = for_each_expr(start, |expr| { - let ctxt = expr.span.ctxt(); - if ctxt.outer_expn().is_descendant_of(expn_id) { - if macro_backtrace(expr.span) - .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) - .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) - { - ControlFlow::Break(expr) +impl FormatArgsStorage { + /// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of + /// `expn_id` + /// + /// See also [`find_format_arg_expr`] + pub fn get(&self, cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&FormatArgs> { + let format_args_expr = for_each_expr(start, |expr| { + let ctxt = expr.span.ctxt(); + if ctxt.outer_expn().is_descendant_of(expn_id) { + if macro_backtrace(expr.span) + .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) + .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) + { + ControlFlow::Break(expr) + } else { + ControlFlow::Continue(Descend::Yes) + } } else { - ControlFlow::Continue(Descend::Yes) + ControlFlow::Continue(Descend::No) } - } else { - ControlFlow::Continue(Descend::No) - } - })?; + })?; - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args - .get()? - .get(&format_args_expr.span.with_parent(None)) - .cloned() - }) + debug_assert!(self.0.get().is_some(), "`FormatArgsStorage` not yet populated"); + + self.0.get()?.get(&format_args_expr.span.with_parent(None)) + } + + /// Should only be called by `FormatArgsCollector` + pub fn set(&self, format_args: FxHashMap) { + self.0 + .set(format_args) + .expect("`FormatArgsStorage::set` should only be called once"); + } } /// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if From c313ef51df3b395f2819d972223809427cd5072a Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 1 May 2024 12:04:11 +0000 Subject: [PATCH 06/58] Don't lint assigning_clones on nested late init locals --- clippy_lints/src/assigning_clones.rs | 8 +++----- clippy_utils/src/lib.rs | 15 +++++++++++++++ tests/ui/assigning_clones.fixed | 6 ++++++ tests/ui/assigning_clones.rs | 6 ++++++ tests/ui/assigning_clones.stderr | 18 +++++++++--------- 5 files changed, 39 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index d88ca84fb97d8..1beeb4f713908 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -2,9 +2,9 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::HirNode; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_trait_method, path_to_local}; +use clippy_utils::{is_trait_method, local_is_initialized, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, Expr, ExprKind, Node}; +use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Instance, Mutability}; use rustc_session::impl_lint_pass; @@ -163,9 +163,7 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. - if let Some(Node::LetStmt(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p)) - && local.init.is_none() - { + if !local_is_initialized(cx, local) { return false; } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index fccd75d815333..649515a29b824 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -192,6 +192,21 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option< None } +/// Checks if the given local has an initializer or is from something other than a `let` statement +/// +/// e.g. returns true for `x` in `fn f(x: usize) { .. }` and `let x = 1;` but false for `let x;` +pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool { + for (_, node) in cx.tcx.hir().parent_iter(local) { + match node { + Node::Pat(..) | Node::PatField(..) => {}, + Node::LetStmt(let_stmt) => return let_stmt.init.is_some(), + _ => return true, + } + } + + false +} + /// Returns `true` if the given `NodeId` is inside a constant context /// /// # Example diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed index 8387c7d6156b5..394d2a67ca3aa 100644 --- a/tests/ui/assigning_clones.fixed +++ b/tests/ui/assigning_clones.fixed @@ -86,6 +86,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) { a = b.clone(); } +fn late_init_let_tuple() { + let (p, q): (String, String); + p = "ghi".to_string(); + q = p.clone(); +} + #[derive(Clone)] pub struct HasDeriveClone; diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs index 6f4da9f652c99..df6b760d5fd98 100644 --- a/tests/ui/assigning_clones.rs +++ b/tests/ui/assigning_clones.rs @@ -86,6 +86,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) { a = b.clone(); } +fn late_init_let_tuple() { + let (p, q): (String, String); + p = "ghi".to_string(); + q = p.clone(); +} + #[derive(Clone)] pub struct HasDeriveClone; diff --git a/tests/ui/assigning_clones.stderr b/tests/ui/assigning_clones.stderr index 793927bd1cb10..87f63ca604fe5 100644 --- a/tests/ui/assigning_clones.stderr +++ b/tests/ui/assigning_clones.stderr @@ -68,55 +68,55 @@ LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:133:5 + --> tests/ui/assigning_clones.rs:139:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:140:5 + --> tests/ui/assigning_clones.rs:146:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:141:5 + --> tests/ui/assigning_clones.rs:147:5 | LL | a = c.to_owned(); | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:171:5 + --> tests/ui/assigning_clones.rs:177:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:175:5 + --> tests/ui/assigning_clones.rs:181:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:196:5 + --> tests/ui/assigning_clones.rs:202:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:200:5 + --> tests/ui/assigning_clones.rs:206:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:204:5 + --> tests/ui/assigning_clones.rs:210:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:208:5 + --> tests/ui/assigning_clones.rs:214:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` From b10be167025d0abec026642b2b69fb81a0edec56 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 1 May 2024 17:50:28 +0200 Subject: [PATCH 07/58] Handle rustc_on_unimplemented in duplicated_attributes --- clippy_lints/src/attrs/duplicated_attributes.rs | 3 ++- tests/ui/duplicated_attributes.rs | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs index 736ee48641d8d..40a1c4e288422 100644 --- a/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -36,9 +36,10 @@ fn check_duplicated_attr( } let Some(ident) = attr.ident() else { return }; let name = ident.name; - if name == sym::doc || name == sym::cfg_attr { + if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented { // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg // conditions are the same. + // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. return; } if let Some(direct_parent) = parent.last() diff --git a/tests/ui/duplicated_attributes.rs b/tests/ui/duplicated_attributes.rs index d51e7e37beb67..97cf4a69682d7 100644 --- a/tests/ui/duplicated_attributes.rs +++ b/tests/ui/duplicated_attributes.rs @@ -1,5 +1,5 @@ //@aux-build:proc_macro_attr.rs - +#![feature(rustc_attrs)] #![warn(clippy::duplicated_attributes)] #![cfg(any(unix, windows))] #![allow(dead_code)] @@ -20,6 +20,10 @@ fn foo() {} #[cfg(unix)] // cfgs are not handled fn bar() {} +// No warning: +#[rustc_on_unimplemented(on(_Self = "&str", label = "`a"), on(_Self = "alloc::string::String", label = "a"))] +trait Abc {} + #[proc_macro_attr::duplicated_attr()] // Should not warn! fn babar() {} From 63cb56e7893c360c92303cc8a88293a6e3f11e06 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Thu, 2 May 2024 17:11:35 +0200 Subject: [PATCH 08/58] assigning_clones: add empty line to doc --- clippy_lints/src/assigning_clones.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index f0dafb1ae0d52..0b5928a65b734 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -36,6 +36,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// struct Thing; + /// /// impl Clone for Thing { /// fn clone(&self) -> Self { todo!() } /// fn clone_from(&mut self, other: &Self) { todo!() } From 80c6f8ff7bf3d16f3d05171c7fe770c9164f0140 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 2 May 2024 17:26:44 +0200 Subject: [PATCH 09/58] Merge commit '20b085d500dfba5afe0869707bf357af3afe20be' into clippy-subtree-update --- CHANGELOG.md | 56 ++++- Cargo.toml | 4 +- book/src/development/adding_lints.md | 8 +- book/src/lint_configuration.md | 15 +- clippy_config/Cargo.toml | 2 +- clippy_config/src/conf.rs | 9 +- clippy_config/src/msrvs.rs | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/assigning_clones.rs | 4 +- clippy_lints/src/box_default.rs | 4 +- clippy_lints/src/cargo/mod.rs | 2 +- .../src/casts/cast_possible_truncation.rs | 7 +- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/collection_is_never_read.rs | 27 ++- clippy_lints/src/copies.rs | 44 ++-- clippy_lints/src/dbg_macro.rs | 10 +- clippy_lints/src/dereference.rs | 4 +- clippy_lints/src/format_args.rs | 6 +- clippy_lints/src/implied_bounds_in_impls.rs | 2 +- clippy_lints/src/index_refutable_slice.rs | 9 +- clippy_lints/src/large_stack_arrays.rs | 65 +++++- clippy_lints/src/lib.rs | 2 + .../src/loops/unused_enumerate_index.rs | 7 +- clippy_lints/src/manual_assert.rs | 5 +- clippy_lints/src/manual_is_ascii_check.rs | 101 +++++---- clippy_lints/src/matches/collapsible_match.rs | 23 +- clippy_lints/src/matches/mod.rs | 23 +- clippy_lints/src/matches/redundant_guards.rs | 27 +-- clippy_lints/src/matches/single_match.rs | 2 +- clippy_lints/src/methods/filter_map.rs | 5 +- .../src/methods/iter_overeager_cloned.rs | 3 +- clippy_lints/src/methods/mod.rs | 17 +- .../src/methods/readonly_write_lock.rs | 7 +- .../src/methods/single_char_insert_string.rs | 2 +- .../src/methods/single_char_pattern.rs | 6 +- .../src/methods/single_char_push_string.rs | 2 +- .../src/methods/unused_enumerate_index.rs | 9 +- clippy_lints/src/methods/utils.rs | 8 +- clippy_lints/src/multiple_bound_locations.rs | 2 +- clippy_lints/src/mut_key.rs | 67 ++---- clippy_lints/src/needless_for_each.rs | 16 +- clippy_lints/src/needless_late_init.rs | 3 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 26 ++- clippy_lints/src/needless_pass_by_value.rs | 4 +- clippy_lints/src/non_canonical_impls.rs | 46 +++- clippy_lints/src/non_copy_const.rs | 99 +++------ .../src/operators/arithmetic_side_effects.rs | 53 +++-- clippy_lints/src/ptr.rs | 9 +- clippy_lints/src/question_mark.rs | 4 +- clippy_lints/src/repeat_vec_with_capacity.rs | 5 +- clippy_lints/src/size_of_ref.rs | 2 +- .../src/slow_vector_initialization.rs | 6 +- ...ead_local_initializer_can_be_made_const.rs | 36 ++- clippy_lints/src/to_string_trait_impl.rs | 2 +- clippy_lints/src/trait_bounds.rs | 1 + clippy_lints/src/types/mod.rs | 12 +- clippy_lints/src/utils/author.rs | 4 +- clippy_lints/src/vec.rs | 6 +- clippy_lints/src/vec_init_then_push.rs | 4 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/src/lib.rs | 16 +- clippy_utils/src/macros.rs | 10 + clippy_utils/src/paths.rs | 5 - clippy_utils/src/ty.rs | 124 +++++++---- declare_clippy_lint/Cargo.toml | 2 +- rust-toolchain | 2 +- tests/compile-test.rs | 13 +- tests/ui-toml/mut_key/mut_key.rs | 8 + .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui-toml/useless_vec/clippy.toml | 1 + tests/ui-toml/useless_vec/useless_vec.fixed | 26 +++ tests/ui-toml/useless_vec/useless_vec.rs | 26 +++ tests/ui-toml/useless_vec/useless_vec.stderr | 11 + tests/ui/arithmetic_side_effects.rs | 10 + tests/ui/arithmetic_side_effects.stderr | 14 +- tests/ui/auxiliary/proc_macros.rs | 17 ++ .../borrow_interior_mutable_const/traits.rs | 4 +- .../traits.stderr | 18 +- tests/ui/box_default.fixed | 12 + tests/ui/box_default.rs | 12 + tests/ui/box_default.stderr | 8 +- tests/ui/cast.rs | 21 +- tests/ui/cast.stderr | 206 ++++++++++-------- tests/ui/collapsible_match.rs | 16 +- tests/ui/collapsible_match.stderr | 68 +++--- tests/ui/collection_is_never_read.rs | 14 ++ tests/ui/crashes/ice-5238.rs | 3 +- .../declare_interior_mutable_const/traits.rs | 5 +- .../traits.stderr | 20 +- tests/ui/disallowed_names.rs | 5 + tests/ui/large_stack_arrays.rs | 48 ++++ tests/ui/large_stack_arrays.stderr | 60 ++++- tests/ui/manual_is_ascii_check.fixed | 27 +++ tests/ui/manual_is_ascii_check.rs | 27 +++ tests/ui/manual_is_ascii_check.stderr | 42 +++- tests/ui/mistyped_literal_suffix.fixed | 2 +- tests/ui/mistyped_literal_suffix.rs | 2 +- tests/ui/mut_key.rs | 10 +- tests/ui/mut_key.stderr | 32 +-- tests/ui/needless_for_each_fixable.fixed | 4 + tests/ui/needless_for_each_fixable.rs | 4 + tests/ui/needless_pass_by_ref_mut.rs | 42 +++- tests/ui/needless_pass_by_ref_mut.stderr | 98 +++++---- tests/ui/needless_pass_by_ref_mut2.fixed | 24 ++ tests/ui/needless_pass_by_ref_mut2.rs | 24 ++ tests/ui/needless_pass_by_ref_mut2.stderr | 20 ++ tests/ui/non_canonical_partial_ord_impl.fixed | 18 ++ tests/ui/non_canonical_partial_ord_impl.rs | 18 ++ tests/ui/readonly_write_lock.fixed | 4 + tests/ui/readonly_write_lock.rs | 4 + tests/ui/redundant_guards.fixed | 12 + tests/ui/redundant_guards.rs | 12 + tests/ui/redundant_guards.stderr | 34 +-- tests/ui/redundant_locals.rs | 22 +- tests/ui/single_char_pattern.fixed | 14 +- tests/ui/single_char_pattern.rs | 8 +- tests/ui/single_char_pattern.stderr | 62 ++---- ..._local_initializer_can_be_made_const.fixed | 33 ++- ...ead_local_initializer_can_be_made_const.rs | 33 ++- tests/ui/type_complexity.rs | 2 - tests/ui/type_complexity.stderr | 30 +-- tests/ui/type_id_on_box_unfixable.rs | 2 +- triagebot.toml | 1 - util/gh-pages/script.js | 2 +- 124 files changed, 1664 insertions(+), 755 deletions(-) create mode 100644 tests/ui-toml/useless_vec/clippy.toml create mode 100644 tests/ui-toml/useless_vec/useless_vec.fixed create mode 100644 tests/ui-toml/useless_vec/useless_vec.rs create mode 100644 tests/ui-toml/useless_vec/useless_vec.stderr create mode 100644 tests/ui/needless_pass_by_ref_mut2.fixed create mode 100644 tests/ui/needless_pass_by_ref_mut2.rs create mode 100644 tests/ui/needless_pass_by_ref_mut2.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index bd3a04e34ae31..9c9ea11408143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,62 @@ document. ## Unreleased / Beta / In Rust Nightly -[66c29b97...master](https://github.com/rust-lang/rust-clippy/compare/66c29b97...master) +[93f0a9a9...master](https://github.com/rust-lang/rust-clippy/compare/93f0a9a9...master) + +## Rust 1.78 + +Current stable, released 2024-05-02 + +[View all 112 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-01-26T05%3A46%3A23Z..2024-03-07T16%3A25%3A52Z+base%3Amaster) + +### New Lints + +* [`assigning_clones`] + [#12077](https://github.com/rust-lang/rust-clippy/pull/12077) +* [`mixed_attributes_style`] + [#12354](https://github.com/rust-lang/rust-clippy/pull/12354) +* [`empty_docs`] + [#12342](https://github.com/rust-lang/rust-clippy/pull/12342) +* [`unnecessary_get_then_check`] + [#12339](https://github.com/rust-lang/rust-clippy/pull/12339) +* [`multiple_bound_locations`] + [#12259](https://github.com/rust-lang/rust-clippy/pull/12259) +* [`unnecessary_clippy_cfg`] + [#12303](https://github.com/rust-lang/rust-clippy/pull/12303) +* [`deprecated_clippy_cfg_attr`] + [#12292](https://github.com/rust-lang/rust-clippy/pull/12292) +* [`manual_c_str_literals`] + [#11919](https://github.com/rust-lang/rust-clippy/pull/11919) +* [`ref_as_ptr`] + [#12087](https://github.com/rust-lang/rust-clippy/pull/12087) +* [`lint_groups_priority`] + [#11832](https://github.com/rust-lang/rust-clippy/pull/11832) +* [`unnecessary_result_map_or_else`] + [#12169](https://github.com/rust-lang/rust-clippy/pull/12169) +* [`to_string_trait_impl`] + [#12122](https://github.com/rust-lang/rust-clippy/pull/12122) +* [`incompatible_msrv`] + [#12160](https://github.com/rust-lang/rust-clippy/pull/12160) + +### Enhancements + +* [`thread_local_initializer_can_be_made_const`]: Now checks the [`msrv`] configuration + [#12405](https://github.com/rust-lang/rust-clippy/pull/12405) +* [`disallowed_macros`]: Code generated by derive macros can no longer allow this lint + [#12267](https://github.com/rust-lang/rust-clippy/pull/12267) +* [`wildcard_imports`]: Add configuration [`allowed-wildcard-imports`] to allow preconfigured wildcards + [#11979](https://github.com/rust-lang/rust-clippy/pull/11979) + +### ICE Fixes + +* [`ptr_as_ptr`]: No longer ICEs when the cast source is a function call to a local variable + [#12617](https://github.com/rust-lang/rust-clippy/pull/12617) +* [`cast_sign_loss`]: Avoids an infinite loop when casting two chained `.unwrap()` calls + [#12508](https://github.com/rust-lang/rust-clippy/pull/12508) ## Rust 1.77 -Current stable, released 2024-03-18 +Released 2024-03-18 [View all 93 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-12-16T18%3A20%3A00Z..2024-01-25T18%3A15%3A56Z+base%3Amaster) @@ -5891,6 +5942,7 @@ Released 2018-09-13 [`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests [`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests +[`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates [`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars diff --git a/Cargo.toml b/Cargo.toml index 43f20ecedc21d..b48f3ab3919cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.79" +version = "0.1.80" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -30,7 +30,7 @@ color-print = "0.3.4" anstream = "0.6.0" [dev-dependencies] -ui_test = "0.22.2" +ui_test = "0.23" regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index b80ac6370e7a5..415022612caaa 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -299,10 +299,10 @@ This is good, because it makes writing this particular lint less complicated. We have to make this decision with every new Clippy lint. It boils down to using either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass]. -In short, the `LateLintPass` has access to type information while the -`EarlyLintPass` doesn't. If you don't need access to type information, use the -`EarlyLintPass`. The `EarlyLintPass` is also faster. However, linting speed -hasn't really been a concern with Clippy so far. +In short, the `EarlyLintPass` runs before type checking and +[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass` +has access to type information. Consider using the `LateLintPass` unless you need +something specific from the `EarlyLintPass`. Since we don't need type information for checking the function name, we used `--pass=early` when running the new lint automation and all the imports were diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 7cefa6852642b..f6af9810ca166 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -132,6 +132,16 @@ Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` * [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) +## `allow-useless-vec-in-tests` +Whether `useless_vec` should ignore test functions or `#[cfg(test)]` + +**Default Value:** `false` + +--- +**Affected lints:** +* [`useless_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec) + + ## `allowed-dotfiles` Additional dotfiles (files or directories starting with a dot) to allow @@ -506,13 +516,14 @@ The maximum byte size a `Future` can have, before it triggers the `clippy::large ## `ignore-interior-mutability` -A list of paths to types that should be treated like `Arc`, i.e. ignored but -for the generic parameters for determining interior mutability +A list of paths to types that should be treated as if they do not contain interior mutability **Default Value:** `["bytes::Bytes"]` --- **Affected lints:** +* [`borrow_interior_mutable_const`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const) +* [`declare_interior_mutable_const`](https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const) * [`ifs_same_cond`](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond) * [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 8ba2ab5662569..7f7dc9d6cfb0e 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.79" +version = "0.1.80" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 781282213cc4a..5cfcbdb57d735 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -463,14 +463,17 @@ define_Conf! { /// /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` (allow_print_in_tests: bool = false), + /// Lint: USELESS_VEC. + /// + /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` + (allow_useless_vec_in_tests: bool = false), /// Lint: RESULT_LARGE_ERR. /// /// The maximum size of the `Err`-variant in a `Result` returned from a function (large_error_threshold: u64 = 128), - /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND. + /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND, BORROW_INTERIOR_MUTABLE_CONST, DECLARE_INTERIOR_MUTABLE_CONST. /// - /// A list of paths to types that should be treated like `Arc`, i.e. ignored but - /// for the generic parameters for determining interior mutability + /// A list of paths to types that should be treated as if they do not contain interior mutability (ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()])), /// Lint: UNINLINED_FORMAT_ARGS. /// diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index 59dd5b334b84b..14808440d48d3 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -23,7 +23,7 @@ msrv_aliases! { 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } - 1,63,0 { ASSIGNING_CLONES } + 1,63,0 { CLONE_INTO } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 1d954607eee8c..5e3a119337ccd 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.79" +version = "0.1.80" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index dc7f44af2b74b..f0dafb1ae0d52 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// a.clone_from(&b); /// } /// ``` - #[clippy::version = "1.77.0"] + #[clippy::version = "1.78.0"] pub ASSIGNING_CLONES, perf, "assigning the result of cloning may be inefficient" @@ -153,7 +153,7 @@ fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option< fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>, msrv: &Msrv) -> bool { // For calls to .to_owned we suggest using .clone_into(), which was only stablilized in 1.63. // If the current MSRV is below that, don't suggest the lint. - if !msrv.meets(msrvs::ASSIGNING_CLONES) && matches!(call.target, TargetTrait::ToOwned) { + if !msrv.meets(msrvs::CLONE_INTO) && matches!(call.target, TargetTrait::ToOwned) { return false; } diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 4062212f408ef..8459f051d3d9a 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -121,9 +121,9 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) && let Some(sig) = expr_sig(cx, path) && let Some(input) = sig.input(index) - && !cx.typeck_results().expr_ty_adjusted(expr).boxed_ty().is_trait() + && let Some(input_ty) = input.no_bound_vars() { - input.no_bound_vars().is_some() + input_ty == cx.typeck_results().expr_ty_adjusted(expr) } else { false } diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index ca7fa4e5a4109..593bc6c81ee8e 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -197,7 +197,7 @@ declare_clippy_lint! { /// pedantic = { level = "warn", priority = -1 } /// similar_names = "allow" /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.78.0"] pub LINT_GROUPS_PRIORITY, correctness, "a lint group in `Cargo.toml` at the same priority as a lint" diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index dbfa8e1ee91b2..7c5acd1a678d7 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -40,9 +40,14 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) }) }, - BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right) + BinOpKind::Rem => get_constant_bits(cx, right) .unwrap_or(u64::MAX) .min(apply_reductions(cx, nbits, left, signed)), + BinOpKind::BitAnd => get_constant_bits(cx, right) + .unwrap_or(u64::MAX) + .min(get_constant_bits(cx, left).unwrap_or(u64::MAX)) + .min(apply_reductions(cx, nbits, right, signed)) + .min(apply_reductions(cx, nbits, left, signed)), BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())), _ => nbits, diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index d14898a8196cf..bd2c96f01f6f4 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -708,7 +708,7 @@ declare_clippy_lint! { /// let a_ref = &1; /// let a_ptr = std::ptr::from_ref(a_ref); /// ``` - #[clippy::version = "1.77.0"] + #[clippy::version = "1.78.0"] pub REF_AS_PTR, pedantic, "using `as` to cast a reference to pointer" diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index 6942ca5364042..70856b808810b 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::visitors::for_each_expr_with_closures; +use clippy_utils::visitors::{for_each_expr_with_closures, Visitable}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; -use rustc_hir::{Block, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; +use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -77,7 +77,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: || is_type_lang_item(cx, ty, LangItem::String) } -fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool { +fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool { let mut has_access = false; let mut has_read_access = false; @@ -109,11 +109,30 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc // traits (identified as local, based on the orphan rule), pessimistically assume that they might // have side effects, so consider them a read. if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) - && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind + && let ExprKind::MethodCall(_, receiver, args, _) = parent.kind && path_to_local_id(receiver, id) && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) && !method_def_id.is_local() { + // If this "official" method takes closures, + // it has read access if one of the closures has read access. + // + // items.retain(|item| send_item(item).is_ok()); + let is_read_in_closure_arg = args.iter().any(|arg| { + if let ExprKind::Closure(closure) = arg.kind + // To keep things simple, we only check the first param to see if its read. + && let Body { params: [param, ..], value } = cx.tcx.hir().body(closure.body) + { + !has_no_read_access(cx, param.hir_id, *value) + } else { + false + } + }); + if is_read_in_closure_arg { + has_read_access = true; + return ControlFlow::Break(()); + } + // The method call is a statement, so the return value is not used. That's not a read access: // // id.foo(args); diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index acdcb54be2719..ccf1d9d6f8c0d 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,15 +1,14 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; -use clippy_utils::ty::{is_interior_mut_ty, needs_ordered_drop}; +use clippy_utils::ty::{needs_ordered_drop, InteriorMut}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ - capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, - if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, + capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence, + is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, }; use core::iter; use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::def_id::DefIdSet; use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -159,40 +158,36 @@ declare_clippy_lint! { "`if` statement with shared code in all blocks" } -pub struct CopyAndPaste { +pub struct CopyAndPaste<'tcx> { ignore_interior_mutability: Vec, - ignored_ty_ids: DefIdSet, + interior_mut: InteriorMut<'tcx>, } -impl CopyAndPaste { +impl CopyAndPaste<'_> { pub fn new(ignore_interior_mutability: Vec) -> Self { Self { ignore_interior_mutability, - ignored_ty_ids: DefIdSet::new(), + interior_mut: InteriorMut::default(), } } } -impl_lint_pass!(CopyAndPaste => [ +impl_lint_pass!(CopyAndPaste<'_> => [ IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, BRANCHES_SHARING_CODE ]); -impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { +impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { fn check_crate(&mut self, cx: &LateContext<'tcx>) { - for ignored_ty in &self.ignore_interior_mutability { - let path: Vec<&str> = ignored_ty.split("::").collect(); - for id in def_path_def_ids(cx, path.as_slice()) { - self.ignored_ty_ids.insert(id); - } - } + self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability); } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { let (conds, blocks) = if_sequence(expr); - lint_same_cond(cx, &conds, &self.ignored_ty_ids); + lint_same_cond(cx, &conds, &mut self.interior_mut); lint_same_fns_in_if_cond(cx, &conds); let all_same = !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); @@ -570,13 +565,14 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo }) } -fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignored_ty_ids: &DefIdSet) -> bool { +fn method_caller_is_mutable<'tcx>( + cx: &LateContext<'tcx>, + caller_expr: &Expr<'_>, + interior_mut: &mut InteriorMut<'tcx>, +) -> bool { let caller_ty = cx.typeck_results().expr_ty(caller_expr); - // Check if given type has inner mutability and was not set to ignored by the configuration - let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty) - && !matches!(caller_ty.ty_adt_def(), Some(adt) if ignored_ty_ids.contains(&adt.did())); - is_inner_mut_ty + interior_mut.is_interior_mut_ty(cx, caller_ty) || caller_ty.is_mutable_ptr() // `find_binding_init` will return the binding iff its not mutable || path_to_local(caller_expr) @@ -585,7 +581,7 @@ fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignore } /// Implementation of `IFS_SAME_COND`. -fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &DefIdSet) { +fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { for (i, j) in search_same( conds, |e| hash_expr(cx, e), @@ -593,7 +589,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &De // Ignore eq_expr side effects iff one of the expression kind is a method call // and the caller is not a mutable, including inner mutable type. if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { - if method_caller_is_mutable(cx, caller, ignored_ty_ids) { + if method_caller_is_mutable(cx, caller, interior_mut) { false } else { SpanlessEq::new(cx).eq_expr(lhs, rhs) diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index e229676743195..db5937266047d 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_in_test; use clippy_utils::macros::{macro_backtrace, MacroCall}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, Node}; +use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; @@ -63,7 +63,7 @@ impl LateLintPass<'_> for DbgMacro { !in_external_macro(cx.sess(), macro_call.span) && self.checked_dbg_call_site.insert(macro_call.span) && // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml - !(self.allow_dbg_in_tests && is_in_test(cx, expr.hir_id)) + !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id)) { let mut applicability = Applicability::MachineApplicable; @@ -129,10 +129,6 @@ impl LateLintPass<'_> for DbgMacro { } } -fn is_in_test(cx: &LateContext<'_>, hir_id: HirId) -> bool { - is_in_test_function(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id) -} - fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option { macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 89e2b34496806..b936b28470b53 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, - Pat, PatKind, Path, QPath, TyKind, UnOp, + self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, + PatKind, Path, QPath, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 80db617c639a5..003a9995c15f3 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; use clippy_utils::macros::{ find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, - is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall, + is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall, }; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; @@ -271,9 +271,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { let mut suggest_format = |spec| { let message = format!("for the {spec} to apply consider using `format!()`"); - if let Some(mac_call) = root_macro_call(arg_span) - && self.cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id) - { + if let Some(mac_call) = matching_root_macro_call(self.cx, arg_span, sym::format_args_macro) { diag.span_suggestion( self.cx.sess().source_map().span_until_char(mac_call.span, '!'), message, diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index 7b97fc15caaf8..3bf8d61895585 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// /// ### Limitations /// This lint does not check for implied bounds transitively. Meaning that - /// it does't check for implied bounds from supertraits of supertraits + /// it doesn't check for implied bounds from supertraits of supertraits /// (e.g. `trait A {} trait B: A {} trait C: B {}`, then having an `fn() -> impl A + C`) /// /// ### Example diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index a4c3b06046e86..128461ce7bcf3 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -93,12 +93,9 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap = FxIndexMap::default(); pat.walk_always(|pat| { // We'll just ignore mut and ref mut for simplicity sake right now - if let hir::PatKind::Binding( - hir::BindingMode(by_ref, hir::Mutability::Not), - value_hir_id, - ident, - sub_pat, - ) = pat.kind && by_ref != hir::ByRef::Yes(hir::Mutability::Mut) + if let hir::PatKind::Binding(hir::BindingMode(by_ref, hir::Mutability::Not), value_hir_id, ident, sub_pat) = + pat.kind + && by_ref != hir::ByRef::Yes(hir::Mutability::Mut) { // This block catches bindings with sub patterns. It would be hard to build a correct suggestion // for them and it's likely that the user knows what they are doing in such a case. diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index afcb674594767..208d1bb6e68a1 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -1,10 +1,13 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; +use clippy_utils::macros::macro_backtrace; use clippy_utils::source::snippet; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; +use rustc_hir::{ArrayLen, Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; use rustc_session::impl_lint_pass; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -25,20 +28,41 @@ declare_clippy_lint! { pub struct LargeStackArrays { maximum_allowed_size: u128, + prev_vec_macro_callsite: Option, } impl LargeStackArrays { #[must_use] pub fn new(maximum_allowed_size: u128) -> Self { - Self { maximum_allowed_size } + Self { + maximum_allowed_size, + prev_vec_macro_callsite: None, + } + } + + /// Check if the given span of an expr is already in a `vec!` call. + fn is_from_vec_macro(&mut self, cx: &LateContext<'_>, span: Span) -> bool { + // First, we check if this is span is within the last encountered `vec!` macro's root callsite. + self.prev_vec_macro_callsite + .is_some_and(|vec_mac| vec_mac.contains(span)) + || { + // Then, we try backtracking the macro expansions, to see if there's a `vec!` macro, + // and update the `prev_vec_macro_callsite`. + let res = macro_backtrace(span).any(|mac| cx.tcx.is_diagnostic_item(sym::vec_macro, mac.def_id)); + if res { + self.prev_vec_macro_callsite = Some(span.source_callsite()); + } + res + } } } impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind + && !self.is_from_vec_macro(cx, expr.span) && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind() && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) @@ -54,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { }) && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) { - span_lint_and_help( + span_lint_and_then( cx, LARGE_STACK_ARRAYS, expr.span, @@ -62,12 +86,33 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { "allocating a local array larger than {} bytes", self.maximum_allowed_size ), - None, - format!( - "consider allocating on the heap with `vec!{}.into_boxed_slice()`", - snippet(cx, expr.span, "[...]") - ), + |diag| { + if !might_be_expanded(cx, expr) { + diag.help(format!( + "consider allocating on the heap with `vec!{}.into_boxed_slice()`", + snippet(cx, expr.span, "[...]") + )); + } + }, ); } } } + +/// Only giving help messages if the expr does not contains macro expanded codes. +fn might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool { + /// Check if the span of `ArrayLen` of a repeat expression is within the expr's span, + /// if not, meaning this repeat expr is definitely from some proc-macro. + /// + /// This is a fail-safe to a case where even the `is_from_proc_macro` is unable to determain the + /// correct result. + fn repeat_expr_might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool { + let ExprKind::Repeat(_, ArrayLen::Body(anon_const)) = expr.kind else { + return false; + }; + let len_span = cx.tcx.def_span(anon_const.def_id); + !expr.span.contains(len_span) + } + + expr.span.from_expansion() || is_from_proc_macro(cx, expr) || repeat_expr_might_be_expanded(cx, expr) +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e2aac58bf9793..2c44c3881aa77 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -535,6 +535,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_print_in_tests, allow_private_module_inception, allow_unwrap_in_tests, + allow_useless_vec_in_tests, ref allowed_dotfiles, ref allowed_idents_below_min_chars, ref allowed_scripts, @@ -754,6 +755,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { too_large_for_stack, msrv: msrv(), span_to_lint_map: BTreeMap::new(), + allow_in_test: allow_useless_vec_in_tests, }) }); store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented)); diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index 31f0f1cfeba31..40ccfec02be58 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,11 +1,12 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; -use clippy_utils::{match_def_path, pat_is_wild, sugg}; +use clippy_utils::{pat_is_wild, sugg}; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_span::sym; /// Checks for the `UNUSED_ENUMERATE_INDEX` lint. /// @@ -16,9 +17,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ && let ty = cx.typeck_results().expr_ty(arg) && pat_is_wild(cx, &index.kind, body) && let ty::Adt(base, _) = *ty.kind() - && match_def_path(cx, base.did(), &clippy_utils::paths::CORE_ITER_ENUMERATE_STRUCT) + && cx.tcx.is_diagnostic_item(sym::Enumerate, base.did()) && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) - && match_def_path(cx, call_id, &clippy_utils::paths::CORE_ITER_ENUMERATE_METHOD) + && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id) { span_lint_and_then( cx, diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 76edbe8b755bd..d76b94eba23ed 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,12 +1,11 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::root_macro_call; +use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::{is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -42,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { && !expr.span.from_expansion() && let then = peel_blocks_with_stmt(then) && let Some(macro_call) = root_macro_call(then.span) - && cx.tcx.item_name(macro_call.def_id) == sym::panic + && is_panic(cx, macro_call.def_id) && !cx.tcx.sess.source_map().is_multiline(cond.span) && let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span) && let Some(panic_snippet) = panic_snippet.strip_suffix(')') diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 338a299740a87..6f6ba1852a681 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,15 +1,15 @@ use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::root_macro_call; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, in_constant}; +use clippy_utils::{higher, in_constant, path_to_local, peel_ref_operators}; use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd}; +use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::def_id::DefId; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -97,12 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { return; } - if let Some(macro_call) = root_macro_call(expr.span) - && is_matches_macro(cx, macro_call.def_id) - { + if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) { if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { let range = check_pat(&arm.pat.kind); - check_is_ascii(cx, macro_call.span, recv, &range); + check_is_ascii(cx, macro_call.span, recv, &range, None); } } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind && path.ident.name == sym!(contains) @@ -111,42 +109,67 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { end: Some(end), limits: RangeLimits::Closed, }) = higher::Range::hir(receiver) + && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { + let arg = peel_ref_operators(cx, arg); + let ty_sugg = get_ty_sugg(cx, arg, start); let range = check_range(start, end); - if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind { - check_is_ascii(cx, expr.span, e, &range); - } else { - check_is_ascii(cx, expr.span, arg, &range); - } + check_is_ascii(cx, expr.span, arg, &range, ty_sugg); } } extract_msrv_attr!(LateContext); } -fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) { - if let Some(sugg) = match range { - CharRange::UpperChar => Some("is_ascii_uppercase"), - CharRange::LowerChar => Some("is_ascii_lowercase"), - CharRange::FullChar => Some("is_ascii_alphabetic"), - CharRange::Digit => Some("is_ascii_digit"), - CharRange::HexDigit => Some("is_ascii_hexdigit"), - CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => None, - } { - let default_snip = ".."; - let mut app = Applicability::MachineApplicable; - let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); +fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> { + if let ExprKind::Lit(lit) = bound_expr.kind + && let local_hid = path_to_local(arg)? + && let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) + // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given + && ty_span == span + { + let ty_str = match lit.node { + Char(_) => "char", + Byte(_) => "u8", + _ => return None, + }; + return Some((*ty_span, ty_str)); + } + None +} - span_lint_and_sugg( - cx, - MANUAL_IS_ASCII_CHECK, - span, - "manual check for common ascii range", - "try", - format!("{recv}.{sugg}()"), - app, - ); +fn check_is_ascii( + cx: &LateContext<'_>, + span: Span, + recv: &Expr<'_>, + range: &CharRange, + ty_sugg: Option<(Span, &'_ str)>, +) { + let sugg = match range { + CharRange::UpperChar => "is_ascii_uppercase", + CharRange::LowerChar => "is_ascii_lowercase", + CharRange::FullChar => "is_ascii_alphabetic", + CharRange::Digit => "is_ascii_digit", + CharRange::HexDigit => "is_ascii_hexdigit", + CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => return, + }; + let default_snip = ".."; + let mut app = Applicability::MachineApplicable; + let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); + let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; + if let Some((ty_span, ty_str)) = ty_sugg { + suggestion.push((ty_span, format!("{recv}: {ty_str}"))); } + + span_lint_and_then( + cx, + MANUAL_IS_ASCII_CHECK, + span, + "manual check for common ascii range", + |diag| { + diag.multipart_suggestion("try", suggestion, app); + }, + ); } fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { @@ -187,11 +210,3 @@ fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { CharRange::Otherwise } } - -fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { - if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) { - return sym::matches_macro == name; - } - - false -} diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 6746920edc516..90cfdecc19936 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,3 +1,4 @@ +use clippy_config::msrvs::Msrv; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet; @@ -11,12 +12,12 @@ use rustc_hir::{Arm, Expr, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; -use super::COLLAPSIBLE_MATCH; +use super::{pat_contains_disallowed_or, COLLAPSIBLE_MATCH}; -pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: &Msrv) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { for arm in arms { - check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body)); + check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv); } } } @@ -26,8 +27,9 @@ pub(super) fn check_if_let<'tcx>( pat: &'tcx Pat<'_>, body: &'tcx Expr<'_>, else_expr: Option<&'tcx Expr<'_>>, + msrv: &Msrv, ) { - check_arm(cx, false, pat, body, None, else_expr); + check_arm(cx, false, pat, body, None, else_expr, msrv); } fn check_arm<'tcx>( @@ -37,6 +39,7 @@ fn check_arm<'tcx>( outer_then_body: &'tcx Expr<'tcx>, outer_guard: Option<&'tcx Expr<'tcx>>, outer_else_body: Option<&'tcx Expr<'tcx>>, + msrv: &Msrv, ) { let inner_expr = peel_blocks_with_stmt(outer_then_body); if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) @@ -57,7 +60,7 @@ fn check_arm<'tcx>( // match expression must be a local binding // match { .. } && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)) - && !pat_contains_or(inner_then_pat) + && !pat_contains_disallowed_or(inner_then_pat, msrv) // the binding must come from the pattern of the containing match arm // .... => match { .. } && let (Some(binding_span), is_innermost_parent_pat_struct) @@ -142,13 +145,3 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi }); (span, is_innermost_parent_pat_struct) } - -fn pat_contains_or(pat: &Pat<'_>) -> bool { - let mut result = false; - pat.walk(|p| { - let is_or = matches!(p.kind, PatKind::Or(_)); - result |= is_or; - !is_or - }); - result -} diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index fae2c4e4af92e..ee9f48d71ad80 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -27,7 +27,7 @@ mod wild_in_or_pats; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::source::walk_span_to_context; use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg}; -use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat}; +use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; @@ -1040,7 +1040,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { significant_drop_in_scrutinee::check(cx, expr, ex, arms, source); } - collapsible_match::check_match(cx, arms); + collapsible_match::check_match(cx, arms, &self.msrv); if !from_expansion { // These don't depend on a relationship between multiple arms match_wild_err_arm::check(cx, ex, arms); @@ -1066,7 +1066,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { needless_match::check_match(cx, ex, arms, expr); match_on_vec_items::check(cx, ex); match_str_case_mismatch::check(cx, ex, arms); - redundant_guards::check(cx, arms); + redundant_guards::check(cx, arms, &self.msrv); if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); @@ -1083,7 +1083,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr); } } else if let Some(if_let) = higher::IfLet::hir(cx, expr) { - collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else); + collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, &self.msrv); if !from_expansion { if let Some(else_expr) = if_let.if_else { if self.msrv.meets(msrvs::MATCHES_MACRO) { @@ -1195,3 +1195,18 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar Err(()) => true, } } + +/// Checks if `pat` contains OR patterns that cannot be nested due to a too low MSRV. +fn pat_contains_disallowed_or(pat: &Pat<'_>, msrv: &Msrv) -> bool { + if msrv.meets(msrvs::OR_PATTERNS) { + return false; + } + + let mut result = false; + pat.walk(|p| { + let is_or = matches!(p.kind, PatKind::Or(_)); + result |= is_or; + !is_or + }); + result +} diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 50cbccc396839..a75cf37945f77 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -1,40 +1,32 @@ +use clippy_config::msrvs::Msrv; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::matching_root_macro_call; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr, is_local_used}; use clippy_utils::{in_constant, path_to_local}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, Pat, PatKind, UnOp}; +use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; +use rustc_span::{sym, Span, Symbol}; use std::borrow::Cow; use std::ops::ControlFlow; -use super::REDUNDANT_GUARDS; +use super::{pat_contains_disallowed_or, REDUNDANT_GUARDS}; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: &Msrv) { for outer_arm in arms { let Some(guard) = outer_arm.guard else { continue; }; // `Some(x) if matches!(x, y)` - if let ExprKind::Match( - scrutinee, - [ - arm, - Arm { - pat: Pat { - kind: PatKind::Wild, .. - }, - .. - }, - ], - MatchSource::Normal, - ) = guard.kind + if let ExprKind::Match(scrutinee, [arm, _], MatchSource::Normal) = guard.kind + && matching_root_macro_call(cx, guard.span, sym::matches_macro).is_some() && let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm) + && !pat_contains_disallowed_or(arm.pat, msrv) { let pat_span = match (arm.pat.kind, binding.byref_ident) { (PatKind::Ref(pat, _), Some(_)) => pat.span, @@ -53,6 +45,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { // `Some(x) if let Some(2) = x` else if let ExprKind::Let(let_expr) = guard.kind && let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm) + && !pat_contains_disallowed_or(let_expr.pat, msrv) { let pat_span = match (let_expr.pat.kind, binding.byref_ident) { (PatKind::Ref(pat, _), Some(_)) => pat.span, diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 37f72528140f1..69791414f72c6 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -75,7 +75,7 @@ fn report_single_pattern( ) { let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; let ctxt = expr.span.ctxt(); - let mut app = Applicability::HasPlaceholders; + let mut app = Applicability::MachineApplicable; let els_str = els.map_or(String::new(), |els| { format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 581e3b308c3cd..02a11257007a9 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::{is_panic, root_macro_call}; +use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq}; @@ -247,8 +247,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { } else { None } - } else if let Some(macro_call) = root_macro_call(expr.span) - && cx.tcx.get_diagnostic_name(macro_call.def_id) == Some(sym::matches_macro) + } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some() // we know for a fact that the wildcard pattern is the second arm && let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind && path_to_local_id(scrutinee, filter_param_id) diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index 6d70989546a25..03b4680c52242 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -89,8 +89,7 @@ pub(super) fn check<'tcx>( } match it.kind { - PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) - | PatKind::Ref(_, Mutability::Mut) => { + PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, Mutability::Mut) => { to_be_discarded = true; false }, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 0939c02856429..63545d6c50359 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1145,11 +1145,16 @@ declare_clippy_lint! { /// `str` as an argument, e.g., `_.split("x")`. /// /// ### Why is this bad? - /// Performing these methods using a `char` is faster than - /// using a `str`. + /// While this can make a perf difference on some systems, + /// benchmarks have proven inconclusive. But at least using a + /// char literal makes it clear that we are looking at a single + /// character. /// /// ### Known problems - /// Does not catch multi-byte unicode characters. + /// Does not catch multi-byte unicode characters. This is by + /// design, on many machines, splitting by a non-ascii char is + /// actually slower. Please do your own measurements instead of + /// relying solely on the results of this lint. /// /// ### Example /// ```rust,ignore @@ -1162,7 +1167,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub SINGLE_CHAR_PATTERN, - perf, + pedantic, "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" } @@ -3988,7 +3993,7 @@ declare_clippy_lint! { /// let x: Result = Ok(0); /// let y = x.unwrap_or_else(|err| handle_error(err)); /// ``` - #[clippy::version = "1.77.0"] + #[clippy::version = "1.78.0"] pub UNNECESSARY_RESULT_MAP_OR_ELSE, suspicious, "making no use of the \"map closure\" when calling `.map_or_else(|err| handle_error(err), |n| n)`" @@ -4022,7 +4027,7 @@ declare_clippy_lint! { /// needs_cstr(c"Hello"); /// unsafe { libc::puts(c"World".as_ptr()) } /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.78.0"] pub MANUAL_C_STR_LITERALS, pedantic, r#"creating a `CStr` through functions when `c""` literals can be used"# diff --git a/clippy_lints/src/methods/readonly_write_lock.rs b/clippy_lints/src/methods/readonly_write_lock.rs index 9b0180d936991..774aaec1afda4 100644 --- a/clippy_lints/src/methods/readonly_write_lock.rs +++ b/clippy_lints/src/methods/readonly_write_lock.rs @@ -4,7 +4,7 @@ use clippy_utils::mir::{enclosing_mir, visit_local_usage}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node}; +use rustc_hir::{Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::mir::{Location, START_BLOCK}; use rustc_span::sym; @@ -25,6 +25,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver && is_unwrap_call(cx, unwrap_call_expr) && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) && let Node::LetStmt(local) = parent + && let PatKind::Binding(.., ident, _) = local.pat.kind + // if the binding is prefixed with `_`, it typically means + // that this guard only exists to protect a section of code + // rather than the contained data + && !ident.as_str().starts_with('_') && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id) && let Some((local, _)) = mir .local_decls diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs index 44a7ad394fa0c..20ec2b74d81e4 100644 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ b/clippy_lints/src/methods/single_char_insert_string.rs @@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR; /// lint for length-1 `str`s as argument for `insert_str` pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability, false) { let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs index 363b1f2b81229..982a7901c4537 100644 --- a/clippy_lints/src/methods/single_char_pattern.rs +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -8,7 +8,7 @@ use rustc_span::symbol::Symbol; use super::SINGLE_CHAR_PATTERN; -const PATTERN_METHODS: [(&str, usize); 24] = [ +const PATTERN_METHODS: [(&str, usize); 22] = [ ("contains", 0), ("starts_with", 0), ("ends_with", 0), @@ -27,8 +27,6 @@ const PATTERN_METHODS: [(&str, usize); 24] = [ ("rmatches", 0), ("match_indices", 0), ("rmatch_indices", 0), - ("strip_prefix", 0), - ("strip_suffix", 0), ("trim_start_matches", 0), ("trim_end_matches", 0), ("replace", 0), @@ -50,7 +48,7 @@ pub(super) fn check( && args.len() > pos && let arg = &args[pos] && let mut applicability = Applicability::MachineApplicable - && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) + && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs index 0698bd6a0c523..97c13825bc104 100644 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ b/clippy_lints/src/methods/single_char_push_string.rs @@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR; /// lint for length-1 `str`s as argument for `push_str` pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability) { + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) { let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); let sugg = format!("{base_string_snippet}.push({extension_string})"); diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index e5cc898612e9a..8b8a965b9f0c9 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then}; -use clippy_utils::paths::{CORE_ITER_ENUMERATE_METHOD, CORE_ITER_ENUMERATE_STRUCT}; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::{expr_or_init, is_trait_method, match_def_path, pat_is_wild}; +use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; @@ -42,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, let recv_ty = cx.typeck_results().expr_ty(recv); if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) // If we call a method on a `std::iter::Enumerate` instance - && match_def_path(cx, recv_ty_defid, &CORE_ITER_ENUMERATE_STRUCT) + && cx.tcx.is_diagnostic_item(sym::Enumerate, recv_ty_defid) // If we are calling a method of the `Iterator` trait && is_trait_method(cx, call_expr, sym::Iterator) // And the map argument is a closure @@ -75,10 +74,10 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, && let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind && let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id) // Make sure the method call is `std::iter::Iterator::enumerate`. - && match_def_path(cx, enumerate_defid, &CORE_ITER_ENUMERATE_METHOD) + && cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_defid) { // Check if the tuple type was explicit. It may be the type system _needs_ the type of the element - // that would be explicited in the closure. + // that would be explicitly in the closure. let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) { // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`. // Fallback to `..` if we fail getting either snippet. diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index ef00c812d510a..c50f24f824ab0 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -52,11 +52,17 @@ pub(super) fn get_hint_if_single_char_arg( cx: &LateContext<'_>, arg: &Expr<'_>, applicability: &mut Applicability, + ascii_only: bool, ) -> Option { if let ExprKind::Lit(lit) = &arg.kind && let ast::LitKind::Str(r, style) = lit.node && let string = r.as_str() - && string.chars().count() == 1 + && let len = if ascii_only { + string.len() + } else { + string.chars().count() + } + && len == 1 { let snip = snippet_with_applicability(cx, arg.span, string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { diff --git a/clippy_lints/src/multiple_bound_locations.rs b/clippy_lints/src/multiple_bound_locations.rs index 191b32408efe1..d608f3bf7b4d5 100644 --- a/clippy_lints/src/multiple_bound_locations.rs +++ b/clippy_lints/src/multiple_bound_locations.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// F: Sized + std::fmt::Debug, /// {} /// ``` - #[clippy::version = "1.77.0"] + #[clippy::version = "1.78.0"] pub MULTIPLE_BOUND_LOCATIONS, suspicious, "defining generic bounds in multiple locations" diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 8c2f43c97f4d9..2eb534da0925c 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_interior_mut_ty; -use clippy_utils::{def_path_def_ids, trait_ref_of_method}; -use rustc_data_structures::fx::FxHashSet; +use clippy_utils::trait_ref_of_method; +use clippy_utils::ty::InteriorMut; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; @@ -23,27 +22,15 @@ declare_clippy_lint! { /// ### Known problems /// /// #### False Positives - /// It's correct to use a struct that contains interior mutability as a key, when its + /// It's correct to use a struct that contains interior mutability as a key when its /// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types. /// However, this lint is unable to recognize this, so it will often cause false positives in - /// theses cases. The `bytes` crate is a great example of this. + /// these cases. /// /// #### False Negatives - /// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind - /// indirection. For example, `struct BadKey<'a>(&'a Cell)` will be seen as immutable - /// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`. - /// - /// This lint does check a few cases for indirection. Firstly, using some standard library - /// types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and - /// `BTreeSet`) directly as keys (e.g. in `HashMap>, ()>`) **will** trigger the - /// lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their - /// contained type. - /// - /// Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`) - /// apply only to the **address** of the contained value. Therefore, interior mutability - /// behind raw pointers (e.g. in `HashSet<*mut Cell>`) can't impact the value of `Hash` - /// or `Ord`, and therefore will not trigger this link. For more info, see issue - /// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745). + /// This lint does not follow raw pointers (`*const T` or `*mut T`) as `Hash` and `Ord` + /// apply only to the **address** of the contained value. This can cause false negatives for + /// custom collections that use raw pointers internally. /// /// ### Example /// ```no_run @@ -51,13 +38,12 @@ declare_clippy_lint! { /// use std::collections::HashSet; /// use std::hash::{Hash, Hasher}; /// use std::sync::atomic::AtomicUsize; - ///# #[allow(unused)] /// /// struct Bad(AtomicUsize); /// impl PartialEq for Bad { /// fn eq(&self, rhs: &Self) -> bool { /// .. - /// ; unimplemented!(); + /// # ; true /// } /// } /// @@ -66,7 +52,7 @@ declare_clippy_lint! { /// impl Hash for Bad { /// fn hash(&self, h: &mut H) { /// .. - /// ; unimplemented!(); + /// # ; /// } /// } /// @@ -80,25 +66,16 @@ declare_clippy_lint! { "Check for mutable `Map`/`Set` key type" } -#[derive(Clone)] -pub struct MutableKeyType { +pub struct MutableKeyType<'tcx> { ignore_interior_mutability: Vec, - ignore_mut_def_ids: FxHashSet, + interior_mut: InteriorMut<'tcx>, } -impl_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); +impl_lint_pass!(MutableKeyType<'_> => [ MUTABLE_KEY_TYPE ]); -impl<'tcx> LateLintPass<'tcx> for MutableKeyType { +impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { fn check_crate(&mut self, cx: &LateContext<'tcx>) { - self.ignore_mut_def_ids.clear(); - let mut path = Vec::new(); - for ty in &self.ignore_interior_mutability { - path.extend(ty.split("::")); - for id in def_path_def_ids(cx, &path[..]) { - self.ignore_mut_def_ids.insert(id); - } - path.clear(); - } + self.interior_mut = InteriorMut::without_pointers(cx, &self.ignore_interior_mutability); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { @@ -121,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { } } - fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &hir::LetStmt<'tcx>) { if let hir::PatKind::Wild = local.pat.kind { return; } @@ -129,15 +106,15 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { } } -impl MutableKeyType { +impl<'tcx> MutableKeyType<'tcx> { pub fn new(ignore_interior_mutability: Vec) -> Self { Self { ignore_interior_mutability, - ignore_mut_def_ids: FxHashSet::default(), + interior_mut: InteriorMut::default(), } } - fn check_sig(&self, cx: &LateContext<'_>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'_>) { + fn check_sig(&mut self, cx: &LateContext<'tcx>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'tcx>) { let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity(); for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { self.check_ty_(cx, hir_ty.span, *ty); @@ -151,7 +128,7 @@ impl MutableKeyType { // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased // generics (because the compiler cannot ensure immutability for unknown types). - fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { + fn check_ty_(&mut self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { let ty = ty.peel_refs(); if let ty::Adt(def, args) = ty.kind() { let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] @@ -162,11 +139,7 @@ impl MutableKeyType { } let subst_ty = args.type_at(0); - // Determines if a type contains interior mutability which would affect its implementation of - // [`Hash`] or [`Ord`]. - if is_interior_mut_ty(cx, subst_ty) - && !matches!(subst_ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did())) - { + if self.interior_mut.is_interior_mut_ty(cx, subst_ty) { span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); } } diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 84a07df1bb0ac..630018238f4c4 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -1,6 +1,6 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Closure, Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::{sym, Span, Symbol}; @@ -35,6 +35,16 @@ declare_clippy_lint! { /// println!("{}", elem); /// } /// ``` + /// + /// ### Known Problems + /// When doing things such as: + /// ```ignore + /// let v = vec![0, 1, 2]; + /// v.iter().for_each(|elem| unsafe { + /// libc::printf(c"%d\n".as_ptr(), elem); + /// }); + /// ``` + /// This lint will not trigger. #[clippy::version = "1.53.0"] pub NEEDLESS_FOR_EACH, pedantic, @@ -68,7 +78,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind && let body = cx.tcx.hir().body(body) - && let ExprKind::Block(..) = body.value.kind + // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}` + // and suggesting `for … in … { unsafe { } }` is a little ugly. + && let ExprKind::Block(Block { rules: BlockCheckMode::DefaultBlock, .. }, ..) = body.value.kind { let mut ret_collector = RetCollector::default(); ret_collector.visit_expr(body.value); diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 0c0b1a73351f5..6605d1fa51a36 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -6,8 +6,7 @@ use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_loca use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{ - BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, - StmtKind, + BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 30d3e86dc4ed7..9e47c3ad0b7fb 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -83,7 +83,9 @@ fn should_skip<'tcx>( } if is_self(arg) { - return true; + // Interestingly enough, `self` arguments make `is_from_proc_macro` return `true`, hence why + // we return early here. + return false; } if let PatKind::Binding(.., name, _) = arg.pat.kind { @@ -185,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { } // Collect variables mutably used and spans which will need dereferencings from the // function body. - let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = { + let mutably_used_vars = { let mut ctx = MutablyUsedVariablesCtxt { mutably_used_vars: HirIdSet::default(), prev_bind: None, @@ -217,7 +219,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { check_closures(&mut ctx, cx, &infcx, &mut checked_closures, async_closures); } } - ctx + ctx.generate_mutably_used_ids_from_aliases() }; for ((&input, &_), arg) in it { // Only take `&mut` arguments. @@ -309,12 +311,22 @@ struct MutablyUsedVariablesCtxt<'tcx> { } impl<'tcx> MutablyUsedVariablesCtxt<'tcx> { - fn add_mutably_used_var(&mut self, mut used_id: HirId) { - while let Some(id) = self.aliases.get(&used_id) { + fn add_mutably_used_var(&mut self, used_id: HirId) { + self.mutably_used_vars.insert(used_id); + } + + // Because the alias may come after the mutable use of a variable, we need to fill the map at + // the end. + fn generate_mutably_used_ids_from_aliases(mut self) -> HirIdSet { + let all_ids = self.mutably_used_vars.iter().copied().collect::>(); + for mut used_id in all_ids { + while let Some(id) = self.aliases.get(&used_id) { + self.mutably_used_vars.insert(used_id); + used_id = *id; + } self.mutably_used_vars.insert(used_id); - used_id = *id; } - self.mutably_used_vars.insert(used_id); + self.mutably_used_vars } fn would_be_alias_cycle(&self, alias: HirId, mut target: HirId) -> bool { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 53bcde680876e..39d374d0d27fd 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -9,8 +9,8 @@ use rustc_ast::ast::Attribute; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind, - QPath, TyKind, + BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind, QPath, + TyKind, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index fd3985a5dafcf..932d6fe54d660 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -182,17 +182,17 @@ impl LateLintPass<'_> for NonCanonicalImpls { if block.stmts.is_empty() && let Some(expr) = block.expr - && let ExprKind::Call( - Expr { - kind: ExprKind::Path(some_path), - hir_id: some_hir_id, - .. - }, - [cmp_expr], - ) = expr.kind - && is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) - // Fix #11178, allow `Self::cmp(self, ..)` too - && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, &mut needs_fully_qualified) + && expr_is_cmp(cx, &expr.kind, impl_item, &mut needs_fully_qualified) + { + } + // Fix #12683, allow [`needless_return`] here + else if block.expr.is_none() + && let Some(stmt) = block.stmts.first() + && let rustc_hir::StmtKind::Semi(Expr { + kind: ExprKind::Ret(Some(Expr { kind: ret_kind, .. })), + .. + }) = stmt.kind + && expr_is_cmp(cx, ret_kind, impl_item, &mut needs_fully_qualified) { } else { // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid @@ -245,6 +245,30 @@ impl LateLintPass<'_> for NonCanonicalImpls { } } +/// Return true if `expr_kind` is a `cmp` call. +fn expr_is_cmp<'tcx>( + cx: &LateContext<'tcx>, + expr_kind: &'tcx ExprKind<'tcx>, + impl_item: &ImplItem<'_>, + needs_fully_qualified: &mut bool, +) -> bool { + if let ExprKind::Call( + Expr { + kind: ExprKind::Path(some_path), + hir_id: some_hir_id, + .. + }, + [cmp_expr], + ) = expr_kind + { + is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + // Fix #11178, allow `Self::cmp(self, ..)` too + && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, needs_fully_qualified) + } else { + false + } +} + /// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`. fn self_cmp_call<'tcx>( cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index ff10a841aef1a..76d9cee18aa7f 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -5,9 +5,9 @@ use std::ptr; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::in_constant; use clippy_utils::macros::macro_backtrace; -use clippy_utils::{def_path_def_ids, in_constant}; -use rustc_data_structures::fx::FxHashSet; +use clippy_utils::ty::InteriorMut; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ @@ -52,8 +52,8 @@ declare_clippy_lint! { /// There're other enums plus associated constants cases that the lint cannot handle. /// /// Types that have underlying or potential interior mutability trigger the lint whether - /// the interior mutable field is used or not. See issues - /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and + /// the interior mutable field is used or not. See issue + /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) /// /// ### Example /// ```no_run @@ -170,42 +170,22 @@ fn lint(cx: &LateContext<'_>, source: Source) { }); } -#[derive(Clone)] -pub struct NonCopyConst { +pub struct NonCopyConst<'tcx> { ignore_interior_mutability: Vec, - ignore_mut_def_ids: FxHashSet, + interior_mut: InteriorMut<'tcx>, } -impl_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); +impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); -impl NonCopyConst { +impl<'tcx> NonCopyConst<'tcx> { pub fn new(ignore_interior_mutability: Vec) -> Self { Self { ignore_interior_mutability, - ignore_mut_def_ids: FxHashSet::default(), + interior_mut: InteriorMut::default(), } } - fn is_ty_ignored(&self, ty: Ty<'_>) -> bool { - matches!(ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did())) - } - - fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, - // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is - // 'unfrozen'. However, this code causes a false negative in which - // a type contains a layout-unknown type, but also an unsafe cell like `const CELL: Cell`. - // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` - // since it works when a pointer indirection involves (`Cell<*const T>`). - // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; - // but I'm not sure whether it's a decent way, if possible. - cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env) - } - - fn is_value_unfrozen_raw_inner<'tcx>(&self, cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { - if self.is_ty_ignored(ty) { - return false; - } + fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { match *ty.kind() { // the fact that we have to dig into every structs to search enums // leads us to the point checking `UnsafeCell` directly is the only option. @@ -216,8 +196,7 @@ impl NonCopyConst { ty::Array(ty, _) => val .unwrap_branch() .iter() - .any(|field| self.is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Adt(def, _) if def.is_union() => false, + .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Adt(def, args) if def.is_enum() => { let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap()); @@ -230,24 +209,23 @@ impl NonCopyConst { .iter() .map(|field| field.ty(cx.tcx, args)), ) - .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, field, ty)) + .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty)) }, ty::Adt(def, args) => val .unwrap_branch() .iter() .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) - .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)), + .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Tuple(tys) => val .unwrap_branch() .iter() .zip(tys) - .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)), + .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), _ => false, } } - fn is_value_unfrozen_raw<'tcx>( - &self, + fn is_value_unfrozen_raw( cx: &LateContext<'tcx>, result: Result>, ErrorHandled>, ty: Ty<'tcx>, @@ -277,11 +255,11 @@ impl NonCopyConst { // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). matches!(err, ErrorHandled::TooGeneric(..)) }, - |val| val.map_or(true, |val| self.is_value_unfrozen_raw_inner(cx, val, ty)), + |val| val.map_or(true, |val| Self::is_value_unfrozen_raw_inner(cx, val, ty)), ) } - fn is_value_unfrozen_poly<'tcx>(&self, cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { + fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { let def_id = body_id.hir_id.owner.to_def_id(); let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); let instance = ty::Instance::new(def_id, args); @@ -291,17 +269,17 @@ impl NonCopyConst { }; let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, DUMMY_SP); - self.is_value_unfrozen_raw(cx, result, ty) + Self::is_value_unfrozen_raw(cx, result, ty) } - fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { + fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { let args = cx.typeck_results().node_args(hir_id); let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP); - self.is_value_unfrozen_raw(cx, result, ty) + Self::is_value_unfrozen_raw(cx, result, ty) } - pub fn const_eval_resolve<'tcx>( + pub fn const_eval_resolve( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ct: ty::UnevaluatedConst<'tcx>, @@ -321,26 +299,17 @@ impl NonCopyConst { } } -impl<'tcx> LateLintPass<'tcx> for NonCopyConst { +impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { fn check_crate(&mut self, cx: &LateContext<'tcx>) { - self.ignore_mut_def_ids.clear(); - let mut path = Vec::new(); - for ty in &self.ignore_interior_mutability { - path.extend(ty.split("::")); - for id in def_path_def_ids(cx, &path[..]) { - self.ignore_mut_def_ids.insert(id); - } - path.clear(); - } + self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability); } fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { if let ItemKind::Const(.., body_id) = it.kind { let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); if !ignored_macro(cx, it) - && !self.is_ty_ignored(ty) - && Self::is_unfrozen(cx, ty) - && self.is_value_unfrozen_poly(cx, body_id, ty) + && self.interior_mut.is_interior_mut_ty(cx, ty) + && Self::is_value_unfrozen_poly(cx, body_id, ty) { lint(cx, Source::Item { item: it.span }); } @@ -354,7 +323,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - if !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized) + if self.interior_mut.is_interior_mut_ty(cx, normalized) // When there's no default value, lint it only according to its type; // in other words, lint consts whose value *could* be unfrozen, not definitely is. // This feels inconsistent with how the lint treats generic types, @@ -367,7 +336,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // i.e. having an enum doesn't necessary mean a type has a frozen variant. // And, implementing it isn't a trivial task; it'll probably end up // re-implementing the trait predicate evaluation specific to `Freeze`. - && body_id_opt.map_or(true, |body_id| self.is_value_unfrozen_poly(cx, body_id, normalized)) + && body_id_opt.map_or(true, |body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized)) { lint(cx, Source::Assoc { item: trait_item.span }); } @@ -409,8 +378,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity() && let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty) - && !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized) - && self.is_value_unfrozen_poly(cx, *body_id, normalized) + && self.interior_mut.is_interior_mut_ty(cx, normalized) + && Self::is_value_unfrozen_poly(cx, *body_id, normalized) { lint(cx, Source::Assoc { item: impl_item.span }); } @@ -420,9 +389,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // Normalize assoc types originated from generic params. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - if !self.is_ty_ignored(ty) - && Self::is_unfrozen(cx, ty) - && self.is_value_unfrozen_poly(cx, *body_id, normalized) + if self.interior_mut.is_interior_mut_ty(cx, normalized) + && Self::is_value_unfrozen_poly(cx, *body_id, normalized) { lint(cx, Source::Assoc { item: impl_item.span }); } @@ -517,9 +485,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { cx.typeck_results().expr_ty(dereferenced_expr) }; - if !self.is_ty_ignored(ty) - && Self::is_unfrozen(cx, ty) - && self.is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) + if self.interior_mut.is_interior_mut_ty(cx, ty) + && Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) { lint(cx, Source::Expr { expr: expr.span }); } diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 96ea063aa74d6..7d6f26cde3e93 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -7,14 +7,13 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]]; const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; -const INTEGER_METHODS: &[Symbol] = &[ +const DISALLOWED_INT_METHODS: &[Symbol] = &[ sym::saturating_div, sym::wrapping_div, sym::wrapping_rem, @@ -27,8 +26,8 @@ pub struct ArithmeticSideEffects { allowed_unary: FxHashSet, // Used to check whether expressions are constants, such as in enum discriminants and consts const_span: Option, + disallowed_int_methods: FxHashSet, expr_span: Option, - integer_methods: FxHashSet, } impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]); @@ -53,8 +52,8 @@ impl ArithmeticSideEffects { allowed_binary, allowed_unary, const_span: None, + disallowed_int_methods: DISALLOWED_INT_METHODS.iter().copied().collect(), expr_span: None, - integer_methods: INTEGER_METHODS.iter().copied().collect(), } } @@ -91,10 +90,10 @@ impl ArithmeticSideEffects { fn has_specific_allowed_type_and_operation<'tcx>( cx: &LateContext<'tcx>, lhs_ty: Ty<'tcx>, - op: &Spanned, + op: hir::BinOpKind, rhs_ty: Ty<'tcx>, ) -> bool { - let is_div_or_rem = matches!(op.node, hir::BinOpKind::Div | hir::BinOpKind::Rem); + let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem); let is_non_zero_u = |cx: &LateContext<'tcx>, ty: Ty<'tcx>| { let tcx = cx.tcx; @@ -166,13 +165,35 @@ impl ArithmeticSideEffects { None } + /// Methods like `add_assign` are send to their `BinOps` references. + fn manage_sugar_methods<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + lhs: &'tcx hir::Expr<'_>, + ps: &hir::PathSegment<'_>, + rhs: &'tcx hir::Expr<'_>, + ) { + if ps.ident.name == sym::add || ps.ident.name == sym::add_assign { + self.manage_bin_ops(cx, expr, hir::BinOpKind::Add, lhs, rhs); + } else if ps.ident.name == sym::div || ps.ident.name == sym::div_assign { + self.manage_bin_ops(cx, expr, hir::BinOpKind::Div, lhs, rhs); + } else if ps.ident.name == sym::mul || ps.ident.name == sym::mul_assign { + self.manage_bin_ops(cx, expr, hir::BinOpKind::Mul, lhs, rhs); + } else if ps.ident.name == sym::rem || ps.ident.name == sym::rem_assign { + self.manage_bin_ops(cx, expr, hir::BinOpKind::Rem, lhs, rhs); + } else if ps.ident.name == sym::sub || ps.ident.name == sym::sub_assign { + self.manage_bin_ops(cx, expr, hir::BinOpKind::Sub, lhs, rhs); + } + } + /// Manages when the lint should be triggered. Operations in constant environments, hard coded - /// types, custom allowed types and non-constant operations that won't overflow are ignored. + /// types, custom allowed types and non-constant operations that don't overflow are ignored. fn manage_bin_ops<'tcx>( &mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, - op: &Spanned, + op: hir::BinOpKind, lhs: &'tcx hir::Expr<'_>, rhs: &'tcx hir::Expr<'_>, ) { @@ -180,7 +201,7 @@ impl ArithmeticSideEffects { return; } if !matches!( - op.node, + op, hir::BinOpKind::Add | hir::BinOpKind::Div | hir::BinOpKind::Mul @@ -204,7 +225,7 @@ impl ArithmeticSideEffects { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { - if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node { + if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op { // At least for integers, shifts are already handled by the CTFE return; } @@ -213,7 +234,7 @@ impl ArithmeticSideEffects { Self::literal_integer(cx, actual_rhs), ) { (None, None) => false, - (None, Some(n)) => match (&op.node, n) { + (None, Some(n)) => match (&op, n) { // Division and module are always valid if applied to non-zero integers (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true, // Adding or subtracting zeros is always a no-op @@ -223,7 +244,7 @@ impl ArithmeticSideEffects { => true, _ => false, }, - (Some(n), None) => match (&op.node, n) { + (Some(n), None) => match (&op, n) { // Adding or subtracting zeros is always a no-op (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) // Multiplication by 1 or 0 will never overflow @@ -249,6 +270,7 @@ impl ArithmeticSideEffects { &mut self, args: &'tcx [hir::Expr<'_>], cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, ps: &'tcx hir::PathSegment<'_>, receiver: &'tcx hir::Expr<'_>, ) { @@ -262,7 +284,8 @@ impl ArithmeticSideEffects { if !Self::is_integral(instance_ty) { return; } - if !self.integer_methods.contains(&ps.ident.name) { + self.manage_sugar_methods(cx, expr, receiver, ps, arg); + if !self.disallowed_int_methods.contains(&ps.ident.name) { return; } let (actual_arg, _) = peel_hir_expr_refs(arg); @@ -310,10 +333,10 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { } match &expr.kind { hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { - self.manage_bin_ops(cx, expr, op, lhs, rhs); + self.manage_bin_ops(cx, expr, op.node, lhs, rhs); }, hir::ExprKind::MethodCall(ps, receiver, args, _) => { - self.manage_method_call(args, cx, ps, receiver); + self.manage_method_call(args, cx, expr, ps, receiver); }, hir::ExprKind::Unary(un_op, un_expr) => { self.manage_unary_ops(cx, expr, un_expr, *un_op); diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index cc61ef9184cdc..2534e3c8468ce 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -11,9 +11,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::{HirId, HirIdMap}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{ - self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, - ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, - TyKind, Unsafety, + self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind, + ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; @@ -687,9 +686,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: .filter_map(|(i, arg)| { let param = &body.params[arg.idx]; match param.pat.kind { - PatKind::Binding(BindingMode::NONE, id, _, None) - if !is_lint_allowed(cx, PTR_ARG, param.hir_id) => - { + PatKind::Binding(BindingMode::NONE, id, _, None) if !is_lint_allowed(cx, PTR_ARG, param.hir_id) => { Some((id, i)) }, _ => { diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 4ad967589a543..1f1ce147ca241 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -14,8 +14,8 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ - BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, - Stmt, StmtKind, + BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt, + StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; diff --git a/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs index a358881bf80ee..792d8fc88f0bb 100644 --- a/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/clippy_lints/src/repeat_vec_with_capacity.rs @@ -1,7 +1,7 @@ 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::macros::matching_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; @@ -65,8 +65,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, kind: &str, note: &'static str, s /// 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) + if matching_root_macro_call(cx, expr.span, sym::vec_macro).is_some() && 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() diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 14ca7a3f0042e..8d7f12af86e8a 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -28,7 +28,7 @@ declare_clippy_lint! { /// fn size(&self) -> usize { /// // Note that `&self` as an argument is a `&&Foo`: Because `self` /// // is already a reference, `&self` is a double-reference. - /// // The return value of `size_of_val()` therefor is the + /// // The return value of `size_of_val()` therefore is the /// // size of the reference-type, not the size of `self`. /// std::mem::size_of_val(&self) /// } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 28c254537abdd..b0e25c02265b7 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::root_macro_call; +use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, @@ -145,9 +145,7 @@ impl SlowVectorInit { // Generally don't warn if the vec initializer comes from an expansion, except for the vec! macro. // This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an // empty vec - if expr.span.from_expansion() - && root_macro_call(expr.span).map(|m| m.def_id) != cx.tcx.get_diagnostic_item(sym::vec_macro) - { + if expr.span.from_expansion() && matching_root_macro_call(cx, expr.span, sym::vec_macro).is_none() { return None; } diff --git a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs b/clippy_lints/src/thread_local_initializer_can_be_made_const.rs index c1e24674e3e8c..4af3ee74d0eaf 100644 --- a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs +++ b/clippy_lints/src/thread_local_initializer_can_be_made_const.rs @@ -1,13 +1,14 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::macros::macro_backtrace; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::source::snippet; use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{intravisit, ExprKind}; +use rustc_hir::{intravisit, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym::thread_local_macro; +use rustc_span::sym::{self, thread_local_macro}; declare_clippy_lint! { /// ### What it does @@ -69,6 +70,26 @@ fn is_thread_local_initializer( ) } +fn is_unreachable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(macro_call) = macro_backtrace(expr.span).next() + && let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + return (matches!( + diag_name, + sym::core_panic_macro + | sym::std_panic_macro + | sym::core_panic_2015_macro + | sym::std_panic_2015_macro + | sym::core_panic_2021_macro + ) && !cx.tcx.hir().is_inside_const_context(expr.hir_id)) + || matches!( + diag_name, + sym::unimplemented_macro | sym::todo_macro | sym::unreachable_macro | sym::unreachable_2015_macro + ); + } + false +} + #[inline] fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id::DefId, msrv: &Msrv) -> bool { // Building MIR for `fn`s with unsatisfiable preds results in ICE. @@ -102,12 +123,17 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst { // for details on this issue, see: // https://github.com/rust-lang/rust-clippy/pull/12276 && !cx.tcx.is_const_fn(defid) - && initializer_can_be_made_const(cx, defid, &self.msrv) - // we know that the function is const-qualifiable, so now - // we need only to get the initializer expression to span-lint it. && let ExprKind::Block(block, _) = body.value.kind && let Some(unpeeled) = block.expr && let ret_expr = peel_blocks(unpeeled) + // A common pattern around threadlocal! is to make the value unreachable + // to force an initialization before usage + // https://github.com/rust-lang/rust-clippy/issues/12637 + // we ensure that this is reachable before we check in mir + && !is_unreachable(cx, ret_expr) + && initializer_can_be_made_const(cx, defid, &self.msrv) + // we know that the function is const-qualifiable, so now + // we need only to get the initializer expression to span-lint it. && let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }") && initializer_snippet != "thread_local! { ... }" { diff --git a/clippy_lints/src/to_string_trait_impl.rs b/clippy_lints/src/to_string_trait_impl.rs index 59ae185c9de74..0361836cdec79 100644 --- a/clippy_lints/src/to_string_trait_impl.rs +++ b/clippy_lints/src/to_string_trait_impl.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.77.0"] + #[clippy::version = "1.78.0"] pub TO_STRING_TRAIT_IMPL, style, "check for direct implementations of `ToString`" diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 9468d367a9261..c05cd9ed59345 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -237,6 +237,7 @@ impl TraitBounds { } } + #[allow(clippy::mutable_key_type)] fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { struct SpanlessTy<'cx, 'tcx> { ty: &'tcx Ty<'tcx>, diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 0802cb2b7c75d..5e45ab211efd8 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -12,8 +12,8 @@ mod vec_box; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitItem, - TraitItemKind, TyKind, + Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitFn, + TraitItem, TraitItemKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -420,7 +420,13 @@ impl<'tcx> LateLintPass<'tcx> for Types { TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => { self.check_ty(cx, ty, context); }, - TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context), + TraitItemKind::Fn(ref sig, trait_method) => { + // Check only methods without body + // Methods with body are covered by check_fn. + if let TraitFn::Required(_) = trait_method { + self.check_fn_decl(cx, sig.decl, context); + } + }, TraitItemKind::Type(..) => (), } } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 7f0769452c73e..4448c9ae3df79 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -7,8 +7,8 @@ use rustc_ast::LitIntType; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::{ - ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, - PatKind, QPath, StmtKind, TyKind, + ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, PatKind, + QPath, StmtKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 27ead55bf39cc..9edf7579d4827 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -7,7 +7,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; 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 clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -22,6 +22,7 @@ pub struct UselessVec { pub too_large_for_stack: u64, pub msrv: Msrv, pub span_to_lint_map: BTreeMap>, + pub allow_in_test: bool, } declare_clippy_lint! { @@ -57,6 +58,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else { return; }; + if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { + return; + }; // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!` let callsite = expr.span.parent_callsite().unwrap_or(expr.span); diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index c46f0298cc8b7..a599415a2dd57 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -6,9 +6,7 @@ use clippy_utils::{get_parent_expr, path_to_local_id}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{ - BindingMode, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp, -}; +use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index d2bb719a517f5..ab883c25338ba 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.79" +version = "0.1.80" edition = "2021" publish = false diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5e242aea354a3..a49414a058b1c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2328,10 +2328,10 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)> +pub fn search_same(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)> where - Hash: Fn(&T) -> u64, - Eq: Fn(&T, &T) -> bool, + Hash: FnMut(&T) -> u64, + Eq: FnMut(&T, &T) -> bool, { match exprs { [a, b] if eq(a, b) => return vec![(a, b)], @@ -2505,8 +2505,9 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Sym /// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { with_test_item_names(tcx, tcx.parent_module(id), |names| { - tcx.hir() - .parent_iter(id) + let node = tcx.hir_node(id); + once((id, node)) + .chain(tcx.hir().parent_iter(id)) // Since you can nest functions we need to collect all until we leave // function scope .any(|(_id, node)| { @@ -2547,6 +2548,11 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool { .any(|parent_id| is_cfg_test(tcx, parent_id)) } +/// Checks if the node is in a `#[test]` function or has any parent node marked `#[cfg(test)]` +pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { + is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id) +} + /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let hir = tcx.hir(); diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index f166087dc3caa..257dd76ab15cf 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -119,10 +119,20 @@ pub fn macro_backtrace(span: Span) -> impl Iterator { /// If the macro backtrace of `span` has a macro call at the root expansion /// (i.e. not a nested macro call), returns `Some` with the `MacroCall` +/// +/// If you only want to check whether the root macro has a specific name, +/// consider using [`matching_root_macro_call`] instead. pub fn root_macro_call(span: Span) -> Option { macro_backtrace(span).last() } +/// A combination of [`root_macro_call`] and +/// [`is_diagnostic_item`](rustc_middle::ty::TyCtxt::is_diagnostic_item) that returns a `MacroCall` +/// at the root expansion if only it matches the given name. +pub fn matching_root_macro_call(cx: &LateContext<'_>, span: Span, name: Symbol) -> Option { + root_macro_call(span).filter(|mc| cx.tcx.is_diagnostic_item(name, mc.def_id)) +} + /// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node" /// produced by the macro call, as in [`first_node_in_macro`]. pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option { diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 456b8019e95cc..9bf068ee3cdee 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -19,8 +19,6 @@ pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "B pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; -pub const CORE_ITER_ENUMERATE_METHOD: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "enumerate"]; -pub const CORE_ITER_ENUMERATE_STRUCT: [&str; 5] = ["core", "iter", "adapters", "enumerate", "Enumerate"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; @@ -73,7 +71,6 @@ pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"]; pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; -pub const SLICE_GET: [&str; 4] = ["core", "slice", "", "get"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; pub const SLICE_INTO: [&str; 4] = ["core", "slice", "", "iter"]; pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"]; @@ -81,7 +78,6 @@ pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; -pub const STR_BYTES: [&str; 4] = ["core", "str", "", "bytes"]; pub const STR_CHARS: [&str; 4] = ["core", "str", "", "chars"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; @@ -108,7 +104,6 @@ pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "Vec pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"]; -pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"]; pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"]; pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"]; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a06a82c565300..23750ed4d1ba0 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -29,9 +29,10 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _ use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::assert_matches::debug_assert_matches; +use std::collections::hash_map::Entry; use std::iter; -use crate::{match_def_path, path_res}; +use crate::{def_path_def_ids, match_def_path, path_res}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -1198,47 +1199,88 @@ pub fn make_normalized_projection<'tcx>( helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?) } -/// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`] -/// etc. -pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match *ty.kind() { - ty::Ref(_, inner_ty, mutbl) => mutbl == Mutability::Mut || is_interior_mut_ty(cx, inner_ty), - ty::Slice(inner_ty) => is_interior_mut_ty(cx, inner_ty), - ty::Array(inner_ty, size) => { - size.try_eval_target_usize(cx.tcx, cx.param_env) - .map_or(true, |u| u != 0) - && is_interior_mut_ty(cx, inner_ty) - }, - ty::Tuple(fields) => fields.iter().any(|ty| is_interior_mut_ty(cx, ty)), - ty::Adt(def, args) => { - // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to - // that of their type parameters. Note: we don't include `HashSet` and `HashMap` - // because they have no impl for `Hash` or `Ord`. - let def_id = def.did(); - let is_std_collection = [ - sym::Option, - sym::Result, - sym::LinkedList, - sym::Vec, - sym::VecDeque, - sym::BTreeMap, - sym::BTreeSet, - sym::Rc, - sym::Arc, - ] +/// Helper to check if given type has inner mutability such as [`std::cell::Cell`] or +/// [`std::cell::RefCell`]. +#[derive(Default, Debug)] +pub struct InteriorMut<'tcx> { + ignored_def_ids: FxHashSet, + ignore_pointers: bool, + tys: FxHashMap, Option>, +} + +impl<'tcx> InteriorMut<'tcx> { + pub fn new(cx: &LateContext<'tcx>, ignore_interior_mutability: &[String]) -> Self { + let ignored_def_ids = ignore_interior_mutability .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id)); - let is_box = Some(def_id) == cx.tcx.lang_items().owned_box(); - if is_std_collection || is_box { - // The type is mutable if any of its type parameters are - args.types().any(|ty| is_interior_mut_ty(cx, ty)) - } else { - !ty.has_escaping_bound_vars() - && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() - && !ty.is_freeze(cx.tcx, cx.param_env) - } - }, - _ => false, + .flat_map(|ignored_ty| { + let path: Vec<&str> = ignored_ty.split("::").collect(); + def_path_def_ids(cx, path.as_slice()) + }) + .collect(); + + Self { + ignored_def_ids, + ..Self::default() + } + } + + pub fn without_pointers(cx: &LateContext<'tcx>, ignore_interior_mutability: &[String]) -> Self { + Self { + ignore_pointers: true, + ..Self::new(cx, ignore_interior_mutability) + } + } + + /// Check if given type has inner mutability such as [`std::cell::Cell`] or + /// [`std::cell::RefCell`] etc. + pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match self.tys.entry(ty) { + Entry::Occupied(o) => return *o.get() == Some(true), + // Temporarily insert a `None` to break cycles + Entry::Vacant(v) => v.insert(None), + }; + + let interior_mut = match *ty.kind() { + ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.is_interior_mut_ty(cx, inner_ty), + ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.is_interior_mut_ty(cx, inner_ty), + ty::Array(inner_ty, size) => { + size.try_eval_target_usize(cx.tcx, cx.param_env) + .map_or(true, |u| u != 0) + && self.is_interior_mut_ty(cx, inner_ty) + }, + ty::Tuple(fields) => fields.iter().any(|ty| self.is_interior_mut_ty(cx, ty)), + ty::Adt(def, _) if def.is_unsafe_cell() => true, + ty::Adt(def, args) => { + let is_std_collection = matches!( + cx.tcx.get_diagnostic_name(def.did()), + Some( + sym::LinkedList + | sym::Vec + | sym::VecDeque + | sym::BTreeMap + | sym::BTreeSet + | sym::HashMap + | sym::HashSet + | sym::Arc + | sym::Rc + ) + ); + + if is_std_collection || def.is_box() { + // Include the types from std collections that are behind pointers internally + args.types().any(|ty| self.is_interior_mut_ty(cx, ty)) + } else if self.ignored_def_ids.contains(&def.did()) || def.is_phantom_data() { + false + } else { + def.all_fields() + .any(|f| self.is_interior_mut_ty(cx, f.ty(cx.tcx, args))) + } + }, + _ => false, + }; + + self.tys.insert(ty, Some(interior_mut)); + interior_mut } } diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 9a3a41e1d1ead..c8c734c3a7c9f 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.79" +version = "0.1.80" edition = "2021" publish = false diff --git a/rust-toolchain b/rust-toolchain index 521c0d12983da..055f305eb8e18 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-04-18" +channel = "nightly-2024-05-02" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 32a31f5e08236..b06a11702ec86 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -4,6 +4,7 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] +use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling}; @@ -122,10 +123,11 @@ fn base_config(test_dir: &str) -> (Config, Args) { out_dir: target_dir.join("ui_test"), ..Config::rustc(Path::new("tests").join(test_dir)) }; - config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo { - rustfix: ui_test::RustfixMode::Everything, - })) - .into(); + config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo)).into(); + config + .comment_defaults + .base() + .set_custom("rustfix", RustfixMode::Everything); config.comment_defaults.base().diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into(); config.with_args(&args); let current_exe_path = env::current_exe().unwrap(); @@ -235,13 +237,12 @@ fn run_ui_cargo() { .push(("RUSTFLAGS".into(), Some("-Dwarnings".into()))); // We need to do this while we still have a rustc in the `program` field. config.fill_host_and_target().unwrap(); - config.dependencies_crate_manifest_path = None; config.program.program.set_file_name(if cfg!(windows) { "cargo-clippy.exe" } else { "cargo-clippy" }); - config.comment_defaults.base().edition = Default::default(); + config.comment_defaults.base().custom.clear(); config .comment_defaults diff --git a/tests/ui-toml/mut_key/mut_key.rs b/tests/ui-toml/mut_key/mut_key.rs index 095e0d15448a7..3a8e3741e20c2 100644 --- a/tests/ui-toml/mut_key/mut_key.rs +++ b/tests/ui-toml/mut_key/mut_key.rs @@ -44,10 +44,18 @@ impl Deref for Counted { } } +#[derive(Hash, PartialEq, Eq)] +struct ContainsCounted { + inner: Counted, +} + // This is not linted because `"mut_key::Counted"` is in // `arc_like_types` in `clippy.toml` fn should_not_take_this_arg(_v: HashSet>) {} +fn indirect(_: HashMap) {} + fn main() { should_not_take_this_arg(HashSet::new()); + indirect(HashMap::new()); } diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 24645b61fdb07..722e9b3bc8d46 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -11,6 +11,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-print-in-tests allow-private-module-inception allow-unwrap-in-tests + allow-useless-vec-in-tests allowed-dotfiles allowed-duplicate-crates allowed-idents-below-min-chars @@ -91,6 +92,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-print-in-tests allow-private-module-inception allow-unwrap-in-tests + allow-useless-vec-in-tests allowed-dotfiles allowed-duplicate-crates allowed-idents-below-min-chars @@ -171,6 +173,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allow-print-in-tests allow-private-module-inception allow-unwrap-in-tests + allow-useless-vec-in-tests allowed-dotfiles allowed-duplicate-crates allowed-idents-below-min-chars diff --git a/tests/ui-toml/useless_vec/clippy.toml b/tests/ui-toml/useless_vec/clippy.toml new file mode 100644 index 0000000000000..230ca2d0ab72b --- /dev/null +++ b/tests/ui-toml/useless_vec/clippy.toml @@ -0,0 +1 @@ +allow-useless-vec-in-tests = true diff --git a/tests/ui-toml/useless_vec/useless_vec.fixed b/tests/ui-toml/useless_vec/useless_vec.fixed new file mode 100644 index 0000000000000..08323a0dcc90f --- /dev/null +++ b/tests/ui-toml/useless_vec/useless_vec.fixed @@ -0,0 +1,26 @@ +//@compile-flags: --test +#![warn(clippy::useless_vec)] +#![allow(clippy::unnecessary_operation, clippy::no_effect)] + +fn foo(_: &[u32]) {} + +fn main() { + foo(&[1_u32]); +} + +#[test] +pub fn in_test() { + foo(&vec![2_u32]); +} + +#[cfg(test)] +fn in_cfg_test() { + foo(&vec![3_u32]); +} + +#[cfg(test)] +mod mod1 { + fn in_cfg_test_mod() { + super::foo(&vec![4_u32]); + } +} diff --git a/tests/ui-toml/useless_vec/useless_vec.rs b/tests/ui-toml/useless_vec/useless_vec.rs new file mode 100644 index 0000000000000..1f4b27c53429d --- /dev/null +++ b/tests/ui-toml/useless_vec/useless_vec.rs @@ -0,0 +1,26 @@ +//@compile-flags: --test +#![warn(clippy::useless_vec)] +#![allow(clippy::unnecessary_operation, clippy::no_effect)] + +fn foo(_: &[u32]) {} + +fn main() { + foo(&vec![1_u32]); +} + +#[test] +pub fn in_test() { + foo(&vec![2_u32]); +} + +#[cfg(test)] +fn in_cfg_test() { + foo(&vec![3_u32]); +} + +#[cfg(test)] +mod mod1 { + fn in_cfg_test_mod() { + super::foo(&vec![4_u32]); + } +} diff --git a/tests/ui-toml/useless_vec/useless_vec.stderr b/tests/ui-toml/useless_vec/useless_vec.stderr new file mode 100644 index 0000000000000..633110c3c8d92 --- /dev/null +++ b/tests/ui-toml/useless_vec/useless_vec.stderr @@ -0,0 +1,11 @@ +error: useless use of `vec!` + --> tests/ui-toml/useless_vec/useless_vec.rs:8:9 + | +LL | foo(&vec![1_u32]); + | ^^^^^^^^^^^^ help: you can use a slice directly: `&[1_u32]` + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index fdec14a1528f7..66d71f337f2f6 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -521,4 +521,14 @@ pub fn issue_11393() { example_rem(x, maybe_zero); } +pub fn issue_12318() { + use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; + let mut one: i32 = 1; + one.add_assign(1); + one.div_assign(1); + one.mul_assign(1); + one.rem_assign(1); + one.sub_assign(1); +} + fn main() {} diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 741c892a52cfc..8039c0bfa2484 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -715,5 +715,17 @@ error: arithmetic operation that can potentially result in unexpected side-effec LL | x % maybe_zero | ^^^^^^^^^^^^^^ -error: aborting due to 119 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:527:5 + | +LL | one.add_assign(1); + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:531:5 + | +LL | one.sub_assign(1); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 121 previous errors diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs index 3303eb1456785..6e6919cd295c4 100644 --- a/tests/ui/auxiliary/proc_macros.rs +++ b/tests/ui/auxiliary/proc_macros.rs @@ -9,6 +9,7 @@ use proc_macro::token_stream::IntoIter; use proc_macro::Delimiter::{self, Brace, Parenthesis}; use proc_macro::Spacing::{self, Alone, Joint}; use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT}; +use syn::spanned::Spanned; type Result = core::result::Result; @@ -124,6 +125,22 @@ fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Resul Ok(()) } +/// Takes an array repeat expression such as `[0_u32; 2]`, and return the tokens with 10 times the +/// original size, which turns to `[0_u32; 20]`. +#[proc_macro] +pub fn make_it_big(input: TokenStream) -> TokenStream { + let mut expr_repeat = syn::parse_macro_input!(input as syn::ExprRepeat); + let len_span = expr_repeat.len.span(); + if let syn::Expr::Lit(expr_lit) = &mut *expr_repeat.len { + if let syn::Lit::Int(lit_int) = &expr_lit.lit { + let orig_val = lit_int.base10_parse::().expect("not a valid length parameter"); + let new_val = orig_val.saturating_mul(10); + expr_lit.lit = syn::parse_quote_spanned!( len_span => #new_val); + } + } + quote::quote!(#expr_repeat).into() +} + /// Within the item this attribute is attached to, an `inline!` macro is available which expands the /// contained tokens as though they came from a macro expansion. /// diff --git a/tests/ui/borrow_interior_mutable_const/traits.rs b/tests/ui/borrow_interior_mutable_const/traits.rs index 4da3833cbf5a0..5570e7cd6d2ee 100644 --- a/tests/ui/borrow_interior_mutable_const/traits.rs +++ b/tests/ui/borrow_interior_mutable_const/traits.rs @@ -158,7 +158,7 @@ trait BothOfCellAndGeneric { const INDIRECT: Cell<*const T>; fn function() { - let _ = &Self::DIRECT; + let _ = &Self::DIRECT; //~ ERROR: interior mutability let _ = &Self::INDIRECT; //~ ERROR: interior mutability } } @@ -168,7 +168,7 @@ impl BothOfCellAndGeneric for Vec { const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); fn function() { - let _ = &Self::DIRECT; + let _ = &Self::DIRECT; //~ ERROR: interior mutability let _ = &Self::INDIRECT; //~ ERROR: interior mutability } } diff --git a/tests/ui/borrow_interior_mutable_const/traits.stderr b/tests/ui/borrow_interior_mutable_const/traits.stderr index 582b744b49ff8..8602b46b0dcfe 100644 --- a/tests/ui/borrow_interior_mutable_const/traits.stderr +++ b/tests/ui/borrow_interior_mutable_const/traits.stderr @@ -75,6 +75,14 @@ LL | let _ = &Self::WRAPPED_SELF; | = help: assign this const to a local or static variable, and use the variable here +error: a `const` item with interior mutability should not be borrowed + --> tests/ui/borrow_interior_mutable_const/traits.rs:161:18 + | +LL | let _ = &Self::DIRECT; + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + error: a `const` item with interior mutability should not be borrowed --> tests/ui/borrow_interior_mutable_const/traits.rs:162:18 | @@ -83,6 +91,14 @@ LL | let _ = &Self::INDIRECT; | = help: assign this const to a local or static variable, and use the variable here +error: a `const` item with interior mutability should not be borrowed + --> tests/ui/borrow_interior_mutable_const/traits.rs:171:18 + | +LL | let _ = &Self::DIRECT; + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + error: a `const` item with interior mutability should not be borrowed --> tests/ui/borrow_interior_mutable_const/traits.rs:172:18 | @@ -123,5 +139,5 @@ LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); | = help: assign this const to a local or static variable, and use the variable here -error: aborting due to 15 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index 6c2896b3aa0f2..1f2f57c2507db 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -65,6 +65,8 @@ fn main() { // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 let mut unnameable = Box::new(Option::default()); let _ = unnameable.insert(|| {}); + + let _ = Box::into_raw(Box::new(String::default())); } fn ret_ty_fn() -> Box { @@ -75,6 +77,16 @@ fn call_ty_fn(_b: Box) { issue_9621_dyn_trait(); } +struct X(T); + +impl X { + fn x(_: Box) {} + + fn same_generic_param() { + Self::x(Box::default()); + } +} + use std::io::{Read, Result}; impl Read for ImplementsDefault { diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index e19a62a90221e..addfebc24f58c 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -65,6 +65,8 @@ fn main() { // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 let mut unnameable = Box::new(Option::default()); let _ = unnameable.insert(|| {}); + + let _ = Box::into_raw(Box::new(String::default())); } fn ret_ty_fn() -> Box { @@ -75,6 +77,16 @@ fn call_ty_fn(_b: Box) { issue_9621_dyn_trait(); } +struct X(T); + +impl X { + fn x(_: Box) {} + + fn same_generic_param() { + Self::x(Box::new(T::default())); + } +} + use std::io::{Read, Result}; impl Read for ImplementsDefault { diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index f172a875dce47..39fd0d29bbf9a 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -55,5 +55,11 @@ error: `Box::new(_)` of default value LL | call_ty_fn(Box::new(u8::default())); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` -error: aborting due to 9 previous errors +error: `Box::new(_)` of default value + --> tests/ui/box_default.rs:86:17 + | +LL | Self::x(Box::new(T::default())); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: aborting due to 10 previous errors diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index 215c008902d20..453d62ce60753 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -13,7 +13,8 @@ clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::identity_op )] fn main() { @@ -479,3 +480,21 @@ fn issue12506() -> usize { let bar: Result, u32> = Ok(Some(10)); bar.unwrap().unwrap() as usize } + +fn issue12721() { + fn x() -> u64 { + u64::MAX + } + + // Don't lint. + (255 & 999999u64) as u8; + // Don't lint. + let _ = ((x() & 255) & 999999) as u8; + // Don't lint. + let _ = (999999 & (x() & 255)) as u8; + + (256 & 999999u64) as u8; + //~^ ERROR: casting `u64` to `u8` may truncate the value + (255 % 999999u64) as u8; + //~^ ERROR: casting `u64` to `u8` may truncate the value +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 8b269c471765c..43c0d8f4ed738 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -1,5 +1,5 @@ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:22:5 + --> tests/ui/cast.rs:23:5 | LL | x0 as f32; | ^^^^^^^^^ @@ -8,37 +8,37 @@ LL | x0 as f32; = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]` error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:26:5 + --> tests/ui/cast.rs:27:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast.rs:28:5 + --> tests/ui/cast.rs:29:5 | LL | x1 as f64; | ^^^^^^^^^ error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:31:5 + --> tests/ui/cast.rs:32:5 | LL | x2 as f32; | ^^^^^^^^^ error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:34:5 + --> tests/ui/cast.rs:35:5 | LL | x3 as f32; | ^^^^^^^^^ error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast.rs:36:5 + --> tests/ui/cast.rs:37:5 | LL | x3 as f64; | ^^^^^^^^^ error: casting `f32` to `i32` may truncate the value - --> tests/ui/cast.rs:39:5 + --> tests/ui/cast.rs:40:5 | LL | 1f32 as i32; | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | 1f32 as i32; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]` error: casting `f32` to `u32` may truncate the value - --> tests/ui/cast.rs:41:5 + --> tests/ui/cast.rs:42:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | 1f32 as u32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:41:5 + --> tests/ui/cast.rs:42:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | 1f32 as u32; = help: to override `-D warnings` add `#[allow(clippy::cast_sign_loss)]` error: casting `f64` to `f32` may truncate the value - --> tests/ui/cast.rs:45:5 + --> tests/ui/cast.rs:46:5 | LL | 1f64 as f32; | ^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | 1f64 as f32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `i32` to `i8` may truncate the value - --> tests/ui/cast.rs:47:5 + --> tests/ui/cast.rs:48:5 | LL | 1i32 as i8; | ^^^^^^^^^^ @@ -85,7 +85,7 @@ LL | i8::try_from(1i32); | ~~~~~~~~~~~~~~~~~~ error: casting `i32` to `u8` may truncate the value - --> tests/ui/cast.rs:49:5 + --> tests/ui/cast.rs:50:5 | LL | 1i32 as u8; | ^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | u8::try_from(1i32); | ~~~~~~~~~~~~~~~~~~ error: casting `f64` to `isize` may truncate the value - --> tests/ui/cast.rs:51:5 + --> tests/ui/cast.rs:52:5 | LL | 1f64 as isize; | ^^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | 1f64 as isize; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `usize` may truncate the value - --> tests/ui/cast.rs:53:5 + --> tests/ui/cast.rs:54:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ @@ -113,13 +113,13 @@ LL | 1f64 as usize; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:53:5 + --> tests/ui/cast.rs:54:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ error: casting `u32` to `u16` may truncate the value - --> tests/ui/cast.rs:56:5 + --> tests/ui/cast.rs:57:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^^^^^^^^ @@ -131,7 +131,7 @@ LL | u16::try_from(1f32 as u32); | ~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `f32` to `u32` may truncate the value - --> tests/ui/cast.rs:56:5 + --> tests/ui/cast.rs:57:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^ @@ -139,13 +139,13 @@ LL | 1f32 as u32 as u16; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:56:5 + --> tests/ui/cast.rs:57:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^ error: casting `i32` to `i8` may truncate the value - --> tests/ui/cast.rs:61:22 + --> tests/ui/cast.rs:62:22 | LL | let _x: i8 = 1i32 as _; | ^^^^^^^^^ @@ -157,7 +157,7 @@ LL | let _x: i8 = 1i32.try_into(); | ~~~~~~~~~~~~~~~ error: casting `f32` to `i32` may truncate the value - --> tests/ui/cast.rs:63:9 + --> tests/ui/cast.rs:64:9 | LL | 1f32 as i32; | ^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | 1f32 as i32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `i32` may truncate the value - --> tests/ui/cast.rs:65:9 + --> tests/ui/cast.rs:66:9 | LL | 1f64 as i32; | ^^^^^^^^^^^ @@ -173,7 +173,7 @@ LL | 1f64 as i32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u8` may truncate the value - --> tests/ui/cast.rs:67:9 + --> tests/ui/cast.rs:68:9 | LL | 1f32 as u8; | ^^^^^^^^^^ @@ -181,13 +181,13 @@ LL | 1f32 as u8; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u8` may lose the sign of the value - --> tests/ui/cast.rs:67:9 + --> tests/ui/cast.rs:68:9 | LL | 1f32 as u8; | ^^^^^^^^^^ error: casting `u8` to `i8` may wrap around the value - --> tests/ui/cast.rs:72:5 + --> tests/ui/cast.rs:73:5 | LL | 1u8 as i8; | ^^^^^^^^^ @@ -196,31 +196,31 @@ LL | 1u8 as i8; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `u16` to `i16` may wrap around the value - --> tests/ui/cast.rs:75:5 + --> tests/ui/cast.rs:76:5 | LL | 1u16 as i16; | ^^^^^^^^^^^ error: casting `u32` to `i32` may wrap around the value - --> tests/ui/cast.rs:77:5 + --> tests/ui/cast.rs:78:5 | LL | 1u32 as i32; | ^^^^^^^^^^^ error: casting `u64` to `i64` may wrap around the value - --> tests/ui/cast.rs:79:5 + --> tests/ui/cast.rs:80:5 | LL | 1u64 as i64; | ^^^^^^^^^^^ error: casting `usize` to `isize` may wrap around the value - --> tests/ui/cast.rs:81:5 + --> tests/ui/cast.rs:82:5 | LL | 1usize as isize; | ^^^^^^^^^^^^^^^ error: casting `usize` to `i8` may truncate the value - --> tests/ui/cast.rs:84:5 + --> tests/ui/cast.rs:85:5 | LL | 1usize as i8; | ^^^^^^^^^^^^ @@ -232,7 +232,7 @@ LL | i8::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i16` may truncate the value - --> tests/ui/cast.rs:87:5 + --> tests/ui/cast.rs:88:5 | LL | 1usize as i16; | ^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL | i16::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i16` may wrap around the value on targets with 16-bit wide pointers - --> tests/ui/cast.rs:87:5 + --> tests/ui/cast.rs:88:5 | LL | 1usize as i16; | ^^^^^^^^^^^^^ @@ -253,7 +253,7 @@ LL | 1usize as i16; = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:92:5 + --> tests/ui/cast.rs:93:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -265,19 +265,19 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:92:5 + --> tests/ui/cast.rs:93:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ error: casting `usize` to `i64` may wrap around the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:96:5 + --> tests/ui/cast.rs:97:5 | LL | 1usize as i64; | ^^^^^^^^^^^^^ error: casting `u16` to `isize` may wrap around the value on targets with 16-bit wide pointers - --> tests/ui/cast.rs:101:5 + --> tests/ui/cast.rs:102:5 | LL | 1u16 as isize; | ^^^^^^^^^^^^^ @@ -286,13 +286,13 @@ LL | 1u16 as isize; = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:105:5 + --> tests/ui/cast.rs:106:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:108:5 + --> tests/ui/cast.rs:109:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -304,55 +304,55 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:108:5 + --> tests/ui/cast.rs:109:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:113:5 + --> tests/ui/cast.rs:114:5 | LL | -1i32 as u32; | ^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:116:5 + --> tests/ui/cast.rs:117:5 | LL | -1isize as usize; | ^^^^^^^^^^^^^^^^ error: casting `i8` to `u8` may lose the sign of the value - --> tests/ui/cast.rs:127:5 + --> tests/ui/cast.rs:128:5 | LL | (i8::MIN).abs() as u8; | ^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:131:5 + --> tests/ui/cast.rs:132:5 | LL | (-1i64).abs() as u64; | ^^^^^^^^^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:132:5 + --> tests/ui/cast.rs:133:5 | LL | (-1isize).abs() as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:139:5 + --> tests/ui/cast.rs:140:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:154:5 + --> tests/ui/cast.rs:155:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `i8` may truncate the value - --> tests/ui/cast.rs:205:5 + --> tests/ui/cast.rs:206:5 | LL | (-99999999999i64).min(1) as i8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -364,7 +364,7 @@ LL | i8::try_from((-99999999999i64).min(1)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `u8` may truncate the value - --> tests/ui/cast.rs:219:5 + --> tests/ui/cast.rs:220:5 | LL | 999999u64.clamp(0, 256) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +376,7 @@ LL | u8::try_from(999999u64.clamp(0, 256)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E2` to `u8` may truncate the value - --> tests/ui/cast.rs:242:21 + --> tests/ui/cast.rs:243:21 | LL | let _ = self as u8; | ^^^^^^^^^^ @@ -388,7 +388,7 @@ LL | let _ = u8::try_from(self); | ~~~~~~~~~~~~~~~~~~ error: casting `main::E2::B` to `u8` will truncate the value - --> tests/ui/cast.rs:244:21 + --> tests/ui/cast.rs:245:21 | LL | let _ = Self::B as u8; | ^^^^^^^^^^^^^ @@ -397,7 +397,7 @@ LL | let _ = Self::B as u8; = help: to override `-D warnings` add `#[allow(clippy::cast_enum_truncation)]` error: casting `main::E5` to `i8` may truncate the value - --> tests/ui/cast.rs:286:21 + --> tests/ui/cast.rs:287:21 | LL | let _ = self as i8; | ^^^^^^^^^^ @@ -409,13 +409,13 @@ LL | let _ = i8::try_from(self); | ~~~~~~~~~~~~~~~~~~ error: casting `main::E5::A` to `i8` will truncate the value - --> tests/ui/cast.rs:288:21 + --> tests/ui/cast.rs:289:21 | LL | let _ = Self::A as i8; | ^^^^^^^^^^^^^ error: casting `main::E6` to `i16` may truncate the value - --> tests/ui/cast.rs:305:21 + --> tests/ui/cast.rs:306:21 | LL | let _ = self as i16; | ^^^^^^^^^^^ @@ -427,7 +427,7 @@ LL | let _ = i16::try_from(self); | ~~~~~~~~~~~~~~~~~~~ error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:324:21 + --> tests/ui/cast.rs:325:21 | LL | let _ = self as usize; | ^^^^^^^^^^^^^ @@ -439,7 +439,7 @@ LL | let _ = usize::try_from(self); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E10` to `u16` may truncate the value - --> tests/ui/cast.rs:371:21 + --> tests/ui/cast.rs:372:21 | LL | let _ = self as u16; | ^^^^^^^^^^^ @@ -451,7 +451,7 @@ LL | let _ = u16::try_from(self); | ~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:382:13 + --> tests/ui/cast.rs:383:13 | LL | let c = (q >> 16) as u8; | ^^^^^^^^^^^^^^^ @@ -463,7 +463,7 @@ LL | let c = u8::try_from(q >> 16); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:386:13 + --> tests/ui/cast.rs:387:13 | LL | let c = (q / 1000) as u8; | ^^^^^^^^^^^^^^^^ @@ -475,85 +475,85 @@ LL | let c = u8::try_from(q / 1000); | ~~~~~~~~~~~~~~~~~~~~~~ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:398:9 + --> tests/ui/cast.rs:399:9 | LL | (x * x) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:403:32 + --> tests/ui/cast.rs:404:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:405:5 + --> tests/ui/cast.rs:406:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:406:5 + --> tests/ui/cast.rs:407:5 | LL | (-2_i32).pow(3) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:411:5 + --> tests/ui/cast.rs:412:5 | LL | (-5_i32 % 2) as u32; | ^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:413:5 + --> tests/ui/cast.rs:414:5 | LL | (-5_i32 % -2) as u32; | ^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:416:5 + --> tests/ui/cast.rs:417:5 | LL | (-2_i32 >> 1) as u32; | ^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:420:5 + --> tests/ui/cast.rs:421:5 | LL | (x * x) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:421:5 + --> tests/ui/cast.rs:422:5 | LL | (x * x * x) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:425:5 + --> tests/ui/cast.rs:426:5 | LL | (y * y * y * y * -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:427:5 + --> tests/ui/cast.rs:428:5 | LL | (y * y * y / y * 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:428:5 + --> tests/ui/cast.rs:429:5 | LL | (y * y / y * 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:430:5 + --> tests/ui/cast.rs:431:5 | LL | (y / y * y * -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `/` - --> tests/ui/cast.rs:430:6 + --> tests/ui/cast.rs:431:6 | LL | (y / y * y * -2) as u16; | ^^^^^ @@ -561,97 +561,97 @@ LL | (y / y * y * -2) as u16; = note: `#[deny(clippy::eq_op)]` on by default error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:433:5 + --> tests/ui/cast.rs:434:5 | LL | (y + y + y + -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:435:5 + --> tests/ui/cast.rs:436:5 | LL | (y + y + y + 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:439:5 + --> tests/ui/cast.rs:440:5 | LL | (z + -2) as u16; | ^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:441:5 + --> tests/ui/cast.rs:442:5 | LL | (z + z + 2) as u16; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:444:9 + --> tests/ui/cast.rs:445:9 | LL | (a * a * b * b * c * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:445:9 + --> tests/ui/cast.rs:446:9 | LL | (a * b * c) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:447:9 + --> tests/ui/cast.rs:448:9 | LL | (a * -b * c) as u32; | ^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:449:9 + --> tests/ui/cast.rs:450:9 | LL | (a * b * c * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:450:9 + --> tests/ui/cast.rs:451:9 | LL | (a * -2) as u32; | ^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:452:9 + --> tests/ui/cast.rs:453:9 | LL | (a * b * c * -2) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:454:9 + --> tests/ui/cast.rs:455:9 | LL | (a / b) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:455:9 + --> tests/ui/cast.rs:456:9 | LL | (a / b * c) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:457:9 + --> tests/ui/cast.rs:458:9 | LL | (a / b + b * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:459:9 + --> tests/ui/cast.rs:460:9 | LL | a.saturating_pow(3) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:461:9 + --> tests/ui/cast.rs:462:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:469:21 + --> tests/ui/cast.rs:470:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss | ^^^^^^^^^^^^^^^ @@ -662,7 +662,7 @@ LL | m!(); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:470:21 + --> tests/ui/cast.rs:471:21 | LL | let _ = u32::MAX as u8; // cast_possible_truncation | ^^^^^^^^^^^^^^ @@ -678,7 +678,7 @@ LL | let _ = u8::try_from(u32::MAX); // cast_possible_truncation | ~~~~~~~~~~~~~~~~~~~~~~ error: casting `f64` to `f32` may truncate the value - --> tests/ui/cast.rs:471:21 + --> tests/ui/cast.rs:472:21 | LL | let _ = std::f64::consts::PI as f32; // cast_possible_truncation | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -690,7 +690,7 @@ LL | m!(); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:480:5 + --> tests/ui/cast.rs:481:5 | LL | bar.unwrap().unwrap() as usize | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -702,10 +702,34 @@ LL | usize::try_from(bar.unwrap().unwrap()) | error: casting `i64` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:480:5 + --> tests/ui/cast.rs:481:5 | LL | bar.unwrap().unwrap() as usize | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 90 previous errors +error: casting `u64` to `u8` may truncate the value + --> tests/ui/cast.rs:496:5 + | +LL | (256 & 999999u64) as u8; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u8::try_from(256 & 999999u64); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: casting `u64` to `u8` may truncate the value + --> tests/ui/cast.rs:498:5 + | +LL | (255 % 999999u64) as u8; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u8::try_from(255 % 999999u64); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 92 previous errors diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 7501fd2b0bd99..2264b560791aa 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -4,7 +4,8 @@ clippy::needless_return, clippy::no_effect, clippy::single_match, - clippy::uninlined_format_args + clippy::uninlined_format_args, + clippy::let_unit_value )] fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { @@ -238,13 +239,22 @@ fn negative_cases(res_opt: Result, String>, res_res: Result return, } - match make::>>() { + #[clippy::msrv = "1.52.0"] + let _ = match make::>>() { Some(val) => match val { E::A(val) | E::B(val) => foo(val), _ => return, }, _ => return, - } + }; + #[clippy::msrv = "1.53.0"] + let _ = match make::>>() { + Some(val) => match val { + E::A(val) | E::B(val) => foo(val), + _ => return, + }, + _ => return, + }; if let Ok(val) = res_opt { if let Some(n) = val { let _ = || { diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 46b484ab05c4e..01944baee79af 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,5 +1,5 @@ error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:13:20 + --> tests/ui/collapsible_match.rs:14:20 | LL | Ok(val) => match val { | ____________________^ @@ -10,7 +10,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:13:12 + --> tests/ui/collapsible_match.rs:14:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -21,7 +21,7 @@ LL | Some(n) => foo(n), = help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]` error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:23:20 + --> tests/ui/collapsible_match.rs:24:20 | LL | Ok(val) => match val { | ____________________^ @@ -32,7 +32,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:23:12 + --> tests/ui/collapsible_match.rs:24:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -41,7 +41,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:33:9 + --> tests/ui/collapsible_match.rs:34:9 | LL | / if let Some(n) = val { LL | | @@ -50,7 +50,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:32:15 + --> tests/ui/collapsible_match.rs:33:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -58,7 +58,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:41:9 + --> tests/ui/collapsible_match.rs:42:9 | LL | / if let Some(n) = val { LL | | @@ -69,7 +69,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:40:15 + --> tests/ui/collapsible_match.rs:41:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -77,7 +77,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:53:9 + --> tests/ui/collapsible_match.rs:54:9 | LL | / match val { LL | | @@ -87,7 +87,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:52:15 + --> tests/ui/collapsible_match.rs:53:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -96,7 +96,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:63:13 + --> tests/ui/collapsible_match.rs:64:13 | LL | / if let Some(n) = val { LL | | @@ -105,7 +105,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:62:12 + --> tests/ui/collapsible_match.rs:63:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -113,7 +113,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:73:9 + --> tests/ui/collapsible_match.rs:74:9 | LL | / match val { LL | | @@ -123,7 +123,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:72:15 + --> tests/ui/collapsible_match.rs:73:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -132,7 +132,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:85:13 + --> tests/ui/collapsible_match.rs:86:13 | LL | / if let Some(n) = val { LL | | @@ -143,7 +143,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:84:12 + --> tests/ui/collapsible_match.rs:85:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -151,7 +151,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:97:20 + --> tests/ui/collapsible_match.rs:98:20 | LL | Ok(val) => match val { | ____________________^ @@ -162,7 +162,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:97:12 + --> tests/ui/collapsible_match.rs:98:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -171,7 +171,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:107:22 + --> tests/ui/collapsible_match.rs:108:22 | LL | Some(val) => match val { | ______________________^ @@ -182,7 +182,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:107:14 + --> tests/ui/collapsible_match.rs:108:14 | LL | Some(val) => match val { | ^^^ replace this binding @@ -190,8 +190,26 @@ LL | LL | Some(n) => foo(n), | ^^^^^^^ with this pattern +error: this `match` can be collapsed into the outer `match` + --> tests/ui/collapsible_match.rs:252:22 + | +LL | Some(val) => match val { + | ______________________^ +LL | | E::A(val) | E::B(val) => foo(val), +LL | | _ => return, +LL | | }, + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:252:14 + | +LL | Some(val) => match val { + | ^^^ replace this binding +LL | E::A(val) | E::B(val) => foo(val), + | ^^^^^^^^^^^^^^^^^^^^^ with this pattern + error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:273:9 + --> tests/ui/collapsible_match.rs:283:9 | LL | / if let Some(u) = a { LL | | @@ -200,7 +218,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:272:27 + --> tests/ui/collapsible_match.rs:282:27 | LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding @@ -208,7 +226,7 @@ LL | if let Some(u) = a { | ^^^^^^^ with this pattern, prefixed by a: error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:282:9 + --> tests/ui/collapsible_match.rs:292:9 | LL | / if let Some(u) = a { LL | | @@ -217,12 +235,12 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:281:35 + --> tests/ui/collapsible_match.rs:291:35 | LL | if let Issue9647::A { a: Some(a), .. } = x { | ^ replace this binding LL | if let Some(u) = a { | ^^^^^^^ with this pattern -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/collection_is_never_read.rs b/tests/ui/collection_is_never_read.rs index bd281f7870cee..eeb10da3402a0 100644 --- a/tests/ui/collection_is_never_read.rs +++ b/tests/ui/collection_is_never_read.rs @@ -222,3 +222,17 @@ fn supported_types() { //~^ ERROR: collection is never read x.push_front(1); } + +fn issue11783() { + struct Sender; + impl Sender { + fn send(&self, msg: String) -> Result<(), ()> { + // pretend to send message + println!("{msg}"); + Ok(()) + } + } + + let mut users: Vec = vec![]; + users.retain(|user| user.send("hello".to_string()).is_ok()); +} diff --git a/tests/ui/crashes/ice-5238.rs b/tests/ui/crashes/ice-5238.rs index fe03a39ad1ba8..ee2ae4f1a042e 100644 --- a/tests/ui/crashes/ice-5238.rs +++ b/tests/ui/crashes/ice-5238.rs @@ -3,7 +3,8 @@ #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] fn main() { - let _ = #[coroutine] || { + let _ = #[coroutine] + || { yield; }; } diff --git a/tests/ui/declare_interior_mutable_const/traits.rs b/tests/ui/declare_interior_mutable_const/traits.rs index adc53891ef54e..490073f97fb12 100644 --- a/tests/ui/declare_interior_mutable_const/traits.rs +++ b/tests/ui/declare_interior_mutable_const/traits.rs @@ -121,13 +121,12 @@ impl SelfType for AtomicUsize { // Even though a constant contains a generic type, if it also have an interior mutable type, // it should be linted at the definition site. trait BothOfCellAndGeneric { - // this is a false negative in the current implementation. - const DIRECT: Cell; + const DIRECT: Cell; //~ ERROR: interior mutable const INDIRECT: Cell<*const T>; //~ ERROR: interior mutable } impl BothOfCellAndGeneric for u64 { - const DIRECT: Cell = Cell::new(T::DEFAULT); + const DIRECT: Cell = Cell::new(T::DEFAULT); //~ ERROR: interior mutable const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); } diff --git a/tests/ui/declare_interior_mutable_const/traits.stderr b/tests/ui/declare_interior_mutable_const/traits.stderr index 328453efa244a..1d1e9e2002fa6 100644 --- a/tests/ui/declare_interior_mutable_const/traits.stderr +++ b/tests/ui/declare_interior_mutable_const/traits.stderr @@ -55,22 +55,34 @@ LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:126:5 + --> tests/ui/declare_interior_mutable_const/traits.rs:124:5 + | +LL | const DIRECT: Cell; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> tests/ui/declare_interior_mutable_const/traits.rs:125:5 | LL | const INDIRECT: Cell<*const T>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:142:5 + --> tests/ui/declare_interior_mutable_const/traits.rs:129:5 + | +LL | const DIRECT: Cell = Cell::new(T::DEFAULT); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> tests/ui/declare_interior_mutable_const/traits.rs:141:5 | LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:148:5 + --> tests/ui/declare_interior_mutable_const/traits.rs:147:5 | LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/disallowed_names.rs b/tests/ui/disallowed_names.rs index 9a701a2cbcfb8..13c883409bf6e 100644 --- a/tests/ui/disallowed_names.rs +++ b/tests/ui/disallowed_names.rs @@ -71,3 +71,8 @@ mod tests { } } } + +#[test] +fn test_with_disallowed_name() { + let foo = 0; +} diff --git a/tests/ui/large_stack_arrays.rs b/tests/ui/large_stack_arrays.rs index d5c4f95f8c422..6bcaf481c9f7e 100644 --- a/tests/ui/large_stack_arrays.rs +++ b/tests/ui/large_stack_arrays.rs @@ -1,6 +1,9 @@ +//@aux-build:proc_macros.rs #![warn(clippy::large_stack_arrays)] #![allow(clippy::large_enum_variant)] +extern crate proc_macros; + #[derive(Clone, Copy)] struct S { pub data: [u64; 32], @@ -55,3 +58,48 @@ fn main() { [(); 20_000_000], ); } + +#[allow(clippy::useless_vec)] +fn issue_12586() { + macro_rules! dummy { + ($n:expr) => { + $n + }; + // Weird rule to test help messages. + ($a:expr => $b:expr) => { + [$a, $b, $a, $b] + //~^ ERROR: allocating a local array larger than 512000 bytes + }; + ($id:ident; $n:literal) => { + dummy!(::std::vec![$id;$n]) + }; + ($($id:expr),+ $(,)?) => { + ::std::vec![$($id),*] + } + } + macro_rules! create_then_move { + ($id:ident; $n:literal) => {{ + let _x_ = [$id; $n]; + //~^ ERROR: allocating a local array larger than 512000 bytes + _x_ + }}; + } + + let x = [0u32; 50_000]; + let y = vec![x, x, x, x, x]; + let y = vec![dummy![x, x, x, x, x]]; + let y = vec![dummy![[x, x, x, x, x]]]; + let y = dummy![x, x, x, x, x]; + let y = [x, x, dummy!(x), x, x]; + //~^ ERROR: allocating a local array larger than 512000 bytes + let y = dummy![x => x]; + let y = dummy![x;5]; + let y = dummy!(vec![dummy![x, x, x, x, x]]); + let y = dummy![[x, x, x, x, x]]; + //~^ ERROR: allocating a local array larger than 512000 bytes + + let y = proc_macros::make_it_big!([x; 1]); + //~^ ERROR: allocating a local array larger than 512000 bytes + let y = vec![proc_macros::make_it_big!([x; 10])]; + let y = vec![create_then_move![x; 5]; 5]; +} diff --git a/tests/ui/large_stack_arrays.stderr b/tests/ui/large_stack_arrays.stderr index 007ca61c2de13..06294ee8b8cb1 100644 --- a/tests/ui/large_stack_arrays.stderr +++ b/tests/ui/large_stack_arrays.stderr @@ -1,5 +1,5 @@ error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:29:14 + --> tests/ui/large_stack_arrays.rs:32:14 | LL | let _x = [build(); 3]; | ^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _x = [build(); 3]; = help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]` error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:32:14 + --> tests/ui/large_stack_arrays.rs:35:14 | LL | let _y = [build(), build(), build()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _y = [build(), build(), build()]; = help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:38:9 + --> tests/ui/large_stack_arrays.rs:41:9 | LL | [0u32; 20_000_000], | ^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | [0u32; 20_000_000], = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:40:9 + --> tests/ui/large_stack_arrays.rs:43:9 | LL | [S { data: [0; 32] }; 5000], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | [S { data: [0; 32] }; 5000], = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:42:9 + --> tests/ui/large_stack_arrays.rs:45:9 | LL | [Some(""); 20_000_000], | ^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | [Some(""); 20_000_000], = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:44:9 + --> tests/ui/large_stack_arrays.rs:47:9 | LL | [E::T(0); 5000], | ^^^^^^^^^^^^^^^ @@ -49,12 +49,56 @@ LL | [E::T(0); 5000], = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:46:9 + --> tests/ui/large_stack_arrays.rs:49:9 | LL | [0u8; usize::MAX], | ^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()` -error: aborting due to 7 previous errors +error: allocating a local array larger than 512000 bytes + --> tests/ui/large_stack_arrays.rs:93:13 + | +LL | let y = [x, x, dummy!(x), x, x]; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![x, x, dummy!(x), x, x].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> tests/ui/large_stack_arrays.rs:70:13 + | +LL | [$a, $b, $a, $b] + | ^^^^^^^^^^^^^^^^ +... +LL | let y = dummy![x => x]; + | -------------- in this macro invocation + | + = note: this error originates in the macro `dummy` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: allocating a local array larger than 512000 bytes + --> tests/ui/large_stack_arrays.rs:98:20 + | +LL | let y = dummy![[x, x, x, x, x]]; + | ^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![x, x, x, x, x].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> tests/ui/large_stack_arrays.rs:101:39 + | +LL | let y = proc_macros::make_it_big!([x; 1]); + | ^^^^^^ + +error: allocating a local array larger than 512000 bytes + --> tests/ui/large_stack_arrays.rs:82:23 + | +LL | let _x_ = [$id; $n]; + | ^^^^^^^^^ +... +LL | let y = vec![create_then_move![x; 5]; 5]; + | ----------------------- in this macro invocation + | + = note: this error originates in the macro `create_then_move` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 12 previous errors diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index 9c4bd335ad8ba..a72caa3a37ee6 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -55,3 +55,30 @@ fn msrv_1_47() { const FOO: bool = 'x'.is_ascii_digit(); const BAR: bool = 'x'.is_ascii_hexdigit(); } + +#[allow(clippy::deref_addrof, clippy::needless_borrow)] +fn with_refs() { + let cool_letter = &&'g'; + cool_letter.is_ascii_digit(); + cool_letter.is_ascii_lowercase(); +} + +fn generics() { + fn a(u: &U) -> bool + where + char: PartialOrd, + U: PartialOrd + ?Sized, + { + ('A'..='Z').contains(u) + } + + fn take_while(cond: F) + where + Item: Sized, + F: Fn(Item) -> bool, + { + } + take_while(|c: char| c.is_ascii_uppercase()); + take_while(|c: u8| c.is_ascii_uppercase()); + take_while(|c: char| c.is_ascii_uppercase()); +} diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index 785943cd24d2c..bb6e2a317da16 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -55,3 +55,30 @@ fn msrv_1_47() { const FOO: bool = matches!('x', '0'..='9'); const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); } + +#[allow(clippy::deref_addrof, clippy::needless_borrow)] +fn with_refs() { + let cool_letter = &&'g'; + ('0'..='9').contains(&&cool_letter); + ('a'..='z').contains(*cool_letter); +} + +fn generics() { + fn a(u: &U) -> bool + where + char: PartialOrd, + U: PartialOrd + ?Sized, + { + ('A'..='Z').contains(u) + } + + fn take_while(cond: F) + where + Item: Sized, + F: Fn(Item) -> bool, + { + } + take_while(|c| ('A'..='Z').contains(&c)); + take_while(|c| (b'A'..=b'Z').contains(&c)); + take_while(|c: char| ('A'..='Z').contains(&c)); +} diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index 3632077ec8086..a93ccace28a6e 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -133,5 +133,45 @@ error: manual check for common ascii range LL | const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()` -error: aborting due to 22 previous errors +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:62:5 + | +LL | ('0'..='9').contains(&&cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_digit()` + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:63:5 + | +LL | ('a'..='z').contains(*cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_lowercase()` + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:81:20 + | +LL | take_while(|c| ('A'..='Z').contains(&c)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | take_while(|c: char| c.is_ascii_uppercase()); + | ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:82:20 + | +LL | take_while(|c| (b'A'..=b'Z').contains(&c)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | take_while(|c: u8| c.is_ascii_uppercase()); + | ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:83:26 + | +LL | take_while(|c: char| ('A'..='Z').contains(&c)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()` + +error: aborting due to 27 previous errors diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index 861764a2aeeb9..b84b3dc349ec3 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -32,7 +32,7 @@ fn main() { // testing that the suggestion actually fits in its type let fail30 = 127_i8; // should be i8 let fail31 = 240_u8; // should be u8 - let ok32 = 360_8; // doesnt fit in either, should be ignored + let ok32 = 360_8; // doesn't fit in either, should be ignored let fail33 = 0x1234_i16; let fail34 = 0xABCD_u16; let ok35 = 0x12345_16; diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 4a15c335fd893..a47a736067a85 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -32,7 +32,7 @@ fn main() { // testing that the suggestion actually fits in its type let fail30 = 127_8; // should be i8 let fail31 = 240_8; // should be u8 - let ok32 = 360_8; // doesnt fit in either, should be ignored + let ok32 = 360_8; // doesn't fit in either, should be ignored let fail33 = 0x1234_16; let fail34 = 0xABCD_16; let ok35 = 0x12345_16; diff --git a/tests/ui/mut_key.rs b/tests/ui/mut_key.rs index 2d70bfd4c770c..81d8732b3b210 100644 --- a/tests/ui/mut_key.rs +++ b/tests/ui/mut_key.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::Relaxed; use std::sync::Arc; -//@no-rustfix + struct Key(AtomicUsize); impl Clone for Key { @@ -77,8 +77,6 @@ fn main() { //~^ ERROR: mutable key type let _map = HashMap::<&mut Cell, usize>::new(); //~^ ERROR: mutable key type - let _map = HashMap::<&mut usize, usize>::new(); - //~^ ERROR: mutable key type // Collection types from `std` who's impl of `Hash` or `Ord` delegate their type parameters let _map = HashMap::>, usize>::new(); //~^ ERROR: mutable key type @@ -92,8 +90,6 @@ fn main() { //~^ ERROR: mutable key type let _map = HashMap::>>, usize>::new(); //~^ ERROR: mutable key type - let _map = HashMap::, usize>::new(); - //~^ ERROR: mutable key type // Smart pointers from `std` who's impl of `Hash` or `Ord` delegate their type parameters let _map = HashMap::>, usize>::new(); //~^ ERROR: mutable key type @@ -101,4 +97,8 @@ fn main() { //~^ ERROR: mutable key type let _map = HashMap::>, usize>::new(); //~^ ERROR: mutable key type + + // Not interior mutability + let _map = HashMap::<&mut usize, usize>::new(); + let _map = HashMap::, usize>::new(); } diff --git a/tests/ui/mut_key.stderr b/tests/ui/mut_key.stderr index e54c3075d4f13..5ad9aad2d0a5a 100644 --- a/tests/ui/mut_key.stderr +++ b/tests/ui/mut_key.stderr @@ -38,70 +38,58 @@ LL | let _map = HashMap::<&mut Cell, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> tests/ui/mut_key.rs:80:5 - | -LL | let _map = HashMap::<&mut usize, usize>::new(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: mutable key type - --> tests/ui/mut_key.rs:83:5 + --> tests/ui/mut_key.rs:81:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> tests/ui/mut_key.rs:85:5 + --> tests/ui/mut_key.rs:83:5 | LL | let _map = HashMap::, ()>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> tests/ui/mut_key.rs:87:5 + --> tests/ui/mut_key.rs:85:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> tests/ui/mut_key.rs:89:5 + --> tests/ui/mut_key.rs:87:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> tests/ui/mut_key.rs:91:5 + --> tests/ui/mut_key.rs:89:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> tests/ui/mut_key.rs:93:5 + --> tests/ui/mut_key.rs:91:5 | LL | let _map = HashMap::>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> tests/ui/mut_key.rs:95:5 - | -LL | let _map = HashMap::, usize>::new(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: mutable key type - --> tests/ui/mut_key.rs:98:5 + --> tests/ui/mut_key.rs:94:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> tests/ui/mut_key.rs:100:5 + --> tests/ui/mut_key.rs:96:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> tests/ui/mut_key.rs:102:5 + --> tests/ui/mut_key.rs:98:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index 8c0e7ba762786..2362314290e61 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -113,6 +113,10 @@ fn should_not_lint() { let _ = v.iter().for_each(|elem| { acc += elem; }); + // `for_each` has a closure with an unsafe block. + v.iter().for_each(|elem| unsafe { + acc += elem; + }); } fn main() {} diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index cdc903a636c99..5b1186daa229b 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -113,6 +113,10 @@ fn should_not_lint() { let _ = v.iter().for_each(|elem| { acc += elem; }); + // `for_each` has a closure with an unsafe block. + v.iter().for_each(|elem| unsafe { + acc += elem; + }); } fn main() {} diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index a92197fb0af5c..3f5f55f400208 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -44,18 +44,13 @@ fn non_mut_ref(_: &Vec) {} struct Bar; impl Bar { - // Should not warn on `&mut self`. fn bar(&mut self) {} + //~^ ERROR: this argument is a mutable reference, but not used mutably fn mushroom(&self, vec: &mut Vec) -> usize { //~^ ERROR: this argument is a mutable reference, but not used mutably vec.len() } - - fn badger(&mut self, vec: &mut Vec) -> usize { - //~^ ERROR: this argument is a mutable reference, but not used mutably - vec.len() - } } trait Babar { @@ -307,6 +302,41 @@ fn filter_copy(predicate: &mut impl FnMut(T) -> bool) -> impl FnMut(&T) move |&item| predicate(item) } +trait MutSelfTrait { + // Should not warn since it's a trait method. + fn mut_self(&mut self); +} + +struct MutSelf { + a: u32, +} + +impl MutSelf { + fn bar(&mut self) {} + //~^ ERROR: this argument is a mutable reference, but not used mutably + async fn foo(&mut self, u: &mut i32, v: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + //~| ERROR: this argument is a mutable reference, but not used mutably + async { + *u += 1; + } + .await; + } + async fn foo2(&mut self, u: &mut i32, v: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + async { + self.a += 1; + *u += 1; + } + .await; + } +} + +impl MutSelfTrait for MutSelf { + // Should not warn since it's a trait method. + fn mut_self(&mut self) {} +} + // `is_from_proc_macro` stress tests fn _empty_tup(x: &mut (())) {} fn _single_tup(x: &mut ((i32,))) {} diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index 89dad3e60b143..21ca393dcb63c 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -14,73 +14,73 @@ LL | fn foo6(s: &mut Vec) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:50:29 + --> tests/ui/needless_pass_by_ref_mut.rs:47:12 | -LL | fn mushroom(&self, vec: &mut Vec) -> usize { - | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` +LL | fn bar(&mut self) {} + | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:55:31 + --> tests/ui/needless_pass_by_ref_mut.rs:50:29 | -LL | fn badger(&mut self, vec: &mut Vec) -> usize { - | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` +LL | fn mushroom(&self, vec: &mut Vec) -> usize { + | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:132:16 + --> tests/ui/needless_pass_by_ref_mut.rs:127:16 | LL | async fn a1(x: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:136:16 + --> tests/ui/needless_pass_by_ref_mut.rs:131:16 | LL | async fn a2(x: &mut i32, y: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:140:16 + --> tests/ui/needless_pass_by_ref_mut.rs:135:16 | LL | async fn a3(x: &mut i32, y: String, z: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:144:16 + --> tests/ui/needless_pass_by_ref_mut.rs:139:16 | LL | async fn a4(x: &mut i32, y: i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:148:24 + --> tests/ui/needless_pass_by_ref_mut.rs:143:24 | LL | async fn a5(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:152:24 + --> tests/ui/needless_pass_by_ref_mut.rs:147:24 | LL | async fn a6(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:156:32 + --> tests/ui/needless_pass_by_ref_mut.rs:151:32 | LL | async fn a7(x: i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:160:24 + --> tests/ui/needless_pass_by_ref_mut.rs:155:24 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:160:45 + --> tests/ui/needless_pass_by_ref_mut.rs:155:45 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:194:16 + --> tests/ui/needless_pass_by_ref_mut.rs:189:16 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -88,7 +88,7 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:200:20 + --> tests/ui/needless_pass_by_ref_mut.rs:195:20 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -96,19 +96,19 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:214:39 + --> tests/ui/needless_pass_by_ref_mut.rs:209:39 | LL | async fn inner_async2(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:222:26 + --> tests/ui/needless_pass_by_ref_mut.rs:217:26 | LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:241:34 + --> tests/ui/needless_pass_by_ref_mut.rs:236:34 | LL | pub async fn call_in_closure1(n: &mut str) { | ^^^^^^^^ help: consider changing to: `&str` @@ -116,15 +116,7 @@ LL | pub async fn call_in_closure1(n: &mut str) { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:253:25 - | -LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { - | ^^^^^^^^^^ help: consider changing to: `&usize` - | - = warning: changing this function will impact semver compatibility - -error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:260:20 + --> tests/ui/needless_pass_by_ref_mut.rs:255:20 | LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -132,7 +124,7 @@ LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:271:26 + --> tests/ui/needless_pass_by_ref_mut.rs:266:26 | LL | pub async fn closure4(n: &mut usize) { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -140,64 +132,88 @@ LL | pub async fn closure4(n: &mut usize) { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:311:18 + --> tests/ui/needless_pass_by_ref_mut.rs:315:12 + | +LL | fn bar(&mut self) {} + | ^^^^^^^^^ help: consider changing to: `&self` + +error: this argument is a mutable reference, but not used mutably + --> tests/ui/needless_pass_by_ref_mut.rs:317:18 + | +LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { + | ^^^^^^^^^ help: consider changing to: `&self` + +error: this argument is a mutable reference, but not used mutably + --> tests/ui/needless_pass_by_ref_mut.rs:317:45 + | +LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { + | ^^^^^^^^ help: consider changing to: `&u32` + +error: this argument is a mutable reference, but not used mutably + --> tests/ui/needless_pass_by_ref_mut.rs:325:46 + | +LL | async fn foo2(&mut self, u: &mut i32, v: &mut u32) { + | ^^^^^^^^ help: consider changing to: `&u32` + +error: this argument is a mutable reference, but not used mutably + --> tests/ui/needless_pass_by_ref_mut.rs:341:18 | LL | fn _empty_tup(x: &mut (())) {} | ^^^^^^^^^ help: consider changing to: `&()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:312:19 + --> tests/ui/needless_pass_by_ref_mut.rs:342:19 | LL | fn _single_tup(x: &mut ((i32,))) {} | ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:313:18 + --> tests/ui/needless_pass_by_ref_mut.rs:343: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 - --> tests/ui/needless_pass_by_ref_mut.rs:314:11 + --> tests/ui/needless_pass_by_ref_mut.rs:344:11 | LL | fn _fn(x: &mut (fn())) {} | ^^^^^^^^^^^ help: consider changing to: `&fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:316:23 + --> tests/ui/needless_pass_by_ref_mut.rs:346: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 - --> tests/ui/needless_pass_by_ref_mut.rs:317:20 + --> tests/ui/needless_pass_by_ref_mut.rs:347: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 - --> tests/ui/needless_pass_by_ref_mut.rs:318:18 + --> tests/ui/needless_pass_by_ref_mut.rs:348: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 - --> tests/ui/needless_pass_by_ref_mut.rs:319:25 + --> tests/ui/needless_pass_by_ref_mut.rs:349: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 - --> tests/ui/needless_pass_by_ref_mut.rs:320:20 + --> tests/ui/needless_pass_by_ref_mut.rs:350: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 - --> tests/ui/needless_pass_by_ref_mut.rs:321:20 + --> tests/ui/needless_pass_by_ref_mut.rs:351: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 +error: aborting due to 34 previous errors diff --git a/tests/ui/needless_pass_by_ref_mut2.fixed b/tests/ui/needless_pass_by_ref_mut2.fixed new file mode 100644 index 0000000000000..3c2576213cd77 --- /dev/null +++ b/tests/ui/needless_pass_by_ref_mut2.fixed @@ -0,0 +1,24 @@ +// If both `inner_async3` and `inner_async4` are present, aliases are declared after +// they're used in `inner_async4` for some reasons... This test ensures that no +// only `v` is marked as not used mutably in `inner_async4`. + +#![allow(clippy::redundant_closure_call)] +#![warn(clippy::needless_pass_by_ref_mut)] + +pub async fn inner_async3(x: &i32, y: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + async { + *y += 1; + } + .await; +} + +pub async fn inner_async4(u: &mut i32, v: &u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + async { + *u += 1; + } + .await; +} + +fn main() {} diff --git a/tests/ui/needless_pass_by_ref_mut2.rs b/tests/ui/needless_pass_by_ref_mut2.rs new file mode 100644 index 0000000000000..34b0b564deb8e --- /dev/null +++ b/tests/ui/needless_pass_by_ref_mut2.rs @@ -0,0 +1,24 @@ +// If both `inner_async3` and `inner_async4` are present, aliases are declared after +// they're used in `inner_async4` for some reasons... This test ensures that no +// only `v` is marked as not used mutably in `inner_async4`. + +#![allow(clippy::redundant_closure_call)] +#![warn(clippy::needless_pass_by_ref_mut)] + +pub async fn inner_async3(x: &mut i32, y: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + async { + *y += 1; + } + .await; +} + +pub async fn inner_async4(u: &mut i32, v: &mut u32) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + async { + *u += 1; + } + .await; +} + +fn main() {} diff --git a/tests/ui/needless_pass_by_ref_mut2.stderr b/tests/ui/needless_pass_by_ref_mut2.stderr new file mode 100644 index 0000000000000..c875360322568 --- /dev/null +++ b/tests/ui/needless_pass_by_ref_mut2.stderr @@ -0,0 +1,20 @@ +error: this argument is a mutable reference, but not used mutably + --> tests/ui/needless_pass_by_ref_mut2.rs:8:30 + | +LL | pub async fn inner_async3(x: &mut i32, y: &mut u32) { + | ^^^^^^^^ help: consider changing to: `&i32` + | + = warning: changing this function will impact semver compatibility + = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` + +error: this argument is a mutable reference, but not used mutably + --> tests/ui/needless_pass_by_ref_mut2.rs:16:43 + | +LL | pub async fn inner_async4(u: &mut i32, v: &mut u32) { + | ^^^^^^^^ help: consider changing to: `&u32` + | + = warning: changing this function will impact semver compatibility + +error: aborting due to 2 previous errors + diff --git a/tests/ui/non_canonical_partial_ord_impl.fixed b/tests/ui/non_canonical_partial_ord_impl.fixed index db55cc094e3ac..d444a753697f0 100644 --- a/tests/ui/non_canonical_partial_ord_impl.fixed +++ b/tests/ui/non_canonical_partial_ord_impl.fixed @@ -142,3 +142,21 @@ impl PartialOrd for H { Some(Ord::cmp(self, other)) } } + +// #12683, do not lint + +#[derive(Eq, PartialEq)] +struct I(u32); + +impl Ord for I { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for I { + #[allow(clippy::needless_return)] + fn partial_cmp(&self, other: &Self) -> Option { + return Some(self.cmp(other)); + } +} diff --git a/tests/ui/non_canonical_partial_ord_impl.rs b/tests/ui/non_canonical_partial_ord_impl.rs index 52f4b85b9172f..dc6c4354604d0 100644 --- a/tests/ui/non_canonical_partial_ord_impl.rs +++ b/tests/ui/non_canonical_partial_ord_impl.rs @@ -146,3 +146,21 @@ impl PartialOrd for H { Some(Ord::cmp(self, other)) } } + +// #12683, do not lint + +#[derive(Eq, PartialEq)] +struct I(u32); + +impl Ord for I { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for I { + #[allow(clippy::needless_return)] + fn partial_cmp(&self, other: &Self) -> Option { + return Some(self.cmp(other)); + } +} diff --git a/tests/ui/readonly_write_lock.fixed b/tests/ui/readonly_write_lock.fixed index 76f4a43ae5300..4db13482ac786 100644 --- a/tests/ui/readonly_write_lock.fixed +++ b/tests/ui/readonly_write_lock.fixed @@ -43,3 +43,7 @@ fn main() { *writer1 = *writer2; } } + +fn issue12733(rw: &RwLock<()>) { + let _write_guard = rw.write().unwrap(); +} diff --git a/tests/ui/readonly_write_lock.rs b/tests/ui/readonly_write_lock.rs index 3d1d3855fe129..66ba1b2d6969e 100644 --- a/tests/ui/readonly_write_lock.rs +++ b/tests/ui/readonly_write_lock.rs @@ -43,3 +43,7 @@ fn main() { *writer1 = *writer2; } } + +fn issue12733(rw: &RwLock<()>) { + let _write_guard = rw.write().unwrap(); +} diff --git a/tests/ui/redundant_guards.fixed b/tests/ui/redundant_guards.fixed index ded91e0837699..ed4b1c219150e 100644 --- a/tests/ui/redundant_guards.fixed +++ b/tests/ui/redundant_guards.fixed @@ -136,6 +136,18 @@ fn f(s: Option) { } } +fn not_matches() { + match Some(42) { + // The pattern + guard is not equivalent to `Some(42)` because of the `panic!` + Some(v) + if match v { + 42 => true, + _ => panic!(), + } => {}, + _ => {}, + } +} + struct S { a: usize, } diff --git a/tests/ui/redundant_guards.rs b/tests/ui/redundant_guards.rs index 2aaa2ace3982e..adbc4ed16cd7b 100644 --- a/tests/ui/redundant_guards.rs +++ b/tests/ui/redundant_guards.rs @@ -136,6 +136,18 @@ fn f(s: Option) { } } +fn not_matches() { + match Some(42) { + // The pattern + guard is not equivalent to `Some(42)` because of the `panic!` + Some(v) + if match v { + 42 => true, + _ => panic!(), + } => {}, + _ => {}, + } +} + struct S { a: usize, } diff --git a/tests/ui/redundant_guards.stderr b/tests/ui/redundant_guards.stderr index 01ca91fcd09e8..fd12e08328231 100644 --- a/tests/ui/redundant_guards.stderr +++ b/tests/ui/redundant_guards.stderr @@ -132,7 +132,7 @@ LL + 1 => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:174:28 + --> tests/ui/redundant_guards.rs:186:28 | LL | Some(ref x) if x == &1 => {}, | ^^^^^^^ @@ -144,7 +144,7 @@ LL + Some(1) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:175:28 + --> tests/ui/redundant_guards.rs:187:28 | LL | Some(ref x) if &1 == x => {}, | ^^^^^^^ @@ -156,7 +156,7 @@ LL + Some(1) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:176:28 + --> tests/ui/redundant_guards.rs:188:28 | LL | Some(ref x) if let &2 = x => {}, | ^^^^^^^^^^ @@ -168,7 +168,7 @@ LL + Some(2) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:177:28 + --> tests/ui/redundant_guards.rs:189:28 | LL | Some(ref x) if matches!(x, &3) => {}, | ^^^^^^^^^^^^^^^ @@ -180,7 +180,7 @@ LL + Some(3) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:197:32 + --> tests/ui/redundant_guards.rs:209:32 | LL | B { ref c, .. } if c == &1 => {}, | ^^^^^^^ @@ -192,7 +192,7 @@ LL + B { c: 1, .. } => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:198:32 + --> tests/ui/redundant_guards.rs:210:32 | LL | B { ref c, .. } if &1 == c => {}, | ^^^^^^^ @@ -204,7 +204,7 @@ LL + B { c: 1, .. } => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:199:32 + --> tests/ui/redundant_guards.rs:211:32 | LL | B { ref c, .. } if let &1 = c => {}, | ^^^^^^^^^^ @@ -216,7 +216,7 @@ LL + B { c: 1, .. } => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:200:32 + --> tests/ui/redundant_guards.rs:212:32 | LL | B { ref c, .. } if matches!(c, &1) => {}, | ^^^^^^^^^^^^^^^ @@ -228,7 +228,7 @@ LL + B { c: 1, .. } => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:210:26 + --> tests/ui/redundant_guards.rs:222:26 | LL | Some(Some(x)) if x.is_empty() => {}, | ^^^^^^^^^^^^ @@ -240,7 +240,7 @@ LL + Some(Some("")) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:221:26 + --> tests/ui/redundant_guards.rs:233:26 | LL | Some(Some(x)) if x.is_empty() => {}, | ^^^^^^^^^^^^ @@ -252,7 +252,7 @@ LL + Some(Some([])) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:226:26 + --> tests/ui/redundant_guards.rs:238:26 | LL | Some(Some(x)) if x.is_empty() => {}, | ^^^^^^^^^^^^ @@ -264,7 +264,7 @@ LL + Some(Some([])) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:237:26 + --> tests/ui/redundant_guards.rs:249:26 | LL | Some(Some(x)) if x.starts_with(&[]) => {}, | ^^^^^^^^^^^^^^^^^^ @@ -276,7 +276,7 @@ LL + Some(Some([..])) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:242:26 + --> tests/ui/redundant_guards.rs:254:26 | LL | Some(Some(x)) if x.starts_with(&[1]) => {}, | ^^^^^^^^^^^^^^^^^^^ @@ -288,7 +288,7 @@ LL + Some(Some([1, ..])) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:247:26 + --> tests/ui/redundant_guards.rs:259:26 | LL | Some(Some(x)) if x.starts_with(&[1, 2]) => {}, | ^^^^^^^^^^^^^^^^^^^^^^ @@ -300,7 +300,7 @@ LL + Some(Some([1, 2, ..])) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:252:26 + --> tests/ui/redundant_guards.rs:264:26 | LL | Some(Some(x)) if x.ends_with(&[1, 2]) => {}, | ^^^^^^^^^^^^^^^^^^^^ @@ -312,7 +312,7 @@ LL + Some(Some([.., 1, 2])) => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:274:18 + --> tests/ui/redundant_guards.rs:286:18 | LL | y if y.is_empty() => {}, | ^^^^^^^^^^^^ @@ -324,7 +324,7 @@ LL + "" => {}, | error: redundant guard - --> tests/ui/redundant_guards.rs:293:22 + --> tests/ui/redundant_guards.rs:305:22 | LL | y if y.is_empty() => {}, | ^^^^^^^^^^^^ diff --git a/tests/ui/redundant_locals.rs b/tests/ui/redundant_locals.rs index e9d77182a9196..d21aa240b2df9 100644 --- a/tests/ui/redundant_locals.rs +++ b/tests/ui/redundant_locals.rs @@ -191,14 +191,20 @@ fn issue12225() { let v4 = v4; dbg!(&v4); }); - assert_static(#[coroutine] static || { - let v5 = v5; - yield; - }); - assert_static(#[coroutine] || { - let v6 = v6; - yield; - }); + assert_static( + #[coroutine] + static || { + let v5 = v5; + yield; + }, + ); + assert_static( + #[coroutine] + || { + let v6 = v6; + yield; + }, + ); fn foo(a: &str, b: &str) {} diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index 9573fdbcfde33..a18d6319f89db 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -1,5 +1,5 @@ #![allow(clippy::needless_raw_strings, clippy::needless_raw_string_hashes, unused_must_use)] - +#![warn(clippy::single_char_pattern)] use std::collections::HashSet; fn main() { @@ -10,9 +10,9 @@ fn main() { let y = "x"; x.split(y); - x.split('ß'); - x.split('ℝ'); - x.split('💣'); + x.split("ß"); + x.split("ℝ"); + x.split("💣"); // Can't use this lint for unicode code points which don't fit in a char x.split("❤️"); x.split_inclusive('x'); @@ -34,8 +34,6 @@ fn main() { x.rmatch_indices('x'); x.trim_start_matches('x'); x.trim_end_matches('x'); - x.strip_prefix('x'); - x.strip_suffix('x'); x.replace('x', "y"); x.replacen('x', "y", 3); // Make sure we escape characters correctly. @@ -64,4 +62,8 @@ fn main() { // Must escape backslash in raw strings when converting to char #8060 x.split('\\'); x.split('\\'); + + // should not warn, the char versions are actually slower in some cases + x.strip_prefix("x"); + x.strip_suffix("x"); } diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index 8a04480dbc643..b52e6fb2fdfba 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -1,5 +1,5 @@ #![allow(clippy::needless_raw_strings, clippy::needless_raw_string_hashes, unused_must_use)] - +#![warn(clippy::single_char_pattern)] use std::collections::HashSet; fn main() { @@ -34,8 +34,6 @@ fn main() { x.rmatch_indices("x"); x.trim_start_matches("x"); x.trim_end_matches("x"); - x.strip_prefix("x"); - x.strip_suffix("x"); x.replace("x", "y"); x.replacen("x", "y", 3); // Make sure we escape characters correctly. @@ -64,4 +62,8 @@ fn main() { // Must escape backslash in raw strings when converting to char #8060 x.split(r#"\"#); x.split(r"\"); + + // should not warn, the char versions are actually slower in some cases + x.strip_prefix("x"); + x.strip_suffix("x"); } diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index 5a2ec6c764b18..b2deed23cbd53 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -7,24 +7,6 @@ LL | x.split("x"); = note: `-D clippy::single-char-pattern` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::single_char_pattern)]` -error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:13:13 - | -LL | x.split("ß"); - | ^^^ help: consider using a `char`: `'ß'` - -error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:14:13 - | -LL | x.split("ℝ"); - | ^^^ help: consider using a `char`: `'ℝ'` - -error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:15:13 - | -LL | x.split("💣"); - | ^^^^ help: consider using a `char`: `'💣'` - error: single-character string constant used as pattern --> tests/ui/single_char_pattern.rs:18:23 | @@ -140,106 +122,94 @@ LL | x.trim_end_matches("x"); | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:37:20 - | -LL | x.strip_prefix("x"); - | ^^^ help: consider using a `char`: `'x'` - -error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:38:20 - | -LL | x.strip_suffix("x"); - | ^^^ help: consider using a `char`: `'x'` - -error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:39:15 + --> tests/ui/single_char_pattern.rs:37:15 | LL | x.replace("x", "y"); | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:40:16 + --> tests/ui/single_char_pattern.rs:38:16 | LL | x.replacen("x", "y", 3); | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:42:13 + --> tests/ui/single_char_pattern.rs:40:13 | LL | x.split("\n"); | ^^^^ help: consider using a `char`: `'\n'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:43:13 + --> tests/ui/single_char_pattern.rs:41:13 | LL | x.split("'"); | ^^^ help: consider using a `char`: `'\''` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:44:13 + --> tests/ui/single_char_pattern.rs:42:13 | LL | x.split("\'"); | ^^^^ help: consider using a `char`: `'\''` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:46:13 + --> tests/ui/single_char_pattern.rs:44:13 | LL | x.split("\""); | ^^^^ help: consider using a `char`: `'"'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:51:31 + --> tests/ui/single_char_pattern.rs:49:31 | LL | x.replace(';', ",").split(","); // issue #2978 | ^^^ help: consider using a `char`: `','` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:52:19 + --> tests/ui/single_char_pattern.rs:50:19 | LL | x.starts_with("\x03"); // issue #2996 | ^^^^^^ help: consider using a `char`: `'\x03'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:59:13 + --> tests/ui/single_char_pattern.rs:57:13 | LL | x.split(r"a"); | ^^^^ help: consider using a `char`: `'a'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:60:13 + --> tests/ui/single_char_pattern.rs:58:13 | LL | x.split(r#"a"#); | ^^^^^^ help: consider using a `char`: `'a'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:61:13 + --> tests/ui/single_char_pattern.rs:59:13 | LL | x.split(r###"a"###); | ^^^^^^^^^^ help: consider using a `char`: `'a'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:62:13 + --> tests/ui/single_char_pattern.rs:60:13 | LL | x.split(r###"'"###); | ^^^^^^^^^^ help: consider using a `char`: `'\''` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:63:13 + --> tests/ui/single_char_pattern.rs:61:13 | LL | x.split(r###"#"###); | ^^^^^^^^^^ help: consider using a `char`: `'#'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:65:13 + --> tests/ui/single_char_pattern.rs:63:13 | LL | x.split(r#"\"#); | ^^^^^^ help: consider using a `char`: `'\\'` error: single-character string constant used as pattern - --> tests/ui/single_char_pattern.rs:66:13 + --> tests/ui/single_char_pattern.rs:64:13 | LL | x.split(r"\"); | ^^^^ help: consider using a `char`: `'\\'` -error: aborting due to 40 previous errors +error: aborting due to 35 previous errors diff --git a/tests/ui/thread_local_initializer_can_be_made_const.fixed b/tests/ui/thread_local_initializer_can_be_made_const.fixed index a6ed59d49c54f..4c9bd0bd8634f 100644 --- a/tests/ui/thread_local_initializer_can_be_made_const.fixed +++ b/tests/ui/thread_local_initializer_can_be_made_const.fixed @@ -1,6 +1,6 @@ #![warn(clippy::thread_local_initializer_can_be_made_const)] -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; fn main() { // lint and suggest const @@ -36,6 +36,37 @@ fn main() { } } +fn issue_12637() { + /// The set methods on LocalKey> and LocalKey> are + /// guaranteed to bypass the thread_local's initialization expression. + /// See rust-lang/rust#92122. Thus, = panic!() is a useful idiom for + /// forcing the use of set on each thread before it accesses the thread local in any other + /// manner. + thread_local! { + static STATE_12637_PANIC: Cell = panic!(); + } + STATE_12637_PANIC.set(9); + println!("{}", STATE_12637_PANIC.get()); + + thread_local! { + static STATE_12637_TODO: Cell = todo!(); + } + STATE_12637_TODO.set(9); + println!("{}", STATE_12637_TODO.get()); + + thread_local! { + static STATE_12637_UNIMPLEMENTED: Cell = unimplemented!(); + } + STATE_12637_UNIMPLEMENTED.set(9); + println!("{}", STATE_12637_UNIMPLEMENTED.get()); + + thread_local! { + static STATE_12637_UNREACHABLE: Cell = unreachable!(); + } + STATE_12637_UNREACHABLE.set(9); + println!("{}", STATE_12637_UNREACHABLE.get()); +} + #[clippy::msrv = "1.58"] fn f() { thread_local! { diff --git a/tests/ui/thread_local_initializer_can_be_made_const.rs b/tests/ui/thread_local_initializer_can_be_made_const.rs index 3f0159c58065c..eb336f0dd1913 100644 --- a/tests/ui/thread_local_initializer_can_be_made_const.rs +++ b/tests/ui/thread_local_initializer_can_be_made_const.rs @@ -1,6 +1,6 @@ #![warn(clippy::thread_local_initializer_can_be_made_const)] -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; fn main() { // lint and suggest const @@ -36,6 +36,37 @@ fn main() { } } +fn issue_12637() { + /// The set methods on LocalKey> and LocalKey> are + /// guaranteed to bypass the thread_local's initialization expression. + /// See rust-lang/rust#92122. Thus, = panic!() is a useful idiom for + /// forcing the use of set on each thread before it accesses the thread local in any other + /// manner. + thread_local! { + static STATE_12637_PANIC: Cell = panic!(); + } + STATE_12637_PANIC.set(9); + println!("{}", STATE_12637_PANIC.get()); + + thread_local! { + static STATE_12637_TODO: Cell = todo!(); + } + STATE_12637_TODO.set(9); + println!("{}", STATE_12637_TODO.get()); + + thread_local! { + static STATE_12637_UNIMPLEMENTED: Cell = unimplemented!(); + } + STATE_12637_UNIMPLEMENTED.set(9); + println!("{}", STATE_12637_UNIMPLEMENTED.get()); + + thread_local! { + static STATE_12637_UNREACHABLE: Cell = unreachable!(); + } + STATE_12637_UNREACHABLE.set(9); + println!("{}", STATE_12637_UNREACHABLE.get()); +} + #[clippy::msrv = "1.58"] fn f() { thread_local! { diff --git a/tests/ui/type_complexity.rs b/tests/ui/type_complexity.rs index b057dc4e89f26..be28ee2da0cd4 100644 --- a/tests/ui/type_complexity.rs +++ b/tests/ui/type_complexity.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::all)] #![allow(unused, clippy::needless_pass_by_value, clippy::vec_box, clippy::useless_vec)] #![feature(associated_type_defaults)] diff --git a/tests/ui/type_complexity.stderr b/tests/ui/type_complexity.stderr index bfbab8647e85c..9e27899e4f904 100644 --- a/tests/ui/type_complexity.stderr +++ b/tests/ui/type_complexity.stderr @@ -1,5 +1,5 @@ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:9:12 + --> tests/ui/type_complexity.rs:7:12 | LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,85 +8,85 @@ LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); = help: to override `-D warnings` add `#[allow(clippy::type_complexity)]` error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:12:12 + --> tests/ui/type_complexity.rs:10:12 | LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:16:8 + --> tests/ui/type_complexity.rs:14:8 | LL | f: Vec>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:20:11 + --> tests/ui/type_complexity.rs:18:11 | LL | struct Ts(Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:24:11 + --> tests/ui/type_complexity.rs:22:11 | LL | Tuple(Vec>>), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:26:17 + --> tests/ui/type_complexity.rs:24:17 | LL | Struct { f: Vec>> }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:31:14 + --> tests/ui/type_complexity.rs:29:14 | LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:33:30 + --> tests/ui/type_complexity.rs:31:30 | LL | fn impl_method(&self, p: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:38:14 + --> tests/ui/type_complexity.rs:36:14 | LL | const A: Vec>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:40:14 + --> tests/ui/type_complexity.rs:38:14 | LL | type B = Vec>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:42:25 + --> tests/ui/type_complexity.rs:40:25 | LL | fn method(&self, p: Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:44:29 + --> tests/ui/type_complexity.rs:42:29 | LL | fn def_method(&self, p: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:57:15 + --> tests/ui/type_complexity.rs:55:15 | LL | fn test1() -> Vec>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:62:14 + --> tests/ui/type_complexity.rs:60:14 | LL | fn test2(_x: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:66:13 + --> tests/ui/type_complexity.rs:64:13 | LL | let _y: Vec>> = vec![]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type_id_on_box_unfixable.rs b/tests/ui/type_id_on_box_unfixable.rs index f6d09834adb17..67e398e604b4b 100644 --- a/tests/ui/type_id_on_box_unfixable.rs +++ b/tests/ui/type_id_on_box_unfixable.rs @@ -19,7 +19,7 @@ where impl NormalTrait for T {} fn main() { - // (currently we don't look deeper than one level into the supertrait hierachy, but we probably + // (currently we don't look deeper than one level into the supertrait hierarchy, but we probably // could) let b: Box = Box::new(1); let _ = b.type_id(); diff --git a/triagebot.toml b/triagebot.toml index 901977da25be4..4d66b728b76df 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -20,7 +20,6 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ - "y21", "matthiaskrgr", "giraffate", "Centri3", diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index f59245e556cd2..c63edd5bf7098 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -415,7 +415,7 @@ let terms = searchStr.split(" "); let docsLowerCase = lint.docs.toLowerCase(); for (index = 0; index < terms.length; index++) { - // This is more likely and will therefor be checked first + // This is more likely and will therefore be checked first if (docsLowerCase.indexOf(terms[index]) !== -1) { continue; } From a032189503f9ead85caefd541fd7b8553e3c1dad Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Fri, 26 Apr 2024 17:26:35 +0800 Subject: [PATCH 10/58] Fix significant drop detection --- .../matches/significant_drop_in_scrutinee.rs | 56 +++++++++++-------- tests/ui/significant_drop_in_scrutinee.stderr | 10 +++- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 10c3203725a81..6b6ded1a337d6 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -119,41 +119,49 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { self.cx.typeck_results().expr_ty(ex) } - fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool { - !self.seen_types.insert(ty) + fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + self.seen_types.clear(); + self.has_sig_drop_attr_impl(cx, ty) } - fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr_impl(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { if let Some(adt) = ty.ty_adt_def() { if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 { return true; } } - match ty.kind() { - rustc_middle::ty::Adt(a, b) => { - for f in a.all_fields() { - let ty = f.ty(cx.tcx, b); - if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) { - return true; - } - } + if !self.seen_types.insert(ty) { + return false; + } - for generic_arg in *b { - if let GenericArgKind::Type(ty) = generic_arg.unpack() { - if self.has_sig_drop_attr(cx, ty) { - return true; - } - } - } - false + let result = match ty.kind() { + rustc_middle::ty::Adt(adt, args) => { + // if some field has significant drop, + adt.all_fields() + .map(|field| field.ty(cx.tcx, args)) + .any(|ty| self.has_sig_drop_attr(cx, ty)) + // or if there is no generic lifetime and.. + // (to avoid false positive on `Ref<'a, MutexGuard>`) + || (args + .iter() + .all(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + // some generic parameter has significant drop + // (to avoid false negative on `Box>`) + && args + .iter() + .filter_map(|arg| match arg.unpack() { + GenericArgKind::Type(ty) => Some(ty), + _ => None, + }) + .any(|ty| self.has_sig_drop_attr(cx, ty))) }, - rustc_middle::ty::Array(ty, _) - | rustc_middle::ty::RawPtr(ty, _) - | rustc_middle::ty::Ref(_, ty, _) - | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr(cx, ty)), + rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), _ => false, - } + }; + + result } } diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index 7d5b1acc7f00a..1f19eff063162 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -28,6 +28,9 @@ LL | match s.lock_m().get_the_value() { LL | println!("{}", s.lock_m().get_the_value()); | ---------- another value with significant `Drop` created here ... +LL | println!("{}", s.lock_m().get_the_value()); + | ---------- another value with significant `Drop` created here +... LL | } | - temporary lives until here | @@ -47,6 +50,9 @@ LL | match s.lock_m_m().get_the_value() { LL | println!("{}", s.lock_m().get_the_value()); | ---------- another value with significant `Drop` created here ... +LL | println!("{}", s.lock_m().get_the_value()); + | ---------- another value with significant `Drop` created here +... LL | } | - temporary lives until here | @@ -360,7 +366,7 @@ LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | _ => println!("Value is {}", s.lock().deref()), - | ---------------- another value with significant `Drop` created here + | -------- another value with significant `Drop` created here LL | }; | - temporary lives until here | @@ -378,7 +384,7 @@ LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | matcher => println!("Value is {}", s.lock().deref()), - | ---------------- another value with significant `Drop` created here + | -------- another value with significant `Drop` created here LL | _ => println!("Value was not a match"), LL | }; | - temporary lives until here From 8d8c0bfb0e2d50ca2a7a4fbbd10cb2e62a319539 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Thu, 2 May 2024 11:55:11 -0700 Subject: [PATCH 11/58] useless_attribute: allow clippy::disallowed_types Closes https://github.com/rust-lang/rust-clippy/issues/12753 --- clippy_lints/src/attrs/useless_attribute.rs | 1 + .../ui-toml/toml_disallowed_types/conf_disallowed_types.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 7575f502a7c29..7515ee9bbc872 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -35,6 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) | "unsafe_removed_from_name" | "module_name_repetitions" | "single_component_path_imports" + | "disallowed_types" ) }) { diff --git a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs index 7f28efd676f2c..f02bd07cfe7bf 100644 --- a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs +++ b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs @@ -40,3 +40,9 @@ fn main() { let _ = HashMap; let _: usize = 64_usize; } + +mod useless_attribute { + // Regression test for https://github.com/rust-lang/rust-clippy/issues/12753 + #[allow(clippy::disallowed_types)] + use std::collections::HashMap; +} From 17d2ab8f6247ea325bdc7e1c9943a0dcaf31568d Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Thu, 2 May 2024 11:56:05 -0700 Subject: [PATCH 12/58] useless_attribute: allow unused_braces --- clippy_lints/src/attrs/useless_attribute.rs | 1 + tests/ui/useless_attribute.fixed | 1 + tests/ui/useless_attribute.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 7515ee9bbc872..40572731bb141 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -25,6 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) || is_word(lint, sym!(unused_import_braces)) + || is_word(lint, sym!(unused_braces)) || extract_clippy_lint(lint).map_or(false, |s| { matches!( s.as_str(), diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index 81759086f79e4..cb1c03464040c 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -86,6 +86,7 @@ mod module { #[rustfmt::skip] #[allow(unused_import_braces)] +#[allow(unused_braces)] use module::{Struct}; fn main() { diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 59a9dcf093bca..44a52cba043ed 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -86,6 +86,7 @@ mod module { #[rustfmt::skip] #[allow(unused_import_braces)] +#[allow(unused_braces)] use module::{Struct}; fn main() { From e9761bdc015430b818ee771b2acf00a00b40bd97 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Thu, 2 May 2024 11:56:38 -0700 Subject: [PATCH 13/58] useless_attribute: allow dead_code Closes https://github.com/rust-lang/rust-clippy/issues/4467 --- clippy_lints/src/attrs/useless_attribute.rs | 1 + tests/ui/useless_attribute.fixed | 4 ++++ tests/ui/useless_attribute.rs | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 40572731bb141..720e4e7932d03 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -26,6 +26,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) || is_word(lint, sym!(unused)) || is_word(lint, sym!(unused_import_braces)) || is_word(lint, sym!(unused_braces)) + || is_word(lint, sym!(dead_code)) || extract_clippy_lint(lint).map_or(false, |s| { matches!( s.as_str(), diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index cb1c03464040c..5fafc05cddeb9 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -92,3 +92,7 @@ use module::{Struct}; fn main() { test_indented_attr(); } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 +#[allow(dead_code)] +use std::collections as puppy_doggy; diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 44a52cba043ed..c9de7677ff8c0 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -92,3 +92,7 @@ use module::{Struct}; fn main() { test_indented_attr(); } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 +#[allow(dead_code)] +use std::collections as puppy_doggy; From 96e69d9b43f1de81ead394c76b5552ab4daf88f1 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Thu, 2 May 2024 11:57:06 -0700 Subject: [PATCH 14/58] useless_attribute: allow hidden_glob_reexports Closes https://github.com/rust-lang/rust-clippy/issues/11595 --- clippy_lints/src/attrs/useless_attribute.rs | 3 ++- tests/ui/useless_attribute.fixed | 21 +++++++++++++++++++++ tests/ui/useless_attribute.rs | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 720e4e7932d03..00f324e4de549 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -26,7 +26,8 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) || is_word(lint, sym!(unused)) || is_word(lint, sym!(unused_import_braces)) || is_word(lint, sym!(unused_braces)) - || is_word(lint, sym!(dead_code)) + || is_word(lint, sym::dead_code) + || is_word(lint, sym!(hidden_glob_reexports)) || extract_clippy_lint(lint).map_or(false, |s| { matches!( s.as_str(), diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index 5fafc05cddeb9..401c384c39ca8 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -96,3 +96,24 @@ fn main() { // Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 #[allow(dead_code)] use std::collections as puppy_doggy; + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/11595 +pub mod hidden_glob_reexports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyCoolTypeInternal; + pub use MyCoolTypeInternal as MyCoolType; + } + + mod my_uncool_type { + pub(crate) struct MyUncoolType; + } + + // This exports `MyCoolType`. + pub use my_prelude::*; + + // This hides `my_prelude::MyCoolType`. + #[allow(hidden_glob_reexports)] + use my_uncool_type::MyUncoolType as MyCoolType; +} diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index c9de7677ff8c0..6629942ff5e49 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -96,3 +96,24 @@ fn main() { // Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 #[allow(dead_code)] use std::collections as puppy_doggy; + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/11595 +pub mod hidden_glob_reexports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyCoolTypeInternal; + pub use MyCoolTypeInternal as MyCoolType; + } + + mod my_uncool_type { + pub(crate) struct MyUncoolType; + } + + // This exports `MyCoolType`. + pub use my_prelude::*; + + // This hides `my_prelude::MyCoolType`. + #[allow(hidden_glob_reexports)] + use my_uncool_type::MyUncoolType as MyCoolType; +} From db0cbbacb774e7c645c740b27914aec313ed0a7e Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Thu, 2 May 2024 11:57:27 -0700 Subject: [PATCH 15/58] useless_attribute: allow ambiguous_glob_exports Closes https://github.com/rust-lang/rust-clippy/issues/10878 --- clippy_lints/src/attrs/useless_attribute.rs | 1 + tests/ui/useless_attribute.fixed | 17 +++++++++++++++++ tests/ui/useless_attribute.rs | 17 +++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 00f324e4de549..f6f4d78044041 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -28,6 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) || is_word(lint, sym!(unused_braces)) || is_word(lint, sym::dead_code) || is_word(lint, sym!(hidden_glob_reexports)) + || is_word(lint, sym!(ambiguous_glob_reexports)) || extract_clippy_lint(lint).map_or(false, |s| { matches!( s.as_str(), diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index 401c384c39ca8..231fc0a892ad1 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -117,3 +117,20 @@ pub mod hidden_glob_reexports { #[allow(hidden_glob_reexports)] use my_uncool_type::MyUncoolType as MyCoolType; } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/10878 +pub mod ambiguous_glob_exports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyType; + } + + mod my_type { + pub struct MyType; + } + + #[allow(ambiguous_glob_reexports)] + pub use my_prelude::*; + pub use my_type::*; +} diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 6629942ff5e49..8dfcd2110a4bb 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -117,3 +117,20 @@ pub mod hidden_glob_reexports { #[allow(hidden_glob_reexports)] use my_uncool_type::MyUncoolType as MyCoolType; } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/10878 +pub mod ambiguous_glob_exports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyType; + } + + mod my_type { + pub struct MyType; + } + + #[allow(ambiguous_glob_reexports)] + pub use my_prelude::*; + pub use my_type::*; +} From 68a6cbfc6c28a7d91d250230262657b5e796c5ae Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Sun, 28 Apr 2024 00:39:32 -0400 Subject: [PATCH 16/58] Update clippy tests for stable exclusive_range --- tests/ui/almost_complete_range.fixed | 1 - tests/ui/almost_complete_range.rs | 1 - tests/ui/almost_complete_range.stderr | 54 +++++++++++++-------------- tests/ui/manual_range_patterns.fixed | 1 - tests/ui/manual_range_patterns.rs | 1 - tests/ui/manual_range_patterns.stderr | 38 +++++++++---------- tests/ui/match_overlapping_arm.rs | 1 - tests/ui/match_overlapping_arm.stderr | 32 ++++++++-------- tests/ui/match_wild_err_arm.rs | 1 - tests/ui/match_wild_err_arm.stderr | 8 ++-- 10 files changed, 66 insertions(+), 72 deletions(-) diff --git a/tests/ui/almost_complete_range.fixed b/tests/ui/almost_complete_range.fixed index 21caeb153e77d..6c2b2f117437d 100644 --- a/tests/ui/almost_complete_range.fixed +++ b/tests/ui/almost_complete_range.fixed @@ -1,7 +1,6 @@ //@edition:2018 //@aux-build:proc_macros.rs -#![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_range)] #![allow(ellipsis_inclusive_range_patterns)] diff --git a/tests/ui/almost_complete_range.rs b/tests/ui/almost_complete_range.rs index 556110a5c8aae..813668a530966 100644 --- a/tests/ui/almost_complete_range.rs +++ b/tests/ui/almost_complete_range.rs @@ -1,7 +1,6 @@ //@edition:2018 //@aux-build:proc_macros.rs -#![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_range)] #![allow(ellipsis_inclusive_range_patterns)] diff --git a/tests/ui/almost_complete_range.stderr b/tests/ui/almost_complete_range.stderr index 0195e59226d51..bfc2beb07d857 100644 --- a/tests/ui/almost_complete_range.stderr +++ b/tests/ui/almost_complete_range.stderr @@ -1,5 +1,5 @@ error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:18:17 + --> tests/ui/almost_complete_range.rs:17:17 | LL | let _ = ('a') ..'z'; | ^^^^^^--^^^ @@ -10,7 +10,7 @@ LL | let _ = ('a') ..'z'; = help: to override `-D warnings` add `#[allow(clippy::almost_complete_range)]` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:19:17 + --> tests/ui/almost_complete_range.rs:18:17 | LL | let _ = 'A' .. ('Z'); | ^^^^--^^^^^^ @@ -18,7 +18,7 @@ LL | let _ = 'A' .. ('Z'); | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:20:17 + --> tests/ui/almost_complete_range.rs:19:17 | LL | let _ = ((('0'))) .. ('9'); | ^^^^^^^^^^--^^^^^^ @@ -26,7 +26,7 @@ LL | let _ = ((('0'))) .. ('9'); | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:27:13 + --> tests/ui/almost_complete_range.rs:26:13 | LL | let _ = (b'a')..(b'z'); | ^^^^^^--^^^^^^ @@ -34,7 +34,7 @@ LL | let _ = (b'a')..(b'z'); | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:28:13 + --> tests/ui/almost_complete_range.rs:27:13 | LL | let _ = b'A'..b'Z'; | ^^^^--^^^^ @@ -42,7 +42,7 @@ LL | let _ = b'A'..b'Z'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:29:13 + --> tests/ui/almost_complete_range.rs:28:13 | LL | let _ = b'0'..b'9'; | ^^^^--^^^^ @@ -50,7 +50,7 @@ LL | let _ = b'0'..b'9'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:35:13 + --> tests/ui/almost_complete_range.rs:34:13 | LL | let _ = inline!('a')..'z'; | ^^^^^^^^^^^^--^^^ @@ -58,7 +58,7 @@ LL | let _ = inline!('a')..'z'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:36:13 + --> tests/ui/almost_complete_range.rs:35:13 | LL | let _ = inline!('A')..'Z'; | ^^^^^^^^^^^^--^^^ @@ -66,7 +66,7 @@ LL | let _ = inline!('A')..'Z'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:37:13 + --> tests/ui/almost_complete_range.rs:36:13 | LL | let _ = inline!('0')..'9'; | ^^^^^^^^^^^^--^^^ @@ -74,7 +74,7 @@ LL | let _ = inline!('0')..'9'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:40:9 + --> tests/ui/almost_complete_range.rs:39:9 | LL | b'a'..b'z' if true => 1, | ^^^^--^^^^ @@ -82,7 +82,7 @@ LL | b'a'..b'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:41:9 + --> tests/ui/almost_complete_range.rs:40:9 | LL | b'A'..b'Z' if true => 2, | ^^^^--^^^^ @@ -90,7 +90,7 @@ LL | b'A'..b'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:42:9 + --> tests/ui/almost_complete_range.rs:41:9 | LL | b'0'..b'9' if true => 3, | ^^^^--^^^^ @@ -98,7 +98,7 @@ LL | b'0'..b'9' if true => 3, | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:50:9 + --> tests/ui/almost_complete_range.rs:49:9 | LL | 'a'..'z' if true => 1, | ^^^--^^^ @@ -106,7 +106,7 @@ LL | 'a'..'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:51:9 + --> tests/ui/almost_complete_range.rs:50:9 | LL | 'A'..'Z' if true => 2, | ^^^--^^^ @@ -114,7 +114,7 @@ LL | 'A'..'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:52:9 + --> tests/ui/almost_complete_range.rs:51:9 | LL | '0'..'9' if true => 3, | ^^^--^^^ @@ -122,7 +122,7 @@ LL | '0'..'9' if true => 3, | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:65:17 + --> tests/ui/almost_complete_range.rs:64:17 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -132,7 +132,7 @@ LL | let _ = 'a'..'z'; = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:66:17 + --> tests/ui/almost_complete_range.rs:65:17 | LL | let _ = 'A'..'Z'; | ^^^--^^^ @@ -142,7 +142,7 @@ LL | let _ = 'A'..'Z'; = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:67:17 + --> tests/ui/almost_complete_range.rs:66:17 | LL | let _ = '0'..'9'; | ^^^--^^^ @@ -152,7 +152,7 @@ LL | let _ = '0'..'9'; = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:74:9 + --> tests/ui/almost_complete_range.rs:73:9 | LL | 'a'..'z' => 1, | ^^^--^^^ @@ -160,7 +160,7 @@ LL | 'a'..'z' => 1, | help: use an inclusive range: `...` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:75:9 + --> tests/ui/almost_complete_range.rs:74:9 | LL | 'A'..'Z' => 2, | ^^^--^^^ @@ -168,7 +168,7 @@ LL | 'A'..'Z' => 2, | help: use an inclusive range: `...` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:76:9 + --> tests/ui/almost_complete_range.rs:75:9 | LL | '0'..'9' => 3, | ^^^--^^^ @@ -176,7 +176,7 @@ LL | '0'..'9' => 3, | help: use an inclusive range: `...` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:83:13 + --> tests/ui/almost_complete_range.rs:82:13 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -184,7 +184,7 @@ LL | let _ = 'a'..'z'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:84:13 + --> tests/ui/almost_complete_range.rs:83:13 | LL | let _ = 'A'..'Z'; | ^^^--^^^ @@ -192,7 +192,7 @@ LL | let _ = 'A'..'Z'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:85:13 + --> tests/ui/almost_complete_range.rs:84:13 | LL | let _ = '0'..'9'; | ^^^--^^^ @@ -200,7 +200,7 @@ LL | let _ = '0'..'9'; | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:87:9 + --> tests/ui/almost_complete_range.rs:86:9 | LL | 'a'..'z' => 1, | ^^^--^^^ @@ -208,7 +208,7 @@ LL | 'a'..'z' => 1, | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:88:9 + --> tests/ui/almost_complete_range.rs:87:9 | LL | 'A'..'Z' => 1, | ^^^--^^^ @@ -216,7 +216,7 @@ LL | 'A'..'Z' => 1, | help: use an inclusive range: `..=` error: almost complete ascii range - --> tests/ui/almost_complete_range.rs:89:9 + --> tests/ui/almost_complete_range.rs:88:9 | LL | '0'..'9' => 3, | ^^^--^^^ diff --git a/tests/ui/manual_range_patterns.fixed b/tests/ui/manual_range_patterns.fixed index e9f6fbcc3fc8c..f1b99637afdba 100644 --- a/tests/ui/manual_range_patterns.fixed +++ b/tests/ui/manual_range_patterns.fixed @@ -1,7 +1,6 @@ #![allow(unused)] #![allow(non_contiguous_range_endpoints)] #![warn(clippy::manual_range_patterns)] -#![feature(exclusive_range_pattern)] fn main() { let f = 6; diff --git a/tests/ui/manual_range_patterns.rs b/tests/ui/manual_range_patterns.rs index d525aaa24ad1b..869ffbe80b973 100644 --- a/tests/ui/manual_range_patterns.rs +++ b/tests/ui/manual_range_patterns.rs @@ -1,7 +1,6 @@ #![allow(unused)] #![allow(non_contiguous_range_endpoints)] #![warn(clippy::manual_range_patterns)] -#![feature(exclusive_range_pattern)] fn main() { let f = 6; diff --git a/tests/ui/manual_range_patterns.stderr b/tests/ui/manual_range_patterns.stderr index af9256aeea390..7c19fdd475f19 100644 --- a/tests/ui/manual_range_patterns.stderr +++ b/tests/ui/manual_range_patterns.stderr @@ -1,5 +1,5 @@ error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:9:25 + --> tests/ui/manual_range_patterns.rs:8:25 | LL | let _ = matches!(f, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` @@ -8,109 +8,109 @@ LL | let _ = matches!(f, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10); = help: to override `-D warnings` add `#[allow(clippy::manual_range_patterns)]` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:10:25 + --> tests/ui/manual_range_patterns.rs:9:25 | LL | let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 7 | 8 | 10); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:17:25 + --> tests/ui/manual_range_patterns.rs:16:25 | LL | let _ = matches!(f, 1 | (2..=4)); | ^^^^^^^^^^^ help: try: `1..=4` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:18:25 + --> tests/ui/manual_range_patterns.rs:17:25 | LL | let _ = matches!(f, 1 | (2..4)); | ^^^^^^^^^^ help: try: `1..4` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:19:25 + --> tests/ui/manual_range_patterns.rs:18:25 | LL | let _ = matches!(f, (1..=10) | (2..=13) | (14..=48324728) | 48324729); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=48324729` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:20:25 + --> tests/ui/manual_range_patterns.rs:19:25 | LL | let _ = matches!(f, 0 | (1..=10) | 48324730 | (2..=13) | (14..=48324728) | 48324729); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0..=48324730` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:21:25 + --> tests/ui/manual_range_patterns.rs:20:25 | LL | let _ = matches!(f, 0..=1 | 0..=2 | 0..=3); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `0..=3` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:24:9 + --> tests/ui/manual_range_patterns.rs:23:9 | LL | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 => true, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:27:25 + --> tests/ui/manual_range_patterns.rs:26:25 | LL | let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1 | 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-5..=3` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:29:25 + --> tests/ui/manual_range_patterns.rs:28:25 | LL | let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1_000_001..=1_000_001` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:32:17 + --> tests/ui/manual_range_patterns.rs:31:17 | LL | matches!(f, 0x00 | 0x01 | 0x02 | 0x03); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x03` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:33:17 + --> tests/ui/manual_range_patterns.rs:32:17 | LL | matches!(f, 0x00..=0x05 | 0x06 | 0x07); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x07` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:34:17 + --> tests/ui/manual_range_patterns.rs:33:17 | LL | matches!(f, -0x09 | -0x08 | -0x07..=0x00); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-0x09..=0x00` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:36:17 + --> tests/ui/manual_range_patterns.rs:35:17 | LL | matches!(f, 0..5 | 5); | ^^^^^^^^ help: try: `0..=5` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:37:17 + --> tests/ui/manual_range_patterns.rs:36:17 | LL | matches!(f, 0 | 1..5); | ^^^^^^^^ help: try: `0..5` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:39:17 + --> tests/ui/manual_range_patterns.rs:38:17 | LL | matches!(f, 0..=5 | 6..10); | ^^^^^^^^^^^^^ help: try: `0..10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:40:17 + --> tests/ui/manual_range_patterns.rs:39:17 | LL | matches!(f, 0..5 | 5..=10); | ^^^^^^^^^^^^^ help: try: `0..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:41:17 + --> tests/ui/manual_range_patterns.rs:40:17 | LL | matches!(f, 5..=10 | 0..5); | ^^^^^^^^^^^^^ help: try: `0..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:45:26 + --> tests/ui/manual_range_patterns.rs:44:26 | LL | matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index c2c2f28392d77..4457ae73da2f0 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,4 +1,3 @@ -#![feature(exclusive_range_pattern)] #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_if)] diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index 1a6c5746c3dd2..65092ffbb5558 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -1,11 +1,11 @@ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:12:9 + --> tests/ui/match_overlapping_arm.rs:11:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:14:9 + --> tests/ui/match_overlapping_arm.rs:13:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ @@ -13,85 +13,85 @@ LL | 0..=11 => println!("0..=11"), = help: to override `-D warnings` add `#[allow(clippy::match_overlapping_arm)]` error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:19:9 + --> tests/ui/match_overlapping_arm.rs:18:9 | LL | 0..=5 => println!("0..=5"), | ^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:22:9 + --> tests/ui/match_overlapping_arm.rs:21:9 | LL | FOO..=11 => println!("FOO..=11"), | ^^^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:57:9 + --> tests/ui/match_overlapping_arm.rs:56:9 | LL | 0..11 => println!("0..11"), | ^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:59:9 + --> tests/ui/match_overlapping_arm.rs:58:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:83:9 + --> tests/ui/match_overlapping_arm.rs:82:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:82:9 + --> tests/ui/match_overlapping_arm.rs:81:9 | LL | 5..14 => println!("5..14"), | ^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:89:9 + --> tests/ui/match_overlapping_arm.rs:88:9 | LL | 0..7 => println!("0..7"), | ^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:91:9 + --> tests/ui/match_overlapping_arm.rs:90:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:102:9 + --> tests/ui/match_overlapping_arm.rs:101:9 | LL | ..=23 => println!("..=23"), | ^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:104:9 + --> tests/ui/match_overlapping_arm.rs:103:9 | LL | ..26 => println!("..26"), | ^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:112:9 + --> tests/ui/match_overlapping_arm.rs:111:9 | LL | 21..=30 => (), | ^^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:114:9 + --> tests/ui/match_overlapping_arm.rs:113:9 | LL | 21..=40 => (), | ^^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:127:9 + --> tests/ui/match_overlapping_arm.rs:126:9 | LL | 0..=0x0000_0000_0000_00ff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:129:9 + --> tests/ui/match_overlapping_arm.rs:128:9 | LL | 0..=0x0000_0000_0000_ffff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_wild_err_arm.rs b/tests/ui/match_wild_err_arm.rs index 7bdd75d7f4639..8e670ce5bda2c 100644 --- a/tests/ui/match_wild_err_arm.rs +++ b/tests/ui/match_wild_err_arm.rs @@ -1,4 +1,3 @@ -#![feature(exclusive_range_pattern)] #![allow(clippy::match_same_arms, dead_code)] #![warn(clippy::match_wild_err_arm)] diff --git a/tests/ui/match_wild_err_arm.stderr b/tests/ui/match_wild_err_arm.stderr index 3145665a341e8..f98065d9a591f 100644 --- a/tests/ui/match_wild_err_arm.stderr +++ b/tests/ui/match_wild_err_arm.stderr @@ -1,5 +1,5 @@ error: `Err(_)` matches all errors - --> tests/ui/match_wild_err_arm.rs:24:9 + --> tests/ui/match_wild_err_arm.rs:23:9 | LL | Err(_) => panic!("err"), | ^^^^^^ @@ -9,7 +9,7 @@ LL | Err(_) => panic!("err"), = help: to override `-D warnings` add `#[allow(clippy::match_wild_err_arm)]` error: `Err(_)` matches all errors - --> tests/ui/match_wild_err_arm.rs:32:9 + --> tests/ui/match_wild_err_arm.rs:31:9 | LL | Err(_) => panic!(), | ^^^^^^ @@ -17,7 +17,7 @@ LL | Err(_) => panic!(), = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_)` matches all errors - --> tests/ui/match_wild_err_arm.rs:40:9 + --> tests/ui/match_wild_err_arm.rs:39:9 | LL | Err(_) => { | ^^^^^^ @@ -25,7 +25,7 @@ LL | Err(_) => { = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_e)` matches all errors - --> tests/ui/match_wild_err_arm.rs:50:9 + --> tests/ui/match_wild_err_arm.rs:49:9 | LL | Err(_e) => panic!(), | ^^^^^^^ From a0d562a183eca869004308466f492e3b18f9cc13 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 2 May 2024 13:51:10 +0200 Subject: [PATCH 17/58] Type safe CLI implementation for clippy-dev Use the derive feature of `clap` to generate CLI of clippy-dev. Adding new commands will be easier in the future and we get better compile time checking through exhaustive matching. --- clippy_dev/Cargo.toml | 3 +- clippy_dev/src/main.rs | 570 +++++++++++++++------------------ clippy_dev/src/new_lint.rs | 14 +- clippy_dev/src/serve.rs | 2 +- clippy_dev/src/update_lints.rs | 4 +- 5 files changed, 265 insertions(+), 328 deletions(-) diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 42a953039b1c0..9cfa5b7630f35 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "clippy_dev" +description = "Clippy developer tooling" version = "0.0.1" edition = "2021" [dependencies] aho-corasick = "1.0" -clap = "4.1.4" +clap = { version = "4.1.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" opener = "0.6" diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 397a0e990829c..366b52b25dfcf 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -2,350 +2,292 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{Arg, ArgAction, ArgMatches, Command}; +use clap::{Args, Parser, Subcommand}; use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints}; -use indoc::indoc; use std::convert::Infallible; fn main() { - let matches = get_clap_config(); + let dev = Dev::parse(); - match matches.subcommand() { - Some(("bless", _)) => { + match dev.command { + DevCommand::Bless => { eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run"); }, - Some(("dogfood", matches)) => { - dogfood::dogfood( - matches.get_flag("fix"), - matches.get_flag("allow-dirty"), - matches.get_flag("allow-staged"), - ); - }, - Some(("fmt", matches)) => { - fmt::run(matches.get_flag("check"), matches.get_flag("verbose")); - }, - Some(("update_lints", matches)) => { - if matches.get_flag("print-only") { + DevCommand::Dogfood { + fix, + allow_dirty, + allow_staged, + } => dogfood::dogfood(fix, allow_dirty, allow_staged), + DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), + DevCommand::UpdateLints { print_only, check } => { + if print_only { update_lints::print_lints(); - } else if matches.get_flag("check") { + } else if check { update_lints::update(update_lints::UpdateMode::Check); } else { update_lints::update(update_lints::UpdateMode::Change); } }, - Some(("new_lint", matches)) => { - match new_lint::create( - matches.get_one::("pass").unwrap(), - matches.get_one::("name"), - matches.get_one::("category").map(String::as_str), - matches.get_one::("type").map(String::as_str), - matches.get_flag("msrv"), - ) { - Ok(()) => update_lints::update(update_lints::UpdateMode::Change), - Err(e) => eprintln!("Unable to create lint: {e}"), - } + DevCommand::NewLint { + pass, + name, + category, + r#type, + msrv, + } => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) { + Ok(()) => update_lints::update(update_lints::UpdateMode::Change), + Err(e) => eprintln!("Unable to create lint: {e}"), }, - Some(("setup", sub_command)) => match sub_command.subcommand() { - Some(("git-hook", matches)) => { - if matches.get_flag("remove") { - setup::git_hook::remove_hook(); + DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { + SetupSubcommand::Intellij { remove, repo_path } => { + if remove { + setup::intellij::remove_rustc_src(); } else { - setup::git_hook::install_hook(matches.get_flag("force-override")); + setup::intellij::setup_rustc_src(&repo_path); } }, - Some(("intellij", matches)) => { - if matches.get_flag("remove") { - setup::intellij::remove_rustc_src(); + SetupSubcommand::GitHook { remove, force_override } => { + if remove { + setup::git_hook::remove_hook(); } else { - setup::intellij::setup_rustc_src( - matches - .get_one::("rustc-repo-path") - .expect("this field is mandatory and therefore always valid"), - ); + setup::git_hook::install_hook(force_override); } }, - Some(("toolchain", matches)) => { - setup::toolchain::create( - matches.get_flag("force"), - matches.get_flag("release"), - matches.get_one::("name").unwrap(), - ); - }, - Some(("vscode-tasks", matches)) => { - if matches.get_flag("remove") { + SetupSubcommand::Toolchain { force, release, name } => setup::toolchain::create(force, release, &name), + SetupSubcommand::VscodeTasks { remove, force_override } => { + if remove { setup::vscode::remove_tasks(); } else { - setup::vscode::install_tasks(matches.get_flag("force-override")); + setup::vscode::install_tasks(force_override); } }, - _ => {}, - }, - Some(("remove", sub_command)) => match sub_command.subcommand() { - Some(("git-hook", _)) => setup::git_hook::remove_hook(), - Some(("intellij", _)) => setup::intellij::remove_rustc_src(), - Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(), - _ => {}, - }, - Some(("serve", matches)) => { - let port = *matches.get_one::("port").unwrap(); - let lint = matches.get_one::("lint"); - serve::run(port, lint); }, - Some(("lint", matches)) => { - let path = matches.get_one::("path").unwrap(); - let args = matches.get_many::("args").into_iter().flatten(); - lint::run(path, args); + DevCommand::Remove(RemoveCommand { subcommand }) => match subcommand { + RemoveSubcommand::Intellij => setup::intellij::remove_rustc_src(), + RemoveSubcommand::GitHook => setup::git_hook::remove_hook(), + RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(), }, - Some(("rename_lint", matches)) => { - let old_name = matches.get_one::("old_name").unwrap(); - let new_name = matches.get_one::("new_name").unwrap_or(old_name); - let uplift = matches.get_flag("uplift"); - update_lints::rename(old_name, new_name, uplift); - }, - Some(("deprecate", matches)) => { - let name = matches.get_one::("name").unwrap(); - let reason = matches.get_one("reason"); - update_lints::deprecate(name, reason); - }, - _ => {}, + DevCommand::Serve { port, lint } => serve::run(port, lint), + DevCommand::Lint { path, args } => lint::run(&path, args.iter()), + DevCommand::RenameLint { + old_name, + new_name, + uplift, + } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), + DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()), } } -fn get_clap_config() -> ArgMatches { - Command::new("Clippy developer tooling") - .arg_required_else_help(true) - .subcommands([ - Command::new("bless").about("bless the test output changes").arg( - Arg::new("ignore-timestamp") - .long("ignore-timestamp") - .action(ArgAction::SetTrue) - .help("Include files updated before clippy was built"), - ), - Command::new("dogfood").about("Runs the dogfood test").args([ - Arg::new("fix") - .long("fix") - .action(ArgAction::SetTrue) - .help("Apply the suggestions when possible"), - Arg::new("allow-dirty") - .long("allow-dirty") - .action(ArgAction::SetTrue) - .help("Fix code even if the working directory has changes") - .requires("fix"), - Arg::new("allow-staged") - .long("allow-staged") - .action(ArgAction::SetTrue) - .help("Fix code even if the working directory has staged changes") - .requires("fix"), - ]), - Command::new("fmt") - .about("Run rustfmt on all projects and tests") - .args([ - Arg::new("check") - .long("check") - .action(ArgAction::SetTrue) - .help("Use the rustfmt --check option"), - Arg::new("verbose") - .short('v') - .long("verbose") - .action(ArgAction::SetTrue) - .help("Echo commands run"), - ]), - Command::new("update_lints") - .about("Updates lint registration and information from the source code") - .long_about( - "Makes sure that:\n \ - * the lint count in README.md is correct\n \ - * the changelog contains markdown link references at the bottom\n \ - * all lint groups include the correct lints\n \ - * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ - * all lints are registered in the lint store", - ) - .args([ - Arg::new("print-only") - .long("print-only") - .action(ArgAction::SetTrue) - .help( - "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", - ), - Arg::new("check") - .long("check") - .action(ArgAction::SetTrue) - .help("Checks that `cargo dev update_lints` has been run. Used on CI."), - ]), - Command::new("new_lint") - .about("Create new lint and run `cargo dev update_lints`") - .args([ - Arg::new("pass") - .short('p') - .long("pass") - .help("Specify whether the lint runs during the early or late pass") - .value_parser(["early", "late"]) - .conflicts_with("type") - .default_value("late"), - Arg::new("name") - .short('n') - .long("name") - .help("Name of the new lint in snake case, ex: fn_too_long") - .required(true) - .value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))), - Arg::new("category") - .short('c') - .long("category") - .help("What category the lint belongs to") - .default_value("nursery") - .value_parser([ - "style", - "correctness", - "suspicious", - "complexity", - "perf", - "pedantic", - "restriction", - "cargo", - "nursery", - "internal", - ]), - Arg::new("type").long("type").help("What directory the lint belongs in"), - Arg::new("msrv") - .long("msrv") - .action(ArgAction::SetTrue) - .help("Add MSRV config code to the lint"), - ]), - Command::new("setup") - .about("Support for setting up your personal development environment") - .arg_required_else_help(true) - .subcommands([ - Command::new("git-hook") - .about("Add a pre-commit git hook that formats your code to make it look pretty") - .args([ - Arg::new("remove") - .long("remove") - .action(ArgAction::SetTrue) - .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"), - Arg::new("force-override") - .long("force-override") - .short('f') - .action(ArgAction::SetTrue) - .help("Forces the override of an existing git pre-commit hook"), - ]), - Command::new("intellij") - .about("Alter dependencies so Intellij Rust can find rustc internals") - .args([ - Arg::new("remove") - .long("remove") - .action(ArgAction::SetTrue) - .help("Remove the dependencies added with 'cargo dev setup intellij'"), - Arg::new("rustc-repo-path") - .long("repo-path") - .short('r') - .help("The path to a rustc repo that will be used for setting the dependencies") - .value_name("path") - .conflicts_with("remove") - .required(true), - ]), - Command::new("toolchain") - .about("Install a rustup toolchain pointing to the local clippy build") - .args([ - Arg::new("force") - .long("force") - .short('f') - .action(ArgAction::SetTrue) - .help("Override an existing toolchain"), - Arg::new("release") - .long("release") - .short('r') - .action(ArgAction::SetTrue) - .help("Point to --release clippy binaries"), - Arg::new("name") - .long("name") - .default_value("clippy") - .help("The name of the created toolchain"), - ]), - Command::new("vscode-tasks") - .about("Add several tasks to vscode for formatting, validation and testing") - .args([ - Arg::new("remove") - .long("remove") - .action(ArgAction::SetTrue) - .help("Remove the tasks added with 'cargo dev setup vscode-tasks'"), - Arg::new("force-override") - .long("force-override") - .short('f') - .action(ArgAction::SetTrue) - .help("Forces the override of existing vscode tasks"), - ]), - ]), - Command::new("remove") - .about("Support for undoing changes done by the setup command") - .arg_required_else_help(true) - .subcommands([ - Command::new("git-hook").about("Remove any existing pre-commit git hook"), - Command::new("vscode-tasks").about("Remove any existing vscode tasks"), - Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"), - ]), - Command::new("serve") - .about("Launch a local 'ALL the Clippy Lints' website in a browser") - .args([ - Arg::new("port") - .long("port") - .short('p') - .help("Local port for the http server") - .default_value("8000") - .value_parser(clap::value_parser!(u16)), - Arg::new("lint").help("Which lint's page to load initially (optional)"), - ]), - Command::new("lint") - .about("Manually run clippy on a file or package") - .after_help(indoc! {" - EXAMPLES - Lint a single file: - cargo dev lint tests/ui/attrs.rs +#[derive(Parser)] +#[command(name = "dev", about)] +struct Dev { + #[command(subcommand)] + command: DevCommand, +} - Lint a package directory: - cargo dev lint tests/ui-cargo/wildcard_dependencies/fail - cargo dev lint ~/my-project +#[derive(Subcommand)] +enum DevCommand { + /// Bless the test output changes + Bless, + /// Runs the dogfood test + Dogfood { + #[arg(long)] + /// Apply the suggestions when possible + fix: bool, + #[arg(long, requires = "fix")] + /// Fix code even if the working directory has changes + allow_dirty: bool, + #[arg(long, requires = "fix")] + /// Fix code even if the working directory has staged changes + allow_staged: bool, + }, + /// Run rustfmt on all projects and tests + Fmt { + #[arg(long)] + /// Use the rustfmt --check option + check: bool, + #[arg(short, long)] + /// Echo commands run + verbose: bool, + }, + #[command(name = "update_lints")] + /// Updates lint registration and information from the source code + /// + /// Makes sure that: {n} + /// * the lint count in README.md is correct {n} + /// * the changelog contains markdown link references at the bottom {n} + /// * all lint groups include the correct lints {n} + /// * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod` {n} + /// * all lints are registered in the lint store + UpdateLints { + #[arg(long)] + /// Print a table of lints to STDOUT + /// + /// This does not include deprecated and internal lints. (Does not modify any files) + print_only: bool, + #[arg(long)] + /// Checks that `cargo dev update_lints` has been run. Used on CI. + check: bool, + }, + #[command(name = "new_lint")] + /// Create a new lint and run `cargo dev update_lints` + NewLint { + #[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")] + /// Specify whether the lint runs during the early or late pass + pass: String, + #[arg( + short, + long, + value_parser = |name: &str| Ok::<_, Infallible>(name.replace('-', "_")), + )] + /// Name of the new lint in snake case, ex: `fn_too_long` + name: String, + #[arg( + short, + long, + value_parser = [ + "style", + "correctness", + "suspicious", + "complexity", + "perf", + "pedantic", + "restriction", + "cargo", + "nursery", + "internal", + ], + default_value = "nursery", + )] + /// What category the lint belongs to + category: String, + #[arg(long)] + /// What directory the lint belongs in + r#type: Option, + #[arg(long)] + /// Add MSRV config code to the lint + msrv: bool, + }, + /// Support for setting up your personal development environment + Setup(SetupCommand), + /// Support for removing changes done by the setup command + Remove(RemoveCommand), + /// Launch a local 'ALL the Clippy Lints' website in a browser + Serve { + #[arg(short, long, default_value = "8000")] + /// Local port for the http server + port: u16, + #[arg(long)] + /// Which lint's page to load initially (optional) + lint: Option, + }, + #[allow(clippy::doc_markdown)] + /// Manually run clippy on a file or package + /// + /// ## Examples + /// + /// Lint a single file: {n} + /// cargo dev lint tests/ui/attrs.rs + /// + /// Lint a package directory: {n} + /// cargo dev lint tests/ui-cargo/wildcard_dependencies/fail {n} + /// cargo dev lint ~/my-project + /// + /// Run rustfix: {n} + /// cargo dev lint ~/my-project -- --fix + /// + /// Set lint levels: {n} + /// cargo dev lint file.rs -- -W clippy::pedantic {n} + /// cargo dev lint ~/my-project -- -- -W clippy::pedantic + Lint { + /// The path to a file or package directory to lint + path: String, + /// Pass extra arguments to cargo/clippy-driver + args: Vec, + }, + #[command(name = "rename_lint")] + /// Rename a lint + RenameLint { + /// The name of the lint to rename + old_name: String, + #[arg(required_unless_present = "uplift")] + /// The new name of the lint + new_name: Option, + #[arg(long)] + /// This lint will be uplifted into rustc + uplift: bool, + }, + /// Deprecate the given lint + Deprecate { + /// The name of the lint to deprecate + name: String, + #[arg(long, short)] + /// The reason for deprecation + reason: Option, + }, +} - Run rustfix: - cargo dev lint ~/my-project -- --fix +#[derive(Args)] +struct SetupCommand { + #[command(subcommand)] + subcommand: SetupSubcommand, +} + +#[derive(Subcommand)] +enum SetupSubcommand { + /// Alter dependencies so Intellij Rust can find rustc internals + Intellij { + #[arg(long)] + /// Remove the dependencies added with 'cargo dev setup intellij' + remove: bool, + #[arg(long, short, conflicts_with = "remove")] + /// The path to a rustc repo that will be used for setting the dependencies + repo_path: String, + }, + /// Add a pre-commit git hook that formats your code to make it look pretty + GitHook { + #[arg(long)] + /// Remove the pre-commit hook added with 'cargo dev setup git-hook' + remove: bool, + #[arg(long, short)] + /// Forces the override of an existing git pre-commit hook + force_override: bool, + }, + /// Install a rustup toolchain pointing to the local clippy build + Toolchain { + #[arg(long, short)] + /// Override an existing toolchain + force: bool, + #[arg(long, short)] + /// Point to --release clippy binary + release: bool, + #[arg(long, default_value = "clippy")] + /// Name of the toolchain + name: String, + }, + /// Add several tasks to vscode for formatting, validation and testing + VscodeTasks { + #[arg(long)] + /// Remove the tasks added with 'cargo dev setup vscode-tasks' + remove: bool, + #[arg(long, short)] + /// Forces the override of existing vscode tasks + force_override: bool, + }, +} + +#[derive(Args)] +struct RemoveCommand { + #[command(subcommand)] + subcommand: RemoveSubcommand, +} - Set lint levels: - cargo dev lint file.rs -- -W clippy::pedantic - cargo dev lint ~/my-project -- -- -W clippy::pedantic - "}) - .args([ - Arg::new("path") - .required(true) - .help("The path to a file or package directory to lint"), - Arg::new("args") - .action(ArgAction::Append) - .help("Pass extra arguments to cargo/clippy-driver"), - ]), - Command::new("rename_lint").about("Renames the given lint").args([ - Arg::new("old_name") - .index(1) - .required(true) - .help("The name of the lint to rename"), - Arg::new("new_name") - .index(2) - .required_unless_present("uplift") - .help("The new name of the lint"), - Arg::new("uplift") - .long("uplift") - .action(ArgAction::SetTrue) - .help("This lint will be uplifted into rustc"), - ]), - Command::new("deprecate").about("Deprecates the given lint").args([ - Arg::new("name") - .index(1) - .required(true) - .help("The name of the lint to deprecate"), - Arg::new("reason") - .long("reason") - .short('r') - .help("The reason for deprecation"), - ]), - ]) - .get_matches() +#[derive(Subcommand)] +enum RemoveSubcommand { + /// Remove the dependencies added with 'cargo dev setup intellij' + Intellij, + /// Remove the pre-commit git hook + GitHook, + /// Remove the tasks added with 'cargo dev setup vscode-tasks' + VscodeTasks, } diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 2940d56350f41..b6481dde4dde0 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -36,22 +36,16 @@ impl Context for io::Result { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create( - pass: &String, - lint_name: Option<&String>, - category: Option<&str>, - mut ty: Option<&str>, - msrv: bool, -) -> io::Result<()> { - if category == Some("cargo") && ty.is_none() { +pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> { + if category == "cargo" && ty.is_none() { // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo` ty = Some("cargo"); } let lint = LintData { pass, - name: lint_name.expect("`name` argument is validated by clap"), - category: category.expect("`category` argument is validated by clap"), + name, + category, ty, project_root: clippy_project_root(), }; diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index ea925f6709f9b..4a4261d1a1e63 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -8,7 +8,7 @@ use std::{env, thread}; /// # Panics /// /// Panics if the python commands could not be spawned -pub fn run(port: u16, lint: Option<&String>) -> ! { +pub fn run(port: u16, lint: Option) -> ! { let mut url = Some(match lint { None => format!("http://localhost:{port}"), Some(lint) => format!("http://localhost:{port}/#{lint}"), diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 625b133959133..45353901c98fd 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -314,7 +314,7 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note"; /// # Panics /// /// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: Option<&String>) { +pub fn deprecate(name: &str, reason: Option<&str>) { fn finish( (lints, mut deprecated_lints, renamed_lints): (Vec, Vec, Vec), name: &str, @@ -335,7 +335,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) { println!("note: you must run `cargo uitest` to update the test results"); } - let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str); + let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON); let name_lower = name.to_lowercase(); let name_upper = name.to_uppercase(); From 537ab6cc879e8b5af888b7a6d1ea4a17cfccc853 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 2 May 2024 14:40:11 +0200 Subject: [PATCH 18/58] Bump clap version -> 4.4 Same version as most other crates in rustc are using --- clippy_dev/Cargo.toml | 2 +- lintcheck/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 9cfa5b7630f35..4104e7d94f146 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] aho-corasick = "1.0" -clap = { version = "4.1.4", features = ["derive"] } +clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" opener = "0.6" diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index a828d12370467..8c5a409e25b15 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -13,7 +13,7 @@ default-run = "lintcheck" [dependencies] anyhow = "1.0.69" cargo_metadata = "0.15.3" -clap = { version = "4.1.8", features = ["derive", "env"] } +clap = { version = "4.4", features = ["derive", "env"] } crates_io_api = "0.8.1" crossbeam-channel = "0.5.6" flate2 = "1.0" From 79a14dea86fe8e8f5588fb2a32f5b73f065529a3 Mon Sep 17 00:00:00 2001 From: Michael Baikov Date: Tue, 23 Apr 2024 09:32:35 -0400 Subject: [PATCH 19/58] less aggressive needless_borrows_for_generic_args Current implementation looks for significant drops, that can change the behavior, but that's not enough - value might not have a Drop itself but one of its children might have it. A good example is passing a reference to `PathBuf` to `std::fs::File::open`. There's no benefits to pass `PathBuf` by value, but since clippy can't see `Drop` on `Vec` several layers down it complains forcing pass by value and making it impossible to use the same name later. New implementation only looks at copy values or values created inplace so existing variable will never be moved but things that take a string reference created and value is created inplace `&"".to_owned()` will make it to suggest to use `"".to_owned()` still. Fixes https://github.com/rust-lang/rust-clippy/issues/12454 --- .../src/needless_borrows_for_generic_args.rs | 45 +++---------------- .../needless_borrows_for_generic_args.fixed | 42 +++++++++++++++-- tests/ui/needless_borrows_for_generic_args.rs | 36 ++++++++++++++- .../needless_borrows_for_generic_args.stderr | 26 +++++------ 4 files changed, 88 insertions(+), 61 deletions(-) diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index a24cd4f9c8a35..5219e39a2ca80 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -1,6 +1,6 @@ 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::mir::PossibleBorrowerMap; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; @@ -11,7 +11,6 @@ use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::{ self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamTy, ProjectionPredicate, Ty, }; @@ -106,7 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { } && let count = needless_borrow_count( cx, - &mut self.possible_borrowers, fn_id, cx.typeck_results().node_args(hir_id), i, @@ -155,11 +153,9 @@ fn path_has_args(p: &QPath<'_>) -> bool { /// The following constraints will be checked: /// * The borrowed expression meets all the generic type's constraints. /// * The generic type appears only once in the functions signature. -/// * The borrowed value will not be moved if it is used later in the function. -#[expect(clippy::too_many_arguments)] +/// * The borrowed value is Copy itself OR not a variable (created by a function call) fn needless_borrow_count<'tcx>( cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, fn_id: DefId, callee_args: &'tcx List>, arg_index: usize, @@ -234,9 +230,9 @@ fn needless_borrow_count<'tcx>( let referent_ty = cx.typeck_results().expr_ty(referent); - if !is_copy(cx, referent_ty) - && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) - || !referent_used_exactly_once(cx, possible_borrowers, reference)) + if (!is_copy(cx, referent_ty) && !referent_ty.is_ref()) + && let ExprKind::AddrOf(_, _, inner) = reference.kind + && !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { return false; } @@ -339,37 +335,6 @@ fn is_mixed_projection_predicate<'tcx>( } } -fn referent_used_exactly_once<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - reference: &Expr<'tcx>, -) -> bool { - if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) - && let Some(local) = expr_local(cx.tcx, reference) - && let [location] = *local_assignments(mir, local).as_slice() - && let block_data = &mir.basic_blocks[location.block] - && let Some(statement) = block_data.statements.get(location.statement_index) - && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind - && !place.is_indirect_first_projection() - { - let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); - if possible_borrowers - .last() - .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) - { - possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); - } - let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; - // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is - // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of - // itself. See the comment in that method for an explanation as to why. - possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) - && used_exactly_once(mir, place.local).unwrap_or(false) - } else { - false - } -} - // Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. diff --git a/tests/ui/needless_borrows_for_generic_args.fixed b/tests/ui/needless_borrows_for_generic_args.fixed index bd7a9a0b9840d..5478372cbe00f 100644 --- a/tests/ui/needless_borrows_for_generic_args.fixed +++ b/tests/ui/needless_borrows_for_generic_args.fixed @@ -141,8 +141,8 @@ fn main() { let f = |arg| { let loc = "loc".to_owned(); let _ = std::fs::write("x", &env); // Don't lint. In environment - let _ = std::fs::write("x", arg); - let _ = std::fs::write("x", loc); + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); }; let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` f(arg); @@ -158,13 +158,13 @@ fn main() { fn f(_: impl Debug) {} let x = X; - f(&x); // Don't lint. Has significant drop + f(&x); // Don't lint, not copy, passed by a reference to a variable } { fn f(_: impl AsRef) {} let x = String::new(); - f(x); + f(&x); } { fn f(_: impl AsRef) {} @@ -299,4 +299,38 @@ fn main() { check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop } } + { + #[derive(Debug)] + struct X(Vec); + + fn f(_: impl Debug) {} + + let x = X(vec![]); + f(&x); // Don't lint, makes x unavailable later + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + #[derive(Debug)] + struct Y(X); + + let y = Y(X); + f(&y); // Don't lint. Not copy, passed by a reference to value + } + { + fn f(_: impl AsRef) {} + let x = String::new(); + f(&x); // Don't lint, not a copy, makes it unavailable later + f(String::new()); // Lint, makes no difference + let y = "".to_owned(); + f(&y); // Don't lint + f("".to_owned()); // Lint + } } diff --git a/tests/ui/needless_borrows_for_generic_args.rs b/tests/ui/needless_borrows_for_generic_args.rs index 5cfd4ce30cc54..2643815d939b5 100644 --- a/tests/ui/needless_borrows_for_generic_args.rs +++ b/tests/ui/needless_borrows_for_generic_args.rs @@ -158,7 +158,7 @@ fn main() { fn f(_: impl Debug) {} let x = X; - f(&x); // Don't lint. Has significant drop + f(&x); // Don't lint, not copy, passed by a reference to a variable } { fn f(_: impl AsRef) {} @@ -299,4 +299,38 @@ fn main() { check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop } } + { + #[derive(Debug)] + struct X(Vec); + + fn f(_: impl Debug) {} + + let x = X(vec![]); + f(&x); // Don't lint, makes x unavailable later + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + #[derive(Debug)] + struct Y(X); + + let y = Y(X); + f(&y); // Don't lint. Not copy, passed by a reference to value + } + { + fn f(_: impl AsRef) {} + let x = String::new(); + f(&x); // Don't lint, not a copy, makes it unavailable later + f(&String::new()); // Lint, makes no difference + let y = "".to_owned(); + f(&y); // Don't lint + f(&"".to_owned()); // Lint + } } diff --git a/tests/ui/needless_borrows_for_generic_args.stderr b/tests/ui/needless_borrows_for_generic_args.stderr index 83c076f8d863a..fba0755d14b5f 100644 --- a/tests/ui/needless_borrows_for_generic_args.stderr +++ b/tests/ui/needless_borrows_for_generic_args.stderr @@ -50,28 +50,22 @@ LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:144:41 - | -LL | let _ = std::fs::write("x", &arg); - | ^^^^ help: change this to: `arg` - -error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:145:41 + --> tests/ui/needless_borrows_for_generic_args.rs:247:13 | -LL | let _ = std::fs::write("x", &loc); - | ^^^^ help: change this to: `loc` +LL | foo(&a); + | ^^ help: change this to: `a` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:167:11 + --> tests/ui/needless_borrows_for_generic_args.rs:331:11 | -LL | f(&x); - | ^^ help: change this to: `x` +LL | f(&String::new()); // Lint, makes no difference + | ^^^^^^^^^^^^^^ help: change this to: `String::new()` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:247:13 + --> tests/ui/needless_borrows_for_generic_args.rs:334:11 | -LL | foo(&a); - | ^^ help: change this to: `a` +LL | f(&"".to_owned()); // Lint + | ^^^^^^^^^^^^^^ help: change this to: `"".to_owned()` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors From a78a15c5d399a813f84a04843164711a743c2692 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Fri, 26 Apr 2024 21:40:01 +0800 Subject: [PATCH 20/58] Avoid unnecessary arguments --- .../matches/significant_drop_in_scrutinee.rs | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 6b6ded1a337d6..13c0b6e742def 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -115,18 +115,25 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } } - fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> { - self.cx.typeck_results().expr_ty(ex) + fn is_sig_drop_expr(&mut self, ex: &'tcx Expr<'_>) -> bool { + self.has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex)) } - fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { self.seen_types.clear(); - self.has_sig_drop_attr_impl(cx, ty) + self.has_sig_drop_attr_impl(ty) } - fn has_sig_drop_attr_impl(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool { if let Some(adt) = ty.ty_adt_def() { - if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 { + if get_attr( + self.cx.sess(), + self.cx.tcx.get_attrs_unchecked(adt.did()), + "has_significant_drop", + ) + .count() + > 0 + { return true; } } @@ -139,8 +146,8 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { rustc_middle::ty::Adt(adt, args) => { // if some field has significant drop, adt.all_fields() - .map(|field| field.ty(cx.tcx, args)) - .any(|ty| self.has_sig_drop_attr(cx, ty)) + .map(|field| field.ty(self.cx.tcx, args)) + .any(|ty| self.has_sig_drop_attr_impl(ty)) // or if there is no generic lifetime and.. // (to avoid false positive on `Ref<'a, MutexGuard>`) || (args @@ -154,10 +161,10 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { GenericArgKind::Type(ty) => Some(ty), _ => None, }) - .any(|ty| self.has_sig_drop_attr(cx, ty))) + .any(|ty| self.has_sig_drop_attr_impl(ty))) }, - rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr(cx, ty)), - rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr_impl(ty)), + rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr_impl(*ty), _ => false, }; @@ -240,7 +247,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { if self.current_sig_drop.is_some() { return; } - let ty = self.sig_drop_checker.get_type(expr); + let ty = self.cx.typeck_results().expr_ty(expr); if ty.is_ref() { // We checked that the type was ref, so builtin_deref will return Some TypeAndMut, // but let's avoid any chance of an ICE @@ -287,11 +294,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - if !self.is_chain_end - && self - .sig_drop_checker - .has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex)) - { + if !self.is_chain_end && self.sig_drop_checker.is_sig_drop_expr(ex) { self.has_significant_drop = true; return; } @@ -395,10 +398,7 @@ fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<' impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if self - .sig_drop_checker - .has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex)) - { + if self.sig_drop_checker.is_sig_drop_expr(ex) { self.found_sig_drop_spans.insert(ex.span); return; } From 509ca90bf1201c00ba2a519dc590c83dc73bdb3b Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Mon, 29 Apr 2024 12:55:12 +0800 Subject: [PATCH 21/58] Ignore significant drops of place expressions --- .../matches/significant_drop_in_scrutinee.rs | 2 +- tests/ui/significant_drop_in_scrutinee.rs | 56 +++++++++++++++++++ tests/ui/significant_drop_in_scrutinee.stderr | 18 +++++- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 13c0b6e742def..3171acda44237 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -116,7 +116,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } fn is_sig_drop_expr(&mut self, ex: &'tcx Expr<'_>) -> bool { - self.has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex)) + !ex.is_syntactic_place_expr() && self.has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex)) } fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index 0305d895fc585..7fc89bb95380e 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -675,4 +675,60 @@ fn should_not_trigger_on_significant_iterator_drop() { } } +// https://github.com/rust-lang/rust-clippy/issues/9072 +fn should_not_trigger_lint_if_place_expr_has_significant_drop() { + let x = Mutex::new(vec![1, 2, 3]); + let x_guard = x.lock().unwrap(); + + match x_guard[0] { + 1 => println!("1!"), + x => println!("{x}"), + } + + match x_guard.len() { + 1 => println!("1!"), + x => println!("{x}"), + } +} + +struct Guard<'a, T>(MutexGuard<'a, T>); + +struct Ref<'a, T>(&'a T); + +impl<'a, T> Guard<'a, T> { + fn guard(&self) -> &MutexGuard { + &self.0 + } + + fn guard_ref(&self) -> Ref> { + Ref(&self.0) + } + + fn take(self) -> Box> { + Box::new(self.0) + } +} + +fn should_not_trigger_for_significant_drop_ref() { + let mutex = Mutex::new(vec![1, 2]); + let guard = Guard(mutex.lock().unwrap()); + + match guard.guard().len() { + 0 => println!("empty"), + _ => println!("not empty"), + } + + match guard.guard_ref().0.len() { + 0 => println!("empty"), + _ => println!("not empty"), + } + + match guard.take().len() { + //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + 0 => println!("empty"), + _ => println!("not empty"), + }; +} + fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index 1f19eff063162..811bb06552792 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -505,5 +505,21 @@ LL ~ let value = mutex.lock().unwrap().foo(); LL ~ match value { | -error: aborting due to 26 previous errors +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:726:11 + | +LL | match guard.take().len() { + | ^^^^^^^^^^^^^^^^^^ +... +LL | }; + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = guard.take().len(); +LL ~ match value { + | + +error: aborting due to 27 previous errors From c9ea0ae502aa5afe84ae528b697824412ebabf77 Mon Sep 17 00:00:00 2001 From: Yusuf Raji Date: Sun, 5 May 2024 23:26:29 +0200 Subject: [PATCH 22/58] Lint while loops with float comparison --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/loops/mod.rs | 36 +++++++++++++++++++++++++++ clippy_lints/src/loops/while_float.rs | 20 +++++++++++++++ tests/ui/while_float.rs | 14 +++++++++++ tests/ui/while_float.stderr | 20 +++++++++++++++ 6 files changed, 92 insertions(+) create mode 100644 clippy_lints/src/loops/while_float.rs create mode 100644 tests/ui/while_float.rs create mode 100644 tests/ui/while_float.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c9ea11408143..1f759ab0828c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5908,6 +5908,7 @@ Released 2018-09-13 [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons [`waker_clone_wake`]: https://rust-lang.github.io/rust-clippy/master/index.html#waker_clone_wake +[`while_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_float [`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition [`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop [`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5ff7d8e513435..8f85f7595551a 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -291,6 +291,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::loops::SAME_ITEM_PUSH_INFO, crate::loops::SINGLE_ELEMENT_LOOP_INFO, crate::loops::UNUSED_ENUMERATE_INDEX_INFO, + crate::loops::WHILE_FLOAT_INFO, crate::loops::WHILE_IMMUTABLE_CONDITION_INFO, crate::loops::WHILE_LET_LOOP_INFO, crate::loops::WHILE_LET_ON_ITERATOR_INFO, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index b5e39b33c6a15..eb363a7039293 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -17,6 +17,7 @@ mod same_item_push; mod single_element_loop; mod unused_enumerate_index; mod utils; +mod while_float; mod while_immutable_condition; mod while_let_loop; mod while_let_on_iterator; @@ -416,6 +417,39 @@ declare_clippy_lint! { "variables used within while expression are not mutated in the body" } +declare_clippy_lint! { + /// ### What it does + /// Checks for while loops comparing floating point values. + /// + /// ### Why is this bad? + /// If you increment floating point values, errors can compound, + /// so, use integers instead if possible. + /// + /// ### Known problems + /// The lint will catch all while loops comparing floating point + /// values no matter whether it's a threshold or something. + /// + /// ### Example + /// ```no_run + /// let mut x = 0.0; + /// while x < 42.0 { + /// x += 1.0; + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// let mut x = 0; + /// while x < 42 { + /// x += 1; + /// } + /// ``` + #[clippy::version = "1.80.0"] + pub WHILE_FLOAT, + nursery, + "while loops comaparing floating point values" +} + declare_clippy_lint! { /// ### What it does /// Checks whether a for loop is being used to push a constant @@ -706,6 +740,7 @@ impl_lint_pass!(Loops => [ NEVER_LOOP, MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, + WHILE_FLOAT, SAME_ITEM_PUSH, SINGLE_ELEMENT_LOOP, MISSING_SPIN_LOOP, @@ -762,6 +797,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) { while_immutable_condition::check(cx, condition, body); + while_float::check(cx, condition); missing_spin_loop::check(cx, condition, body); manual_while_let_some::check(cx, condition, body, span); } diff --git a/clippy_lints/src/loops/while_float.rs b/clippy_lints/src/loops/while_float.rs new file mode 100644 index 0000000000000..cf62ce29f0c7a --- /dev/null +++ b/clippy_lints/src/loops/while_float.rs @@ -0,0 +1,20 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_hir::ExprKind; + +pub(super) fn check(cx: &rustc_lint::LateContext<'_>, condition: &rustc_hir::Expr<'_>) { + if let ExprKind::Binary(_op, left, right) = condition.kind + && is_float_type(cx, left) + && is_float_type(cx, right) + { + span_lint( + cx, + super::WHILE_FLOAT, + condition.span, + "while condition comparing floats", + ); + } +} + +fn is_float_type(cx: &rustc_lint::LateContext<'_>, expr: &rustc_hir::Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_floating_point() +} diff --git a/tests/ui/while_float.rs b/tests/ui/while_float.rs new file mode 100644 index 0000000000000..a3b0618948e6b --- /dev/null +++ b/tests/ui/while_float.rs @@ -0,0 +1,14 @@ +#[deny(clippy::while_float)] +fn main() { + let mut x = 0.0_f32; + while x < 42.0_f32 { + x += 0.5; + } + while x < 42.0 { + x += 1.0; + } + let mut x = 0; + while x < 42 { + x += 1; + } +} diff --git a/tests/ui/while_float.stderr b/tests/ui/while_float.stderr new file mode 100644 index 0000000000000..b8e934b97c6c4 --- /dev/null +++ b/tests/ui/while_float.stderr @@ -0,0 +1,20 @@ +error: while condition comparing floats + --> tests/ui/while_float.rs:4:11 + | +LL | while x < 42.0_f32 { + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/while_float.rs:1:8 + | +LL | #[deny(clippy::while_float)] + | ^^^^^^^^^^^^^^^^^^^ + +error: while condition comparing floats + --> tests/ui/while_float.rs:7:11 + | +LL | while x < 42.0 { + | ^^^^^^^^ + +error: aborting due to 2 previous errors + From 566bfff8bb36eb4d52a637e28732ab38439491c5 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Thu, 2 May 2024 12:05:45 -0700 Subject: [PATCH 23/58] useless_attribute: Update docs for allowed attributes --- clippy_lints/src/attrs/mod.rs | 12 ++++- clippy_lints/src/attrs/useless_attribute.rs | 54 ++++++++++++--------- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 8f47bc7653b7c..39f4060779957 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -61,11 +61,21 @@ declare_clippy_lint! { /// /// This lint permits lint attributes for lints emitted on the items themself. /// For `use` items these lints are: + /// * ambiguous_glob_reexports + /// * dead_code /// * deprecated + /// * hidden_glob_reexports /// * unreachable_pub - /// * unused_imports + /// * unused + /// * unused_braces + /// * unused_import_braces + /// * clippy::disallowed_types /// * clippy::enum_glob_use /// * clippy::macro_use_imports + /// * clippy::module_name_repetitions + /// * clippy::redundant_pub_crate + /// * clippy::single_component_path_imports + /// * clippy::unsafe_removed_from_name /// * clippy::wildcard_imports /// /// For `extern crate` items these lints are: diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index f6f4d78044041..f0868231d01a3 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -2,6 +2,7 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word}; use super::{Attribute, USELESS_ATTRIBUTE}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{first_line_of_span, snippet_opt}; +use rustc_ast::NestedMetaItem; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LintContext}; @@ -20,31 +21,40 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) for lint in lint_list { match item.kind { ItemKind::Use(..) => { - if is_word(lint, sym::unused_imports) - || is_word(lint, sym::deprecated) - || is_word(lint, sym!(unreachable_pub)) - || is_word(lint, sym!(unused)) - || is_word(lint, sym!(unused_import_braces)) - || is_word(lint, sym!(unused_braces)) - || is_word(lint, sym::dead_code) - || is_word(lint, sym!(hidden_glob_reexports)) - || is_word(lint, sym!(ambiguous_glob_reexports)) - || extract_clippy_lint(lint).map_or(false, |s| { - matches!( - s.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - | "disallowed_types" - ) - }) + if let NestedMetaItem::MetaItem(meta_item) = lint + && meta_item.is_word() + && let Some(ident) = meta_item.ident() + && matches!( + ident.name.as_str(), + "ambiguous_glob_reexports" + | "dead_code" + | "deprecated" + | "hidden_glob_reexports" + | "unreachable_pub" + | "unused" + | "unused_braces" + | "unused_import_braces" + | "unused_imports" + ) { return; } + + if extract_clippy_lint(lint).is_some_and(|symbol| { + matches!( + symbol.as_str(), + "wildcard_imports" + | "enum_glob_use" + | "redundant_pub_crate" + | "macro_use_imports" + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" + | "disallowed_types" + ) + }) { + return; + } }, ItemKind::ExternCrate(..) => { if is_word(lint, sym::unused_imports) && skip_unused_imports { From f3dd31e21477c908daece71dc50bb7730b06a6ee Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 6 May 2024 13:38:18 -0700 Subject: [PATCH 24/58] Add lint for markdown lazy paragraph continuations This is a follow-up for https://github.com/rust-lang/rust/pull/121659, since most cases of unintended block quotes are lazy continuations. The lint is designed to be more generally useful than that, though, because it will also catch unintended list items and unintended block quotes that didn't coincidentally hit a pulldown-cmark bug. --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/doc/lazy_continuation.rs | 95 ++++++++++++++++++ clippy_lints/src/doc/mod.rs | 115 +++++++++++++++++++++- tests/ui/doc/doc_lazy_blockquote.fixed | 47 +++++++++ tests/ui/doc/doc_lazy_blockquote.rs | 47 +++++++++ tests/ui/doc/doc_lazy_blockquote.stderr | 76 ++++++++++++++ tests/ui/doc/doc_lazy_list.fixed | 42 ++++++++ tests/ui/doc/doc_lazy_list.rs | 42 ++++++++ tests/ui/doc/doc_lazy_list.stderr | 112 +++++++++++++++++++++ 10 files changed, 573 insertions(+), 5 deletions(-) create mode 100644 clippy_lints/src/doc/lazy_continuation.rs create mode 100644 tests/ui/doc/doc_lazy_blockquote.fixed create mode 100644 tests/ui/doc/doc_lazy_blockquote.rs create mode 100644 tests/ui/doc/doc_lazy_blockquote.stderr create mode 100644 tests/ui/doc/doc_lazy_list.fixed create mode 100644 tests/ui/doc/doc_lazy_list.rs create mode 100644 tests/ui/doc/doc_lazy_list.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c9ea11408143..76010d82cae30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5249,6 +5249,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5ff7d8e513435..8a7e51fc76835 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -140,6 +140,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::disallowed_names::DISALLOWED_NAMES_INFO, crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_LAZY_CONTINUATION_INFO, crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, crate::doc::EMPTY_DOCS_INFO, diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs new file mode 100644 index 0000000000000..497e9e5d4d1b9 --- /dev/null +++ b/clippy_lints/src/doc/lazy_continuation.rs @@ -0,0 +1,95 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use itertools::Itertools; +use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_lint::LateContext; +use rustc_span::{BytePos, Span}; +use std::ops::Range; + +use super::DOC_LAZY_CONTINUATION; + +fn map_container_to_text(c: &super::Container) -> &'static str { + match c { + super::Container::Blockquote => "> ", + // numbered list can have up to nine digits, plus the dot, plus four spaces on either side + super::Container::List(indent) => &" "[0..*indent], + } +} + +// TODO: Adjust the parameters as necessary +pub(super) fn check( + cx: &LateContext<'_>, + doc: &str, + range: Range, + mut span: Span, + containers: &[super::Container], +) { + if doc[range.clone()].contains('\t') { + // We don't do tab stops correctly. + return; + } + + let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count(); + let blockquote_level = containers + .iter() + .filter(|c| matches!(c, super::Container::Blockquote)) + .count(); + let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count(); + let list_indentation = containers + .iter() + .map(|c| { + if let super::Container::List(indent) = c { + *indent + } else { + 0 + } + }) + .sum(); + if ccount < blockquote_level || lcount < list_indentation { + let msg = if ccount < blockquote_level { + "doc quote missing `>` marker" + } else { + "doc list item missing indentation" + }; + span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { + if ccount == 0 && blockquote_level == 0 { + // simpler suggestion style for indentation + let indent = list_indentation - lcount; + diag.span_suggestion_with_style( + span.shrink_to_hi(), + "indent this line", + std::iter::repeat(" ").take(indent).join(""), + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + diag.help("if this is supposed to be its own paragraph, add a blank line"); + return; + } + let mut doc_start_range = &doc[range]; + let mut suggested = String::new(); + for c in containers { + let text = map_container_to_text(c); + if doc_start_range.starts_with(text) { + doc_start_range = &doc_start_range[text.len()..]; + span = span + .with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger"))); + } else if matches!(c, super::Container::Blockquote) + && let Some(i) = doc_start_range.find('>') + { + doc_start_range = &doc_start_range[i + 1..]; + span = + span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); + } else { + suggested.push_str(text); + } + } + diag.span_suggestion_with_style( + span, + "add markers to start of line", + suggested, + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + diag.help("if this not intended to be a quote at all, escape it with `\\>`"); + }); + } +} diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 4bced104d3bc9..29ccf4fceb5ba 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1,3 +1,4 @@ +mod lazy_continuation; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; @@ -7,7 +8,7 @@ use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; -use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph}; +use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; @@ -362,6 +363,63 @@ declare_clippy_lint! { "docstrings exist but documentation is empty" } +declare_clippy_lint! { + /// ### What it does + /// + /// In CommonMark Markdown, the language used to write doc comments, a + /// paragraph nested within a list or block quote does not need any line + /// after the first one to be indented or marked. The specification calls + /// this a "lazy paragraph continuation." + /// + /// ### Why is this bad? + /// + /// This is easy to write but hard to read. Lazy continuations makes + /// unintended markers hard to see, and make it harder to deduce the + /// document's intended structure. + /// + /// ### Example + /// + /// This table is probably intended to have two rows, + /// but it does not. It has zero rows, and is followed by + /// a block quote. + /// ```no_run + /// /// Range | Description + /// /// ----- | ----------- + /// /// >= 1 | fully opaque + /// /// < 1 | partially see-through + /// fn set_opacity(opacity: f32) {} + /// ``` + /// + /// Fix it by escaping the marker: + /// ```no_run + /// /// Range | Description + /// /// ----- | ----------- + /// /// \>= 1 | fully opaque + /// /// < 1 | partially see-through + /// fn set_opacity(opacity: f32) {} + /// ``` + /// + /// This example is actually intended to be a list: + /// ```no_run + /// /// * Do nothing. + /// /// * Then do something. Whatever it is needs done, + /// /// it should be done right now. + /// # fn do_stuff() {} + /// ``` + /// + /// Fix it by indenting the list contents: + /// ```no_run + /// /// * Do nothing. + /// /// * Then do something. Whatever it is needs done, + /// /// it should be done right now. + /// # fn do_stuff() {} + /// ``` + #[clippy::version = "1.80.0"] + pub DOC_LAZY_CONTINUATION, + style, + "require every line of a paragraph to be indented and marked" +} + #[derive(Clone)] pub struct Documentation { valid_idents: FxHashSet, @@ -388,6 +446,7 @@ impl_lint_pass!(Documentation => [ UNNECESSARY_SAFETY_DOC, SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, + DOC_LAZY_CONTINUATION, ]); impl<'tcx> LateLintPass<'tcx> for Documentation { @@ -551,6 +610,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ cx, valid_idents, parser.into_offset_iter(), + &doc, Fragments { fragments: &fragments, doc: &doc, @@ -560,6 +620,11 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; +enum Container { + Blockquote, + List(usize), +} + /// Checks parsed documentation. /// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`, /// so lints here will generally access that information. @@ -569,6 +634,7 @@ fn check_doc<'a, Events: Iterator, Range, valid_idents: &FxHashSet, events: Events, + doc: &str, fragments: Fragments<'_>, ) -> DocHeaders { // true if a safety header was found @@ -576,6 +642,7 @@ fn check_doc<'a, Events: Iterator, Range, Range { if tag.starts_with(", Range blockquote_level += 1, - End(BlockQuote) => blockquote_level -= 1, + Start(BlockQuote) => { + blockquote_level += 1; + containers.push(Container::Blockquote); + }, + End(BlockQuote) => { + blockquote_level -= 1; + containers.pop(); + }, Start(CodeBlock(ref kind)) => { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { @@ -633,6 +710,13 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range in_footnote_definition = true, + End(FootnoteDefinition(..)) => in_footnote_definition = false, Start(_tag) | End(_tag) => (), // We don't care about other tags - SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), + SoftBreak | HardBreak => { + if !containers.is_empty() + && let Some((_next_event, next_range)) = events.peek() + && let Some(next_span) = fragments.span(cx, next_range.clone()) + && let Some(span) = fragments.span(cx, range.clone()) + && !in_footnote_definition + { + lazy_continuation::check( + cx, + doc, + range.end..next_range.start, + Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()), + &containers[..], + ); + } + }, + TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { paragraph_range.end = range.end; ticks_unbalanced |= text.contains('`') && !in_code; diff --git a/tests/ui/doc/doc_lazy_blockquote.fixed b/tests/ui/doc/doc_lazy_blockquote.fixed new file mode 100644 index 0000000000000..9877991f183a5 --- /dev/null +++ b/tests/ui/doc/doc_lazy_blockquote.fixed @@ -0,0 +1,47 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// > blockquote with +/// > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn first() {} + +/// > blockquote with no +/// > lazy continuation +fn first_nowarn() {} + +/// > blockquote with no +/// +/// lazy continuation +fn two_nowarn() {} + +/// > nest here +/// > +/// > > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn two() {} + +/// > nest here +/// > +/// > > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn three() {} + +/// > * > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four() {} + +/// > * > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four_point_1() {} + +/// * > nest here lazy continuation +fn five() {} + +/// 1. > nest here +/// > lazy continuation (this results in strange indentation, but still works) +//~^ ERROR: doc quote missing `>` marker +fn six() {} diff --git a/tests/ui/doc/doc_lazy_blockquote.rs b/tests/ui/doc/doc_lazy_blockquote.rs new file mode 100644 index 0000000000000..587b2fdd533ce --- /dev/null +++ b/tests/ui/doc/doc_lazy_blockquote.rs @@ -0,0 +1,47 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// > blockquote with +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn first() {} + +/// > blockquote with no +/// > lazy continuation +fn first_nowarn() {} + +/// > blockquote with no +/// +/// lazy continuation +fn two_nowarn() {} + +/// > nest here +/// > +/// > > nest here +/// > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn two() {} + +/// > nest here +/// > +/// > > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn three() {} + +/// > * > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four() {} + +/// > * > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four_point_1() {} + +/// * > nest here lazy continuation +fn five() {} + +/// 1. > nest here +/// lazy continuation (this results in strange indentation, but still works) +//~^ ERROR: doc quote missing `>` marker +fn six() {} diff --git a/tests/ui/doc/doc_lazy_blockquote.stderr b/tests/ui/doc/doc_lazy_blockquote.stderr new file mode 100644 index 0000000000000..975184a01c3f0 --- /dev/null +++ b/tests/ui/doc/doc_lazy_blockquote.stderr @@ -0,0 +1,76 @@ +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:4:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` + = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` +help: add markers to start of line + | +LL | /// > lazy continuation + | + + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:20:5 + | +LL | /// > lazy continuation + | ^^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | + + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:27:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:32:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++++++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:37:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:45:5 + | +LL | /// lazy continuation (this results in strange indentation, but still works) + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > lazy continuation (this results in strange indentation, but still works) + | + + +error: aborting due to 6 previous errors + diff --git a/tests/ui/doc/doc_lazy_list.fixed b/tests/ui/doc/doc_lazy_list.fixed new file mode 100644 index 0000000000000..e19371ea9aa22 --- /dev/null +++ b/tests/ui/doc/doc_lazy_list.fixed @@ -0,0 +1,42 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// 1. nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn one() {} + +/// 1. first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn two() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn three() {} + +/// - first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn four() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn five() {} + +/// - - first line +/// this will warn on the lazy continuation +//~^ ERROR: doc list item missing indentation +/// and so should this +//~^ ERROR: doc list item missing indentation +fn six() {} + +/// - - first line +/// +/// this is not a lazy continuation +fn seven() {} diff --git a/tests/ui/doc/doc_lazy_list.rs b/tests/ui/doc/doc_lazy_list.rs new file mode 100644 index 0000000000000..fdfe9afaf1d73 --- /dev/null +++ b/tests/ui/doc/doc_lazy_list.rs @@ -0,0 +1,42 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// 1. nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn one() {} + +/// 1. first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn two() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn three() {} + +/// - first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn four() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn five() {} + +/// - - first line +/// this will warn on the lazy continuation +//~^ ERROR: doc list item missing indentation +/// and so should this +//~^ ERROR: doc list item missing indentation +fn six() {} + +/// - - first line +/// +/// this is not a lazy continuation +fn seven() {} diff --git a/tests/ui/doc/doc_lazy_list.stderr b/tests/ui/doc/doc_lazy_list.stderr new file mode 100644 index 0000000000000..d0f6279f62d73 --- /dev/null +++ b/tests/ui/doc/doc_lazy_list.stderr @@ -0,0 +1,112 @@ +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:4:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line + = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` +help: indent this line + | +LL | /// lazy continuation + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:9:5 + | +LL | /// lazy list continuations don't make warnings with this lint + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy list continuations don't make warnings with this lint + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:11:5 + | +LL | /// because they don't have the + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// because they don't have the + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:16:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy continuation + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:21:5 + | +LL | /// lazy list continuations don't make warnings with this lint + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy list continuations don't make warnings with this lint + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:23:5 + | +LL | /// because they don't have the + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// because they don't have the + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:28:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy continuation + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:33:5 + | +LL | /// this will warn on the lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// this will warn on the lazy continuation + | ++++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:35:5 + | +LL | /// and so should this + | ^^^^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// and so should this + | ++ + +error: aborting due to 9 previous errors + From afedaf6a269027d5504acf898cbe1914ddf7672c Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 6 May 2024 15:33:55 -0700 Subject: [PATCH 25/58] Update doc comments to avoid lazy continuations --- clippy_lints/src/casts/cast_sign_loss.rs | 4 ++++ clippy_lints/src/manual_clamp.rs | 7 +++++++ clippy_lints/src/methods/iter_filter.rs | 8 ++++---- clippy_lints/src/methods/iter_kv_map.rs | 2 ++ clippy_lints/src/methods/utils.rs | 1 + clippy_lints/src/needless_continue.rs | 3 +-- clippy_utils/src/lib.rs | 3 +++ clippy_utils/src/source.rs | 2 +- clippy_utils/src/sugg.rs | 3 +-- 9 files changed, 24 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 2b6e17dc1030d..864489ee3fcd5 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -255,8 +255,10 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { /// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`], /// where the result depends on: +/// /// - the number of negative values in the entire expression, or /// - the number of negative values on the left hand side of the expression. +/// /// Ignores overflow. /// /// @@ -303,8 +305,10 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { } /// Peels binary operators such as [`BinOpKind::Add`], where the result depends on: +/// /// - all the expressions being positive, or /// - all the expressions being negative. +/// /// Ignores overflow. /// /// Expressions using other operators are preserved, so we can try to evaluate them later. diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 1eadc200bedc8..e2ab441551824 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -611,15 +611,22 @@ impl<'tcx> BinaryOp<'tcx> { /// The clamp meta pattern is a pattern shared between many (but not all) patterns. /// In summary, this pattern consists of two if statements that meet many criteria, +/// /// - binary operators that are one of [`>`, `<`, `>=`, `<=`]. +/// /// - Both binary statements must have a shared argument +/// /// - Which can appear on the left or right side of either statement +/// /// - The binary operators must define a finite range for the shared argument. To put this in /// the terms of Rust `std` library, the following ranges are acceptable +/// /// - `Range` /// - `RangeInclusive` +/// /// And all other range types are not accepted. For the purposes of `clamp` it's irrelevant /// whether the range is inclusive or not, the output is the same. +/// /// - The result of each if statement must be equal to the argument unique to that if statement. The /// result can not be the shared argument in either case. fn is_clamp_meta_pattern<'tcx>( diff --git a/clippy_lints/src/methods/iter_filter.rs b/clippy_lints/src/methods/iter_filter.rs index 12647ea1ffcb3..b93d51eac647b 100644 --- a/clippy_lints/src/methods/iter_filter.rs +++ b/clippy_lints/src/methods/iter_filter.rs @@ -126,15 +126,15 @@ enum FilterType { /// /// How this is done: /// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs` -/// 2. we check that we are in a trait method. Therefore we are in an -/// `(x as Iterator).filter({filter_arg})` method call. +/// 2. we check that we are in a trait method. Therefore we are in an `(x as +/// Iterator).filter({filter_arg})` method call. /// 3. we check that the parent expression is not a map. This is because we don't want to lint /// twice, and we already have a specialized lint for that. /// 4. we check that the span of the filter does not contain a comment. /// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and -/// Result. +/// Result. /// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or -/// `is_ok`. +/// `is_ok`. /// 7. if all of the above are true, then we return the `FilterType` fn expression_type( cx: &LateContext<'_>, diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 7c852a3768d16..05e77386128f9 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -12,8 +12,10 @@ use rustc_middle::ty; use rustc_span::{sym, Span}; /// lint use of: +/// /// - `hashmap.iter().map(|(_, v)| v)` /// - `hashmap.into_iter().map(|(_, v)| v)` +/// /// on `HashMaps` and `BTreeMaps` in std pub(super) fn check<'tcx>( diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index c50f24f824ab0..34d7b9acbe4b0 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -120,6 +120,7 @@ fn pat_bindings(pat: &Pat<'_>) -> Vec { /// operations performed on `binding_hir_ids` are: /// * to take non-mutable references to them /// * to use them as non-mutable `&self` in method calls +/// /// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true /// when `CloneOrCopyVisitor` is done visiting. struct CloneOrCopyVisitor<'cx, 'tcx> { diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 8b4a12bb76648..b97cb4579ca7b 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -178,8 +178,7 @@ impl EarlyLintPass for NeedlessContinue { /// Given an expression, returns true if either of the following is true /// /// - The expression is a `continue` node. -/// - The expression node is a block with the first statement being a -/// `continue`. +/// - The expression node is a block with the first statement being a `continue`. fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool { match else_expr.kind { ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0ef732f9268cf..1f7edabcf0189 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1513,15 +1513,18 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { } /// Checks whether the given `Expr` is a range equivalent to a `RangeFull`. +/// /// For the lower bound, this means that: /// - either there is none /// - or it is the smallest value that can be represented by the range's integer type +/// /// For the upper bound, this means that: /// - either there is none /// - or it is the largest value that can be represented by the range's integer type and is /// inclusive /// - or it is a call to some container's `len` method and is exclusive, and the range is passed to /// a method call on that same container (e.g. `v.drain(..v.len())`) +/// /// If the given `Expr` is not some kind of range, the function returns `false`. pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { let ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index a4a6f8e15f935..69f593fe04ac3 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -250,7 +250,7 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow< /// - Applicability level `Unspecified` will never be changed. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it to -/// `HasPlaceholders` +/// `HasPlaceholders` pub fn snippet_with_applicability<'a, T: LintContext>( cx: &T, span: Span, diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 8d6057272c4e7..1d49634aff3f4 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -68,8 +68,7 @@ impl<'a> Sugg<'a> { /// - Applicability level `Unspecified` will never be changed. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it - /// to - /// `HasPlaceholders` + /// to `HasPlaceholders` pub fn hir_with_applicability( cx: &LateContext<'_>, expr: &hir::Expr<'_>, From 28d5115067c35a858af5248f0e7b4bc92380798d Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Wed, 20 Sep 2023 19:05:37 +0800 Subject: [PATCH 26/58] add new lint that disallow renaming parameters in trait functions --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/functions/mod.rs | 34 ++++++++ .../src/functions/renamed_function_params.rs | 82 +++++++++++++++++++ tests/ui/renamed_function_params.rs | 68 +++++++++++++++ tests/ui/renamed_function_params.stderr | 60 ++++++++++++++ 6 files changed, 246 insertions(+) create mode 100644 clippy_lints/src/functions/renamed_function_params.rs create mode 100644 tests/ui/renamed_function_params.rs create mode 100644 tests/ui/renamed_function_params.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c9ea11408143..ce2dda0cc2ac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5702,6 +5702,7 @@ Released 2018-09-13 [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`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 +[`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params [`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 diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5ff7d8e513435..8da893faa402a 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -205,6 +205,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, + crate::functions::RENAMED_FUNCTION_PARAMS_INFO, crate::functions::RESULT_LARGE_ERR_INFO, crate::functions::RESULT_UNIT_ERR_INFO, crate::functions::TOO_MANY_ARGUMENTS_INFO, diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 9cc51fa8cd5dd..387e0c964ccce 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -2,6 +2,7 @@ mod impl_trait_in_params; mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; +mod renamed_function_params; mod result; mod too_many_arguments; mod too_many_lines; @@ -359,6 +360,37 @@ declare_clippy_lint! { "`impl Trait` is used in the function's parameters" } +declare_clippy_lint! { + /// ### What it does + /// Lints when the name of function parameters from trait impl is + /// different than its default implementation. + /// + /// ### Why is this bad? + /// Using the default name for parameters of a trait method is often + /// more desirable for consistency's sake. + /// + /// ### Example + /// ```rust + /// impl From for String { + /// fn from(a: A) -> Self { + /// a.0.to_string() + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// impl From for String { + /// fn from(value: A) -> Self { + /// value.0.to_string() + /// } + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub RENAMED_FUNCTION_PARAMS, + restriction, + "renamed function parameters in trait implementation" +} + #[derive(Copy, Clone)] #[allow(clippy::struct_field_names)] pub struct Functions { @@ -395,6 +427,7 @@ impl_lint_pass!(Functions => [ RESULT_LARGE_ERR, MISNAMED_GETTERS, IMPL_TRAIT_IN_PARAMS, + RENAMED_FUNCTION_PARAMS, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -424,6 +457,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { must_use::check_impl_item(cx, item); result::check_impl_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_impl_item(cx, item); + renamed_function_params::check_impl_item(cx, item); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { diff --git a/clippy_lints/src/functions/renamed_function_params.rs b/clippy_lints/src/functions/renamed_function_params.rs new file mode 100644 index 0000000000000..4c0d5aba11222 --- /dev/null +++ b/clippy_lints/src/functions/renamed_function_params.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::def_id::DefId; +use rustc_hir::hir_id::OwnerId; +use rustc_hir::{ImplItem, ImplItemKind, ItemKind, Node}; +use rustc_lint::LateContext; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +use super::RENAMED_FUNCTION_PARAMS; + +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>) { + if let ImplItemKind::Fn(_, body_id) = item.kind && + let Some(did) = impled_item_def_id(cx, item.owner_id) + { + let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id); + let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied(); + + let renames = renamed_params(&mut default_param_idents_iter, &mut param_idents_iter); + // FIXME: Should we use `MultiSpan` to combine output together? + // But how should we display help message if so. + for rename in renames { + span_lint_and_help( + cx, + RENAMED_FUNCTION_PARAMS, + rename.renamed_span, + "function parameter name was renamed from its trait default", + None, + &format!("consider changing the name to: '{}'", rename.default_name.as_str()) + ); + } + } +} + +struct RenamedParam { + renamed_span: Span, + default_name: Symbol, +} + +fn renamed_params(default_names: &mut I, current_names: &mut T) -> Vec +where + I: Iterator, + T: Iterator, +{ + let mut renamed = vec![]; + // FIXME: Should we stop if they have different length? + while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) { + let current_name = cur_name.name; + let default_name = def_name.name; + if is_ignored_or_empty_symbol(current_name) || is_ignored_or_empty_symbol(default_name) { + continue; + } + if current_name != default_name { + renamed.push(RenamedParam { + renamed_span: cur_name.span, + default_name, + }); + } + } + renamed +} + +fn is_ignored_or_empty_symbol(symbol: Symbol) -> bool { + let s = symbol.as_str(); + s.is_empty() || s.starts_with('_') +} + +fn impled_item_def_id(cx: &LateContext<'_>, impl_item_id: OwnerId) -> Option { + let trait_node = cx.tcx.hir().find_parent(impl_item_id.into())?; + if let Node::Item(item) = trait_node && + let ItemKind::Impl(impl_) = &item.kind + { + impl_.items.iter().find_map(|item| { + if item.id.owner_id == impl_item_id { + item.trait_item_def_id + } else { + None + } + }) + } else { + None + } +} diff --git a/tests/ui/renamed_function_params.rs b/tests/ui/renamed_function_params.rs new file mode 100644 index 0000000000000..ccc11968bcc67 --- /dev/null +++ b/tests/ui/renamed_function_params.rs @@ -0,0 +1,68 @@ +#![warn(clippy::renamed_function_params)] +#![allow(clippy::partialeq_ne_impl)] +#![allow(unused)] + +use std::hash::{Hash, Hasher}; + +struct A; +impl From for String { + fn from(_value: A) -> Self { + String::new() + } +} +impl ToString for A { + fn to_string(&self) -> String { + String::new() + } +} + +struct B(u32); +impl From for String { + fn from(b: B) -> Self { + //~^ ERROR: function parameter name was renamed from its trait default + b.0.to_string() + } +} +impl PartialEq for B { + fn eq(&self, rhs: &Self) -> bool { + //~^ ERROR: function parameter name was renamed from its trait default + self.0 == rhs.0 + } + fn ne(&self, rhs: &Self) -> bool { + //~^ ERROR: function parameter name was renamed from its trait default + self.0 != rhs.0 + } +} + +trait MyTrait { + fn foo(&self, val: u8); + fn bar(a: u8, b: u8); + fn baz(self, _val: u8); +} + +impl MyTrait for B { + fn foo(&self, i_dont_wanna_use_your_name: u8) {} + //~^ ERROR: function parameter name was renamed from its trait default + fn bar(_a: u8, _b: u8) {} + fn baz(self, val: u8) {} +} + +impl Hash for B { + fn hash(&self, states: &mut H) { + //~^ ERROR: function parameter name was renamed from its trait default + self.0.hash(states); + } + fn hash_slice(date: &[Self], states: &mut H) { + //~^ ERROR: function parameter name was renamed from its trait default + for d in date { + d.hash(states); + } + } +} + +impl B { + fn totally_irrelevant(&self, right: bool) {} + fn some_fn(&self, other: impl MyTrait) {} +} + +fn main() {} diff --git a/tests/ui/renamed_function_params.stderr b/tests/ui/renamed_function_params.stderr new file mode 100644 index 0000000000000..e42931a57b6cd --- /dev/null +++ b/tests/ui/renamed_function_params.stderr @@ -0,0 +1,60 @@ +error: function parameter name was renamed from its trait default + --> $DIR/renamed_function_params.rs:21:13 + | +LL | fn from(b: B) -> Self { + | ^ + | + = help: consider changing the name to: 'value' + = note: `-D clippy::renamed-function-params` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]` + +error: function parameter name was renamed from its trait default + --> $DIR/renamed_function_params.rs:27:18 + | +LL | fn eq(&self, rhs: &Self) -> bool { + | ^^^ + | + = help: consider changing the name to: 'other' + +error: function parameter name was renamed from its trait default + --> $DIR/renamed_function_params.rs:31:18 + | +LL | fn ne(&self, rhs: &Self) -> bool { + | ^^^ + | + = help: consider changing the name to: 'other' + +error: function parameter name was renamed from its trait default + --> $DIR/renamed_function_params.rs:44:19 + | +LL | fn foo(&self, i_dont_wanna_use_your_name: u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider changing the name to: 'val' + +error: function parameter name was renamed from its trait default + --> $DIR/renamed_function_params.rs:51:31 + | +LL | fn hash(&self, states: &mut H) { + | ^^^^^^ + | + = help: consider changing the name to: 'state' + +error: function parameter name was renamed from its trait default + --> $DIR/renamed_function_params.rs:55:30 + | +LL | fn hash_slice(date: &[Self], states: &mut H) { + | ^^^^ + | + = help: consider changing the name to: 'data' + +error: function parameter name was renamed from its trait default + --> $DIR/renamed_function_params.rs:55:45 + | +LL | fn hash_slice(date: &[Self], states: &mut H) { + | ^^^^^^ + | + = help: consider changing the name to: 'state' + +error: aborting due to 7 previous errors + From 40ec76057295e0a517a888cb4347f86c32f7243a Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Thu, 28 Mar 2024 16:29:52 +0800 Subject: [PATCH 27/58] fix doc test failure; apply review suggestions by @Centri3: use multi suggestion; change output message format; add macro expansion check & tests; --- clippy_lints/src/functions/mod.rs | 4 + .../src/functions/renamed_function_params.rs | 106 +++++++++++------- tests/ui/renamed_function_params.rs | 57 ++++++++-- tests/ui/renamed_function_params.stderr | 60 +++++----- 4 files changed, 142 insertions(+), 85 deletions(-) diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 387e0c964ccce..9a09dbaed4adc 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -371,6 +371,8 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// struct A(u32); + /// /// impl From for String { /// fn from(a: A) -> Self { /// a.0.to_string() @@ -379,6 +381,8 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust + /// struct A(u32); + /// /// impl From for String { /// fn from(value: A) -> Self { /// value.0.to_string() diff --git a/clippy_lints/src/functions/renamed_function_params.rs b/clippy_lints/src/functions/renamed_function_params.rs index 4c0d5aba11222..cea76996f052a 100644 --- a/clippy_lints/src/functions/renamed_function_params.rs +++ b/clippy_lints/src/functions/renamed_function_params.rs @@ -1,73 +1,93 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::OwnerId; use rustc_hir::{ImplItem, ImplItemKind, ItemKind, Node}; use rustc_lint::LateContext; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; use super::RENAMED_FUNCTION_PARAMS; pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>) { - if let ImplItemKind::Fn(_, body_id) = item.kind && - let Some(did) = impled_item_def_id(cx, item.owner_id) + if !item.span.from_expansion() + && let ImplItemKind::Fn(_, body_id) = item.kind + && let Some(did) = trait_item_def_id_of_impl(cx, item.owner_id) { let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id); let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied(); - let renames = renamed_params(&mut default_param_idents_iter, &mut param_idents_iter); - // FIXME: Should we use `MultiSpan` to combine output together? - // But how should we display help message if so. - for rename in renames { - span_lint_and_help( + let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter); + if !renames.0.is_empty() { + let multi_span = renames.multi_span(); + let plural = if renames.0.len() == 1 { "" } else { "s" }; + span_lint_and_then( cx, RENAMED_FUNCTION_PARAMS, - rename.renamed_span, - "function parameter name was renamed from its trait default", - None, - &format!("consider changing the name to: '{}'", rename.default_name.as_str()) + multi_span, + &format!("renamed function parameter{plural} of trait impl"), + |diag| { + diag.multipart_suggestion( + format!("consider using the default name{plural}"), + renames.0, + Applicability::Unspecified, + ); + }, ); } } } -struct RenamedParam { - renamed_span: Span, - default_name: Symbol, -} +struct RenamedFnArgs(Vec<(Span, String)>); -fn renamed_params(default_names: &mut I, current_names: &mut T) -> Vec -where - I: Iterator, - T: Iterator, -{ - let mut renamed = vec![]; - // FIXME: Should we stop if they have different length? - while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) { - let current_name = cur_name.name; - let default_name = def_name.name; - if is_ignored_or_empty_symbol(current_name) || is_ignored_or_empty_symbol(default_name) { - continue; - } - if current_name != default_name { - renamed.push(RenamedParam { - renamed_span: cur_name.span, - default_name, - }); +impl RenamedFnArgs { + /// Comparing between an iterator of default names and one with current names, + /// then collect the ones that got renamed. + fn new(default_names: &mut I, current_names: &mut T) -> Self + where + I: Iterator, + T: Iterator, + { + let mut renamed: Vec<(Span, String)> = vec![]; + + debug_assert!(default_names.size_hint() == current_names.size_hint()); + while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) { + let current_name = cur_name.name; + let default_name = def_name.name; + if is_unused_or_empty_symbol(current_name) || is_unused_or_empty_symbol(default_name) { + continue; + } + if current_name != default_name { + renamed.push((cur_name.span, default_name.to_string())); + } } + + Self(renamed) + } + + fn multi_span(&self) -> MultiSpan { + self.0 + .iter() + .map(|(span, _)| span) + .copied() + .collect::>() + .into() } - renamed } -fn is_ignored_or_empty_symbol(symbol: Symbol) -> bool { - let s = symbol.as_str(); - s.is_empty() || s.starts_with('_') +fn is_unused_or_empty_symbol(symbol: Symbol) -> bool { + // FIXME: `body_param_names` currently returning empty symbols for `wild` as well, + // so we need to check if the symbol is empty first. + // Therefore the check of whether it's equal to [`kw::Underscore`] has no use for now, + // but it would be nice to keep it here just to be future-proof. + symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_') } -fn impled_item_def_id(cx: &LateContext<'_>, impl_item_id: OwnerId) -> Option { - let trait_node = cx.tcx.hir().find_parent(impl_item_id.into())?; - if let Node::Item(item) = trait_node && - let ItemKind::Impl(impl_) = &item.kind +/// Get the [`trait_item_def_id`](rustc_hir::hir::ImplItemRef::trait_item_def_id) of an impl item. +fn trait_item_def_id_of_impl(cx: &LateContext<'_>, impl_item_id: OwnerId) -> Option { + let trait_node = cx.tcx.parent_hir_node(impl_item_id.into()); + if let Node::Item(item) = trait_node + && let ItemKind::Impl(impl_) = &item.kind { impl_.items.iter().find_map(|item| { if item.id.owner_id == impl_item_id { diff --git a/tests/ui/renamed_function_params.rs b/tests/ui/renamed_function_params.rs index ccc11968bcc67..4f06ae706dde0 100644 --- a/tests/ui/renamed_function_params.rs +++ b/tests/ui/renamed_function_params.rs @@ -1,5 +1,6 @@ +//@no-rustfix #![warn(clippy::renamed_function_params)] -#![allow(clippy::partialeq_ne_impl)] +#![allow(clippy::partialeq_ne_impl, clippy::to_string_trait_impl)] #![allow(unused)] use std::hash::{Hash, Hasher}; @@ -19,17 +20,17 @@ impl ToString for A { struct B(u32); impl From for String { fn from(b: B) -> Self { - //~^ ERROR: function parameter name was renamed from its trait default + //~^ ERROR: renamed function parameter of trait impl b.0.to_string() } } impl PartialEq for B { fn eq(&self, rhs: &Self) -> bool { - //~^ ERROR: function parameter name was renamed from its trait default + //~^ ERROR: renamed function parameter of trait impl self.0 == rhs.0 } fn ne(&self, rhs: &Self) -> bool { - //~^ ERROR: function parameter name was renamed from its trait default + //~^ ERROR: renamed function parameter of trait impl self.0 != rhs.0 } } @@ -38,22 +39,24 @@ trait MyTrait { fn foo(&self, val: u8); fn bar(a: u8, b: u8); fn baz(self, _val: u8); + fn quz(&self, _: u8); } impl MyTrait for B { fn foo(&self, i_dont_wanna_use_your_name: u8) {} - //~^ ERROR: function parameter name was renamed from its trait default - fn bar(_a: u8, _b: u8) {} + //~^ ERROR: renamed function parameter of trait impl + fn bar(_a: u8, _: u8) {} fn baz(self, val: u8) {} + fn quz(&self, val: u8) {} } impl Hash for B { fn hash(&self, states: &mut H) { - //~^ ERROR: function parameter name was renamed from its trait default + //~^ ERROR: renamed function parameter of trait impl self.0.hash(states); } fn hash_slice(date: &[Self], states: &mut H) { - //~^ ERROR: function parameter name was renamed from its trait default + //~^ ERROR: renamed function parameters of trait impl for d in date { d.hash(states); } @@ -65,4 +68,42 @@ impl B { fn some_fn(&self, other: impl MyTrait) {} } +#[derive(Copy, Clone)] +enum C { + A, + B(u32), +} + +impl std::ops::Add for C { + type Output = C; + fn add(self, b: B) -> C { + //~^ ERROR: renamed function parameter of trait impl + C::B(b.0) + } +} + +impl From for C { + fn from(_: A) -> C { + C::A + } +} + +trait CustomTraitA { + fn foo(&self, other: u32); +} +trait CustomTraitB { + fn bar(&self, value: u8); +} + +macro_rules! impl_trait { + ($impl_for:ident, $tr:ty, $fn_name:ident, $t:ty) => { + impl $tr for $impl_for { + fn $fn_name(&self, v: $t) {} + } + }; +} + +impl_trait!(C, CustomTraitA, foo, u32); +impl_trait!(C, CustomTraitB, bar, u8); + fn main() {} diff --git a/tests/ui/renamed_function_params.stderr b/tests/ui/renamed_function_params.stderr index e42931a57b6cd..7193541edb626 100644 --- a/tests/ui/renamed_function_params.stderr +++ b/tests/ui/renamed_function_params.stderr @@ -1,60 +1,52 @@ -error: function parameter name was renamed from its trait default - --> $DIR/renamed_function_params.rs:21:13 +error: renamed function parameter of trait impl + --> tests/ui/renamed_function_params.rs:22:13 | LL | fn from(b: B) -> Self { - | ^ + | ^ help: consider using the default name: `value` | - = help: consider changing the name to: 'value' = note: `-D clippy::renamed-function-params` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]` -error: function parameter name was renamed from its trait default - --> $DIR/renamed_function_params.rs:27:18 +error: renamed function parameter of trait impl + --> tests/ui/renamed_function_params.rs:28:18 | LL | fn eq(&self, rhs: &Self) -> bool { - | ^^^ - | - = help: consider changing the name to: 'other' + | ^^^ help: consider using the default name: `other` -error: function parameter name was renamed from its trait default - --> $DIR/renamed_function_params.rs:31:18 +error: renamed function parameter of trait impl + --> tests/ui/renamed_function_params.rs:32:18 | LL | fn ne(&self, rhs: &Self) -> bool { - | ^^^ - | - = help: consider changing the name to: 'other' + | ^^^ help: consider using the default name: `other` -error: function parameter name was renamed from its trait default - --> $DIR/renamed_function_params.rs:44:19 +error: renamed function parameter of trait impl + --> tests/ui/renamed_function_params.rs:46:19 | LL | fn foo(&self, i_dont_wanna_use_your_name: u8) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider changing the name to: 'val' + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the default name: `val` -error: function parameter name was renamed from its trait default - --> $DIR/renamed_function_params.rs:51:31 +error: renamed function parameter of trait impl + --> tests/ui/renamed_function_params.rs:54:31 | LL | fn hash(&self, states: &mut H) { - | ^^^^^^ - | - = help: consider changing the name to: 'state' + | ^^^^^^ help: consider using the default name: `state` -error: function parameter name was renamed from its trait default - --> $DIR/renamed_function_params.rs:55:30 +error: renamed function parameters of trait impl + --> tests/ui/renamed_function_params.rs:58:30 | LL | fn hash_slice(date: &[Self], states: &mut H) { - | ^^^^ + | ^^^^ ^^^^^^ | - = help: consider changing the name to: 'data' - -error: function parameter name was renamed from its trait default - --> $DIR/renamed_function_params.rs:55:45 +help: consider using the default names | -LL | fn hash_slice(date: &[Self], states: &mut H) { - | ^^^^^^ +LL | fn hash_slice(data: &[Self], state: &mut H) { + | ~~~~ ~~~~~ + +error: renamed function parameter of trait impl + --> tests/ui/renamed_function_params.rs:79:18 | - = help: consider changing the name to: 'state' +LL | fn add(self, b: B) -> C { + | ^ help: consider using the default name: `rhs` error: aborting due to 7 previous errors From a8c35cbbda8bc274cd41146a44956fcb691e6277 Mon Sep 17 00:00:00 2001 From: cocodery Date: Tue, 7 May 2024 16:07:13 +0800 Subject: [PATCH 28/58] Check inner caller for clone and judge whether they are mutable if immutbale -> lint delete redudant clone if mutable -> lint check whether clone is needed --- .../src/methods/unnecessary_iter_cloned.rs | 53 ++++++++++++++++++- tests/ui/unnecessary_iter_cloned.fixed | 32 +++++++++++ tests/ui/unnecessary_iter_cloned.rs | 32 +++++++++++ tests/ui/unnecessary_iter_cloned.stderr | 4 +- 4 files changed, 117 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 520dcb2d52dcb..7431dc1cf0b16 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -3,10 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; -use clippy_utils::{fn_def_id, get_parent_expr}; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; +use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; use rustc_span::{sym, Symbol}; @@ -40,6 +42,53 @@ pub fn check_for_loop_iter( && !clone_or_copy_needed && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) { + // Issue 12098 + // https://github.com/rust-lang/rust-clippy/issues/12098 + // if the assignee have `mut borrow` conflict with the iteratee + // the lint should not execute, former didn't consider the mut case + + // check whether `expr` is mutable + fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(hir_id) = path_to_local(expr) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + { + matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) + } else { + true + } + } + + fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool { + let mut change = false; + if let ExprKind::Block(block, ..) = body.kind { + for_each_expr(block, |e| { + match e.kind { + ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => { + change |= !can_mut_borrow_both(cx, caller, assignee); + }, + _ => {}, + } + // the return value has no effect but the function need one return value + ControlFlow::<()>::Continue(()) + }); + } + change + } + + if let ExprKind::Call(_, [child, ..]) = expr.kind { + // filter first layer of iterator + let mut child = child; + // get inner real caller requests for clone + while let ExprKind::MethodCall(_, caller, _, _) = child.kind { + child = caller; + } + if is_mutable(cx, child) && is_caller_or_fields_change(cx, body, child) { + // skip lint + return true; + } + }; + + // the lint should not be executed if no violation happens let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind && maybe_iter_method_name.ident.name == sym::iter && let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) diff --git a/tests/ui/unnecessary_iter_cloned.fixed b/tests/ui/unnecessary_iter_cloned.fixed index ad0e5fab029ee..2c582c90ba8ce 100644 --- a/tests/ui/unnecessary_iter_cloned.fixed +++ b/tests/ui/unnecessary_iter_cloned.fixed @@ -21,6 +21,8 @@ fn main() { let _ = check_files_ref_mut(&[(FileType::Account, path)]); let _ = check_files_self_and_arg(&[(FileType::Account, path)]); let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); + + check_mut_iteratee_and_modify_inner_variable(); } // `check_files` and its variants are based on: @@ -138,3 +140,33 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { fn get_file_path(_file_type: &FileType) -> Result { Ok(std::path::PathBuf::new()) } + +// Issue 12098 +// https://github.com/rust-lang/rust-clippy/issues/12098 +// no message emits +fn check_mut_iteratee_and_modify_inner_variable() { + struct Test { + list: Vec, + mut_this: bool, + } + + impl Test { + fn list(&self) -> &[String] { + &self.list + } + } + + let mut test = Test { + list: vec![String::from("foo"), String::from("bar")], + mut_this: false, + }; + + for _item in test.list().to_vec() { + println!("{}", _item); + + test.mut_this = true; + { + test.mut_this = true; + } + } +} diff --git a/tests/ui/unnecessary_iter_cloned.rs b/tests/ui/unnecessary_iter_cloned.rs index d3d59c4c70f5c..a28ccd1efef26 100644 --- a/tests/ui/unnecessary_iter_cloned.rs +++ b/tests/ui/unnecessary_iter_cloned.rs @@ -21,6 +21,8 @@ fn main() { let _ = check_files_ref_mut(&[(FileType::Account, path)]); let _ = check_files_self_and_arg(&[(FileType::Account, path)]); let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); + + check_mut_iteratee_and_modify_inner_variable(); } // `check_files` and its variants are based on: @@ -138,3 +140,33 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { fn get_file_path(_file_type: &FileType) -> Result { Ok(std::path::PathBuf::new()) } + +// Issue 12098 +// https://github.com/rust-lang/rust-clippy/issues/12098 +// no message emits +fn check_mut_iteratee_and_modify_inner_variable() { + struct Test { + list: Vec, + mut_this: bool, + } + + impl Test { + fn list(&self) -> &[String] { + &self.list + } + } + + let mut test = Test { + list: vec![String::from("foo"), String::from("bar")], + mut_this: false, + }; + + for _item in test.list().to_vec() { + println!("{}", _item); + + test.mut_this = true; + { + test.mut_this = true; + } + } +} diff --git a/tests/ui/unnecessary_iter_cloned.stderr b/tests/ui/unnecessary_iter_cloned.stderr index 9d3591e0dbfc8..fb98cfddc262f 100644 --- a/tests/ui/unnecessary_iter_cloned.stderr +++ b/tests/ui/unnecessary_iter_cloned.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:29:22 + --> tests/ui/unnecessary_iter_cloned.rs:31:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL + let other = match get_file_path(t) { | error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:44:22 + --> tests/ui/unnecessary_iter_cloned.rs:46:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ From 362ef42b68567bfa963f53cce1dc7419b0d64ef6 Mon Sep 17 00:00:00 2001 From: roife Date: Wed, 8 May 2024 13:38:00 +0800 Subject: [PATCH 29/58] fix: merge multiple suggestions into a single multi-span suggestion in `needless_late_init` --- clippy_lints/src/needless_late_init.rs | 69 ++++------ tests/ui/needless_late_init.stderr | 177 +++++++++++-------------- 2 files changed, 104 insertions(+), 142 deletions(-) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 6605d1fa51a36..5a0ae1a4d6d29 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -273,24 +273,16 @@ fn check<'tcx>( msg_span, "unneeded late initialization", |diag| { - diag.tool_only_span_suggestion( - local_stmt.span, - "remove the local", - "", - Applicability::MachineApplicable, - ); - - diag.span_suggestion( - assign.lhs_span, - format!("declare `{binding_name}` here"), - let_snippet, + diag.multipart_suggestion( + format!("move the declaration `{binding_name}` here"), + vec![(local_stmt.span, String::new()), (assign.lhs_span, let_snippet)], Applicability::MachineApplicable, ); }, ); }, ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { - let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; + let (applicability, mut suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; span_lint_and_then( cx, @@ -298,30 +290,26 @@ fn check<'tcx>( local_stmt.span, "unneeded late initialization", |diag| { - diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); - - diag.span_suggestion_verbose( - usage.stmt.span.shrink_to_lo(), - format!("declare `{binding_name}` here"), - format!("{let_snippet} = "), - applicability, - ); - - diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability); + suggestions.push((local_stmt.span, String::new())); + suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); if usage.needs_semi { - diag.span_suggestion( - usage.stmt.span.shrink_to_hi(), - "add a semicolon after the `if` expression", - ";", - applicability, - ); + suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); } + + diag.multipart_suggestion( + format!( + "move the declaration `{binding_name}` here and remove the assignments from the branches" + ), + suggestions, + applicability, + ); }, ); }, ExprKind::Match(_, arms, MatchSource::Normal) => { - let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; + let (applicability, mut suggestions) = + assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; span_lint_and_then( cx, @@ -329,29 +317,18 @@ fn check<'tcx>( local_stmt.span, "unneeded late initialization", |diag| { - diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); + suggestions.push((local_stmt.span, String::new())); + suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); - diag.span_suggestion_verbose( - usage.stmt.span.shrink_to_lo(), - format!("declare `{binding_name}` here"), - format!("{let_snippet} = "), - applicability, - ); + if usage.needs_semi { + suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); + } diag.multipart_suggestion( - "remove the assignments from the `match` arms", + format!("move the declaration `{binding_name}` here and remove the assignments from the `match` arms"), suggestions, applicability, ); - - if usage.needs_semi { - diag.span_suggestion( - usage.stmt.span.shrink_to_hi(), - "add a semicolon after the `match` expression", - ";", - applicability, - ); - } }, ); }, diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 1695784030d02..ce64861fa40a7 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -8,10 +8,11 @@ LL | a = "zero"; | = note: `-D clippy::needless-late-init` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_late_init)]` -help: declare `a` here +help: move the declaration `a` here + | +LL ~ +LL ~ let a = "zero"; | -LL | let a = "zero"; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:30:5 @@ -22,10 +23,12 @@ LL | let c; LL | b = 1; | ^^^^^ initialised here | -help: declare `b` here +help: move the declaration `b` here + | +LL ~ +LL | let c; +LL ~ let b = 1; | -LL | let b = 1; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:31:5 @@ -36,10 +39,12 @@ LL | b = 1; LL | c = 2; | ^^^^^ initialised here | -help: declare `c` here +help: move the declaration `c` here + | +LL ~ +LL | b = 1; +LL ~ let c = 2; | -LL | let c = 2; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:35:5 @@ -49,10 +54,11 @@ LL | let d: usize; LL | d = 1; | ^^^^^ initialised here | -help: declare `d` here +help: move the declaration `d` here + | +LL ~ +LL ~ let d: usize = 1; | -LL | let d: usize = 1; - | ~~~~~~~~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:38:5 @@ -62,10 +68,11 @@ LL | let e; LL | e = format!("{}", d); | ^^^^^^^^^^^^^^^^^^^^ initialised here | -help: declare `e` here +help: move the declaration `e` here + | +LL ~ +LL ~ let e = format!("{}", d); | -LL | let e = format!("{}", d); - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:43:5 @@ -73,20 +80,17 @@ error: unneeded late initialization LL | let a; | ^^^^^^ | -help: declare `a` here - | -LL | let a = match n { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `a` here and remove the assignments from the `match` arms | +LL ~ +LL | let n = 1; +LL ~ let a = match n { LL ~ 1 => "one", LL | _ => { LL ~ "two" +LL | }, +LL ~ }; | -help: add a semicolon after the `match` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:52:5 @@ -94,20 +98,15 @@ error: unneeded late initialization LL | let b; | ^^^^^^ | -help: declare `b` here - | -LL | let b = if n == 3 { - | +++++++ -help: remove the assignments from the branches +help: move the declaration `b` here and remove the assignments from the branches | +LL ~ +LL ~ let b = if n == 3 { LL ~ "four" LL | } else { LL ~ "five" +LL ~ }; | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:59:5 @@ -115,20 +114,16 @@ error: unneeded late initialization LL | let d; | ^^^^^^ | -help: declare `d` here - | -LL | let d = if true { - | +++++++ -help: remove the assignments from the branches +help: move the declaration `d` here and remove the assignments from the branches | +LL ~ +LL ~ let d = if true { +LL | let temp = 5; LL ~ temp LL | } else { LL ~ 15 +LL ~ }; | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:67:5 @@ -136,20 +131,15 @@ error: unneeded late initialization LL | let e; | ^^^^^^ | -help: declare `e` here - | -LL | let e = if true { - | +++++++ -help: remove the assignments from the branches +help: move the declaration `e` here and remove the assignments from the branches | +LL ~ +LL ~ let e = if true { LL ~ format!("{} {}", a, b) LL | } else { LL ~ format!("{}", n) +LL ~ }; | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:74:5 @@ -157,14 +147,11 @@ error: unneeded late initialization LL | let f; | ^^^^^^ | -help: declare `f` here - | -LL | let f = match 1 { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `f` here and remove the assignments from the `match` arms | -LL - 1 => f = "three", -LL + 1 => "three", +LL ~ +LL ~ let f = match 1 { +LL ~ 1 => "three", | error: unneeded late initialization @@ -173,19 +160,15 @@ error: unneeded late initialization LL | let g: usize; | ^^^^^^^^^^^^^ | -help: declare `g` here - | -LL | let g: usize = if true { - | ++++++++++++++ -help: remove the assignments from the branches - | -LL - g = 5; -LL + 5 +help: move the declaration `g` here and remove the assignments from the branches | -help: add a semicolon after the `if` expression +LL ~ +LL ~ let g: usize = if true { +LL ~ 5 +LL | } else { +LL | panic!(); +LL ~ }; | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:88:5 @@ -196,10 +179,12 @@ LL | let y = SignificantDrop; LL | x = 1; | ^^^^^ initialised here | -help: declare `x` here +help: move the declaration `x` here + | +LL ~ +LL | let y = SignificantDrop; +LL ~ let x = 1; | -LL | let x = 1; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:92:5 @@ -210,10 +195,12 @@ LL | let y = 1; LL | x = SignificantDrop; | ^^^^^^^^^^^^^^^^^^^ initialised here | -help: declare `x` here +help: move the declaration `x` here + | +LL ~ +LL | let y = 1; +LL ~ let x = SignificantDrop; | -LL | let x = SignificantDrop; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:96:5 @@ -224,10 +211,14 @@ LL | let x; LL | x = SignificantDrop; | ^^^^^^^^^^^^^^^^^^^ initialised here | -help: declare `x` here +help: move the declaration `x` here + | +LL ~ +LL | // types that should be considered insignificant + ... +LL | let y = Box::new(4); +LL ~ let x = SignificantDrop; | -LL | let x = SignificantDrop; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:115:5 @@ -235,20 +226,17 @@ error: unneeded late initialization LL | let a; | ^^^^^^ | -help: declare `a` here - | -LL | let a = match n { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `a` here and remove the assignments from the `match` arms | +LL ~ +LL | let n = 1; +LL ~ let a = match n { LL ~ 1 => f().await, LL | _ => { LL ~ "two" +LL | }, +LL ~ }; | -help: add a semicolon after the `match` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:132:5 @@ -256,20 +244,17 @@ error: unneeded late initialization LL | let a; | ^^^^^^ | -help: declare `a` here - | -LL | let a = match n { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `a` here and remove the assignments from the `match` arms | +LL ~ +LL | let n = 1; +LL ~ let a = match n { LL ~ 1 => f(), LL | _ => { LL ~ "two" +LL | }, +LL ~ }; | -help: add a semicolon after the `match` expression - | -LL | }; - | + error: aborting due to 16 previous errors From dc8a455a002ba9672975899564a32be89a7a42d0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 8 May 2024 16:56:02 +1000 Subject: [PATCH 30/58] Simplify `use crate::rustc_foo::bar` occurrences. They can just be written as `use rustc_foo::bar`, which is far more standard. (I didn't even know that a `crate::` prefix was valid.) --- clippy_lints/src/manual_assert.rs | 3 +-- clippy_lints/src/methods/iter_overeager_cloned.rs | 2 +- clippy_lints/src/redundant_closure_call.rs | 3 +-- clippy_lints/src/semicolon_if_nothing_returned.rs | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index d76b94eba23ed..83c16d4466d06 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,10 +1,9 @@ -use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::{is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; declare_clippy_lint! { diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index 03b4680c52242..deac159457a66 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -9,10 +9,10 @@ use rustc_lint::LateContext; use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; use rustc_span::sym; +use rustc_trait_selection::infer::TyCtxtInferExt; use super::ITER_OVEREAGER_CLONED; use crate::redundant_clone::REDUNDANT_CLONE; -use crate::rustc_trait_selection::infer::TyCtxtInferExt; #[derive(Clone, Copy)] pub(super) enum Op<'a> { diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 2863eb190d341..47d3ed08b8ec1 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -1,4 +1,3 @@ -use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir}; use clippy_utils::get_parent_expr; use clippy_utils::sugg::Sugg; @@ -9,7 +8,7 @@ use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; use rustc_hir::{ intravisit as hir_visit, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 6540626f7d5a3..63237c655ef17 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,9 +1,8 @@ -use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::{ExpnKind, MacroKind, Span}; From 076f2e5dea94b02fbb4baf224a5cec2a2ef58427 Mon Sep 17 00:00:00 2001 From: Maarten de Vries Date: Wed, 8 May 2024 11:41:40 +0200 Subject: [PATCH 31/58] assigning_clones: move to pedantic so it is allow by default --- clippy_lints/src/assigning_clones.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 64cf7755c878d..4de763151736b 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -48,7 +48,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.78.0"] pub ASSIGNING_CLONES, - perf, + pedantic, "assigning the result of cloning may be inefficient" } From cd00f5b9e48419563d6d44f68b24de218c0f39e5 Mon Sep 17 00:00:00 2001 From: future-highway <113635015+future-highway@users.noreply.github.com> Date: Wed, 8 May 2024 09:35:29 -0400 Subject: [PATCH 32/58] Ignore `_to_string` lints in code `from_expansion` Includes the `string_to_string` and `str_to_string` lints. --- clippy_lints/src/strings.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 87a3c3874d77b..292124196ff64 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -389,6 +389,10 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.name == sym::to_string && let ty = cx.typeck_results().expr_ty(self_arg) @@ -437,6 +441,10 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.name == sym::to_string && let ty = cx.typeck_results().expr_ty(self_arg) From 7ad336f3a8948bd7a52cf02944c70280713e76dc Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Tue, 23 Apr 2024 21:15:33 +0200 Subject: [PATCH 33/58] Simplify `clippy` lint. --- clippy_lints/src/transmute/mod.rs | 10 +-- .../transmute/transmute_int_to_non_zero.rs | 35 ++------ tests/ui/transmute_int_to_non_zero.fixed | 62 +++++++------- tests/ui/transmute_int_to_non_zero.rs | 62 +++++++------- tests/ui/transmute_int_to_non_zero.stderr | 80 +++++++++---------- 5 files changed, 113 insertions(+), 136 deletions(-) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 7fa536a1a29d9..598032ccdebed 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -257,7 +257,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for transmutes from integers to `NonZero*` types, and suggests their `new_unchecked` + /// Checks for transmutes from `T` to `NonZero`, and suggests the `new_unchecked` /// method instead. /// /// ### Why is this bad? @@ -266,13 +266,13 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # use core::num::NonZeroU32; - /// let _non_zero: NonZeroU32 = unsafe { std::mem::transmute(123) }; + /// # use core::num::NonZero; + /// let _: NonZero = unsafe { std::mem::transmute(123) }; /// ``` /// Use instead: /// ```no_run - /// # use core::num::NonZeroU32; - /// let _non_zero = unsafe { NonZeroU32::new_unchecked(123) }; + /// # use core::num::NonZero; + /// let _: NonZero = unsafe { NonZero::new_unchecked(123) }; /// ``` #[clippy::version = "1.69.0"] pub TRANSMUTE_INT_TO_NON_ZERO, diff --git a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 2bea3be3d6030..7d824ef213900 100644 --- a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -26,45 +26,22 @@ pub(super) fn check<'tcx>( return false; }; - // FIXME: This can be simplified once `NonZero` is stable. - let coercible_types = [ - ("NonZeroU8", tcx.types.u8), - ("NonZeroU16", tcx.types.u16), - ("NonZeroU32", tcx.types.u32), - ("NonZeroU64", tcx.types.u64), - ("NonZeroU128", tcx.types.u128), - ("NonZeroUsize", tcx.types.usize), - ("NonZeroI8", tcx.types.i8), - ("NonZeroI16", tcx.types.i16), - ("NonZeroI32", tcx.types.i32), - ("NonZeroI64", tcx.types.i64), - ("NonZeroI128", tcx.types.i128), - ("NonZeroIsize", tcx.types.isize), - ]; - - let int_type = substs.type_at(0); - - let Some(nonzero_alias) = coercible_types.iter().find_map(|(nonzero_alias, t)| { - if *t == int_type && *t == from_ty { - Some(nonzero_alias) - } else { - None - } - }) else { - return false; - }; + let int_ty = substs.type_at(0); + if from_ty != int_ty { + return false; + } span_lint_and_then( cx, TRANSMUTE_INT_TO_NON_ZERO, e.span, - format!("transmute from a `{from_ty}` to a `{nonzero_alias}`"), + format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); diag.span_suggestion( e.span, "consider using", - format!("{nonzero_alias}::{}({arg})", sym::new_unchecked), + format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked), Applicability::Unspecified, ); }, diff --git a/tests/ui/transmute_int_to_non_zero.fixed b/tests/ui/transmute_int_to_non_zero.fixed index fe8db3dcb0cf9..1a48051ec8c4d 100644 --- a/tests/ui/transmute_int_to_non_zero.fixed +++ b/tests/ui/transmute_int_to_non_zero.fixed @@ -1,7 +1,7 @@ #![warn(clippy::transmute_int_to_non_zero)] #![allow(clippy::missing_transmute_annotations)] -use core::num::*; +use core::num::NonZero; fn main() { let int_u8: u8 = 1; @@ -16,38 +16,38 @@ fn main() { let int_i64: i64 = 1; let int_i128: i128 = 1; - let _: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(int_u8) }; - //~^ ERROR: transmute from a `u8` to a `NonZeroU8` + let _: NonZero = unsafe { NonZero::new_unchecked(int_u8) }; + //~^ ERROR: transmute from a `u8` to a `NonZero` //~| NOTE: `-D clippy::transmute-int-to-non-zero` implied by `-D warnings` - let _: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(int_u16) }; - //~^ ERROR: transmute from a `u16` to a `NonZeroU16` - let _: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(int_u32) }; - //~^ ERROR: transmute from a `u32` to a `NonZeroU32` - let _: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(int_u64) }; - //~^ ERROR: transmute from a `u64` to a `NonZeroU64` - let _: NonZeroU128 = unsafe { NonZeroU128::new_unchecked(int_u128) }; - //~^ ERROR: transmute from a `u128` to a `NonZeroU128` + let _: NonZero = unsafe { NonZero::new_unchecked(int_u16) }; + //~^ ERROR: transmute from a `u16` to a `NonZero` + let _: NonZero = unsafe { NonZero::new_unchecked(int_u32) }; + //~^ ERROR: transmute from a `u32` to a `NonZero` + let _: NonZero = unsafe { NonZero::new_unchecked(int_u64) }; + //~^ ERROR: transmute from a `u64` to a `NonZero` + let _: NonZero = unsafe { NonZero::new_unchecked(int_u128) }; + //~^ ERROR: transmute from a `u128` to a `NonZero` - let _: NonZeroI8 = unsafe { NonZeroI8::new_unchecked(int_i8) }; - //~^ ERROR: transmute from a `i8` to a `NonZeroI8` - let _: NonZeroI16 = unsafe { NonZeroI16::new_unchecked(int_i16) }; - //~^ ERROR: transmute from a `i16` to a `NonZeroI16` - let _: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(int_i32) }; - //~^ ERROR: transmute from a `i32` to a `NonZeroI32` - let _: NonZeroI64 = unsafe { NonZeroI64::new_unchecked(int_i64) }; - //~^ ERROR: transmute from a `i64` to a `NonZeroI64` - let _: NonZeroI128 = unsafe { NonZeroI128::new_unchecked(int_i128) }; - //~^ ERROR: transmute from a `i128` to a `NonZeroI128` + let _: NonZero = unsafe { NonZero::new_unchecked(int_i8) }; + //~^ ERROR: transmute from a `i8` to a `NonZero` + let _: NonZero = unsafe { NonZero::new_unchecked(int_i16) }; + //~^ ERROR: transmute from a `i16` to a `NonZero` + let _: NonZero = unsafe { NonZero::new_unchecked(int_i32) }; + //~^ ERROR: transmute from a `i32` to a `NonZero` + let _: NonZero = unsafe { NonZero::new_unchecked(int_i64) }; + //~^ ERROR: transmute from a `i64` to a `NonZero` + let _: NonZero = unsafe { NonZero::new_unchecked(int_i128) }; + //~^ ERROR: transmute from a `i128` to a `NonZero` - let _: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(int_u8) }; - let _: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(int_u16) }; - let _: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(int_u32) }; - let _: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(int_u64) }; - let _: NonZeroU128 = unsafe { NonZeroU128::new_unchecked(int_u128) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u8) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u16) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u32) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u64) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u128) }; - let _: NonZeroI8 = unsafe { NonZeroI8::new_unchecked(int_i8) }; - let _: NonZeroI16 = unsafe { NonZeroI16::new_unchecked(int_i16) }; - let _: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(int_i32) }; - let _: NonZeroI64 = unsafe { NonZeroI64::new_unchecked(int_i64) }; - let _: NonZeroI128 = unsafe { NonZeroI128::new_unchecked(int_i128) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i8) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i16) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i32) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i64) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i128) }; } diff --git a/tests/ui/transmute_int_to_non_zero.rs b/tests/ui/transmute_int_to_non_zero.rs index a79ed5279b1f2..d8e842fb99ceb 100644 --- a/tests/ui/transmute_int_to_non_zero.rs +++ b/tests/ui/transmute_int_to_non_zero.rs @@ -1,7 +1,7 @@ #![warn(clippy::transmute_int_to_non_zero)] #![allow(clippy::missing_transmute_annotations)] -use core::num::*; +use core::num::NonZero; fn main() { let int_u8: u8 = 1; @@ -16,38 +16,38 @@ fn main() { let int_i64: i64 = 1; let int_i128: i128 = 1; - let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) }; - //~^ ERROR: transmute from a `u8` to a `NonZeroU8` + let _: NonZero = unsafe { std::mem::transmute(int_u8) }; + //~^ ERROR: transmute from a `u8` to a `NonZero` //~| NOTE: `-D clippy::transmute-int-to-non-zero` implied by `-D warnings` - let _: NonZeroU16 = unsafe { std::mem::transmute(int_u16) }; - //~^ ERROR: transmute from a `u16` to a `NonZeroU16` - let _: NonZeroU32 = unsafe { std::mem::transmute(int_u32) }; - //~^ ERROR: transmute from a `u32` to a `NonZeroU32` - let _: NonZeroU64 = unsafe { std::mem::transmute(int_u64) }; - //~^ ERROR: transmute from a `u64` to a `NonZeroU64` - let _: NonZeroU128 = unsafe { std::mem::transmute(int_u128) }; - //~^ ERROR: transmute from a `u128` to a `NonZeroU128` + let _: NonZero = unsafe { std::mem::transmute(int_u16) }; + //~^ ERROR: transmute from a `u16` to a `NonZero` + let _: NonZero = unsafe { std::mem::transmute(int_u32) }; + //~^ ERROR: transmute from a `u32` to a `NonZero` + let _: NonZero = unsafe { std::mem::transmute(int_u64) }; + //~^ ERROR: transmute from a `u64` to a `NonZero` + let _: NonZero = unsafe { std::mem::transmute(int_u128) }; + //~^ ERROR: transmute from a `u128` to a `NonZero` - let _: NonZeroI8 = unsafe { std::mem::transmute(int_i8) }; - //~^ ERROR: transmute from a `i8` to a `NonZeroI8` - let _: NonZeroI16 = unsafe { std::mem::transmute(int_i16) }; - //~^ ERROR: transmute from a `i16` to a `NonZeroI16` - let _: NonZeroI32 = unsafe { std::mem::transmute(int_i32) }; - //~^ ERROR: transmute from a `i32` to a `NonZeroI32` - let _: NonZeroI64 = unsafe { std::mem::transmute(int_i64) }; - //~^ ERROR: transmute from a `i64` to a `NonZeroI64` - let _: NonZeroI128 = unsafe { std::mem::transmute(int_i128) }; - //~^ ERROR: transmute from a `i128` to a `NonZeroI128` + let _: NonZero = unsafe { std::mem::transmute(int_i8) }; + //~^ ERROR: transmute from a `i8` to a `NonZero` + let _: NonZero = unsafe { std::mem::transmute(int_i16) }; + //~^ ERROR: transmute from a `i16` to a `NonZero` + let _: NonZero = unsafe { std::mem::transmute(int_i32) }; + //~^ ERROR: transmute from a `i32` to a `NonZero` + let _: NonZero = unsafe { std::mem::transmute(int_i64) }; + //~^ ERROR: transmute from a `i64` to a `NonZero` + let _: NonZero = unsafe { std::mem::transmute(int_i128) }; + //~^ ERROR: transmute from a `i128` to a `NonZero` - let _: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(int_u8) }; - let _: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(int_u16) }; - let _: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(int_u32) }; - let _: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(int_u64) }; - let _: NonZeroU128 = unsafe { NonZeroU128::new_unchecked(int_u128) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u8) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u16) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u32) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u64) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_u128) }; - let _: NonZeroI8 = unsafe { NonZeroI8::new_unchecked(int_i8) }; - let _: NonZeroI16 = unsafe { NonZeroI16::new_unchecked(int_i16) }; - let _: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(int_i32) }; - let _: NonZeroI64 = unsafe { NonZeroI64::new_unchecked(int_i64) }; - let _: NonZeroI128 = unsafe { NonZeroI128::new_unchecked(int_i128) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i8) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i16) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i32) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i64) }; + let _: NonZero = unsafe { NonZero::new_unchecked(int_i128) }; } diff --git a/tests/ui/transmute_int_to_non_zero.stderr b/tests/ui/transmute_int_to_non_zero.stderr index bb0b0d0ff4f06..199b8ec59d092 100644 --- a/tests/ui/transmute_int_to_non_zero.stderr +++ b/tests/ui/transmute_int_to_non_zero.stderr @@ -1,65 +1,65 @@ -error: transmute from a `u8` to a `NonZeroU8` - --> tests/ui/transmute_int_to_non_zero.rs:19:33 +error: transmute from a `u8` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:19:35 | -LL | let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU8::new_unchecked(int_u8)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_u8)` | = note: `-D clippy::transmute-int-to-non-zero` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_non_zero)]` -error: transmute from a `u16` to a `NonZeroU16` - --> tests/ui/transmute_int_to_non_zero.rs:22:34 +error: transmute from a `u16` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:22:36 | -LL | let _: NonZeroU16 = unsafe { std::mem::transmute(int_u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU16::new_unchecked(int_u16)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_u16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_u16)` -error: transmute from a `u32` to a `NonZeroU32` - --> tests/ui/transmute_int_to_non_zero.rs:24:34 +error: transmute from a `u32` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:24:36 | -LL | let _: NonZeroU32 = unsafe { std::mem::transmute(int_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU32::new_unchecked(int_u32)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_u32)` -error: transmute from a `u64` to a `NonZeroU64` - --> tests/ui/transmute_int_to_non_zero.rs:26:34 +error: transmute from a `u64` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:26:36 | -LL | let _: NonZeroU64 = unsafe { std::mem::transmute(int_u64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU64::new_unchecked(int_u64)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_u64)` -error: transmute from a `u128` to a `NonZeroU128` - --> tests/ui/transmute_int_to_non_zero.rs:28:35 +error: transmute from a `u128` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:28:37 | -LL | let _: NonZeroU128 = unsafe { std::mem::transmute(int_u128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU128::new_unchecked(int_u128)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_u128) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_u128)` -error: transmute from a `i8` to a `NonZeroI8` - --> tests/ui/transmute_int_to_non_zero.rs:31:33 +error: transmute from a `i8` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:31:35 | -LL | let _: NonZeroI8 = unsafe { std::mem::transmute(int_i8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI8::new_unchecked(int_i8)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_i8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_i8)` -error: transmute from a `i16` to a `NonZeroI16` - --> tests/ui/transmute_int_to_non_zero.rs:33:34 +error: transmute from a `i16` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:33:36 | -LL | let _: NonZeroI16 = unsafe { std::mem::transmute(int_i16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI16::new_unchecked(int_i16)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_i16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_i16)` -error: transmute from a `i32` to a `NonZeroI32` - --> tests/ui/transmute_int_to_non_zero.rs:35:34 +error: transmute from a `i32` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:35:36 | -LL | let _: NonZeroI32 = unsafe { std::mem::transmute(int_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI32::new_unchecked(int_i32)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_i32)` -error: transmute from a `i64` to a `NonZeroI64` - --> tests/ui/transmute_int_to_non_zero.rs:37:34 +error: transmute from a `i64` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:37:36 | -LL | let _: NonZeroI64 = unsafe { std::mem::transmute(int_i64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI64::new_unchecked(int_i64)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_i64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_i64)` -error: transmute from a `i128` to a `NonZeroI128` - --> tests/ui/transmute_int_to_non_zero.rs:39:35 +error: transmute from a `i128` to a `NonZero` + --> tests/ui/transmute_int_to_non_zero.rs:39:37 | -LL | let _: NonZeroI128 = unsafe { std::mem::transmute(int_i128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI128::new_unchecked(int_i128)` +LL | let _: NonZero = unsafe { std::mem::transmute(int_i128) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(int_i128)` error: aborting due to 10 previous errors From d32629891ababfdf1a5f8fe9e12daab8d6797c05 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 21 Apr 2024 21:45:18 +0200 Subject: [PATCH 34/58] Use generic `NonZero`. --- clippy_lints/src/transmute/eager_transmute.rs | 2 +- lintcheck/src/config.rs | 4 ++-- tests/ui/arithmetic_side_effects.rs | 8 ++++---- tests/ui/eager_transmute.fixed | 20 +++++++++---------- tests/ui/eager_transmute.rs | 20 +++++++++---------- tests/ui/eager_transmute.stderr | 18 ++++++++--------- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/transmute/eager_transmute.rs b/clippy_lints/src/transmute/eager_transmute.rs index c44f5150dd1a9..1dfc9f7091e8c 100644 --- a/clippy_lints/src/transmute/eager_transmute.rs +++ b/clippy_lints/src/transmute/eager_transmute.rs @@ -87,7 +87,7 @@ pub(super) fn check<'tcx>( && is_normalizable(cx, cx.param_env, from_ty) && is_normalizable(cx, cx.param_env, to_ty) // we only want to lint if the target type has a niche that is larger than the one of the source type - // e.g. `u8` to `NonZeroU8` should lint, but `NonZeroU8` to `u8` should not + // e.g. `u8` to `NonZero` should lint, but `NonZero` to `u8` should not && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from_ty)) && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to_ty)) && match (from_layout.largest_niche, to_layout.largest_niche) { diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index e678d40795ee8..3f712f453fa0a 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -1,5 +1,5 @@ use clap::Parser; -use std::num::NonZeroUsize; +use std::num::NonZero; use std::path::PathBuf; #[derive(Clone, Debug, Parser)] @@ -61,7 +61,7 @@ impl LintcheckConfig { config.max_jobs = if config.fix || config.recursive { 1 } else { - std::thread::available_parallelism().map_or(1, NonZeroUsize::get) + std::thread::available_parallelism().map_or(1, NonZero::get) }; }; diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index 66d71f337f2f6..33a91e8bbbe53 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -15,7 +15,7 @@ extern crate proc_macro_derive; -use core::num::{NonZeroUsize, Saturating, Wrapping}; +use core::num::{NonZero, Saturating, Wrapping}; const ONE: i32 = 1; const ZERO: i32 = 0; @@ -494,15 +494,15 @@ pub fn issue_11262() { } pub fn issue_11392() { - fn example_div(unsigned: usize, nonzero_unsigned: NonZeroUsize) -> usize { + fn example_div(unsigned: usize, nonzero_unsigned: NonZero) -> usize { unsigned / nonzero_unsigned } - fn example_rem(unsigned: usize, nonzero_unsigned: NonZeroUsize) -> usize { + fn example_rem(unsigned: usize, nonzero_unsigned: NonZero) -> usize { unsigned % nonzero_unsigned } - let (unsigned, nonzero_unsigned) = (0, NonZeroUsize::new(1).unwrap()); + let (unsigned, nonzero_unsigned) = (0, NonZero::new(1).unwrap()); example_div(unsigned, nonzero_unsigned); example_rem(unsigned, nonzero_unsigned); } diff --git a/tests/ui/eager_transmute.fixed b/tests/ui/eager_transmute.fixed index c29e7dd9ab3ea..ba4342462dca0 100644 --- a/tests/ui/eager_transmute.fixed +++ b/tests/ui/eager_transmute.fixed @@ -2,7 +2,7 @@ #![warn(clippy::eager_transmute)] #![allow(clippy::transmute_int_to_non_zero, clippy::missing_transmute_annotations)] -use std::num::NonZeroU8; +use std::num::NonZero; #[repr(u8)] enum Opcode { @@ -85,21 +85,21 @@ macro_rules! impls { } impls!(NonMaxU8, NonZeroNonMaxU8); -fn niche_tests(v1: u8, v2: NonZeroU8, v3: NonZeroNonMaxU8) { - // u8 -> NonZeroU8, do lint - let _: Option = (v1 > 0).then(|| unsafe { std::mem::transmute(v1) }); +fn niche_tests(v1: u8, v2: NonZero, v3: NonZeroNonMaxU8) { + // u8 -> NonZero, do lint + let _: Option> = (v1 > 0).then(|| unsafe { std::mem::transmute(v1) }); - // NonZeroU8 -> u8, don't lint, target type has no niche and therefore a higher validity range - let _: Option = (v2 > NonZeroU8::new(1).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); + // NonZero -> u8, don't lint, target type has no niche and therefore a higher validity range + let _: Option = (v2 > NonZero::new(1u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); - // NonZeroU8 -> NonMaxU8, do lint, different niche - let _: Option = (v2 < NonZeroU8::new(255).unwrap()).then(|| unsafe { std::mem::transmute(v2) }); + // NonZero -> NonMaxU8, do lint, different niche + let _: Option = (v2 < NonZero::new(255u8).unwrap()).then(|| unsafe { std::mem::transmute(v2) }); // NonZeroNonMaxU8 -> NonMaxU8, don't lint, target type has more validity let _: Option = (v3 < 255).then_some(unsafe { std::mem::transmute(v2) }); - // NonZeroU8 -> NonZeroNonMaxU8, do lint, target type has less validity - let _: Option = (v2 < NonZeroU8::new(255).unwrap()).then(|| unsafe { std::mem::transmute(v2) }); + // NonZero -> NonZeroNonMaxU8, do lint, target type has less validity + let _: Option = (v2 < NonZero::new(255u8).unwrap()).then(|| unsafe { std::mem::transmute(v2) }); } fn main() {} diff --git a/tests/ui/eager_transmute.rs b/tests/ui/eager_transmute.rs index 491a9485c9327..9750e87ce5741 100644 --- a/tests/ui/eager_transmute.rs +++ b/tests/ui/eager_transmute.rs @@ -2,7 +2,7 @@ #![warn(clippy::eager_transmute)] #![allow(clippy::transmute_int_to_non_zero, clippy::missing_transmute_annotations)] -use std::num::NonZeroU8; +use std::num::NonZero; #[repr(u8)] enum Opcode { @@ -85,21 +85,21 @@ macro_rules! impls { } impls!(NonMaxU8, NonZeroNonMaxU8); -fn niche_tests(v1: u8, v2: NonZeroU8, v3: NonZeroNonMaxU8) { - // u8 -> NonZeroU8, do lint - let _: Option = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); +fn niche_tests(v1: u8, v2: NonZero, v3: NonZeroNonMaxU8) { + // u8 -> NonZero, do lint + let _: Option> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); - // NonZeroU8 -> u8, don't lint, target type has no niche and therefore a higher validity range - let _: Option = (v2 > NonZeroU8::new(1).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); + // NonZero -> u8, don't lint, target type has no niche and therefore a higher validity range + let _: Option = (v2 > NonZero::new(1u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); - // NonZeroU8 -> NonMaxU8, do lint, different niche - let _: Option = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); + // NonZero -> NonMaxU8, do lint, different niche + let _: Option = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); // NonZeroNonMaxU8 -> NonMaxU8, don't lint, target type has more validity let _: Option = (v3 < 255).then_some(unsafe { std::mem::transmute(v2) }); - // NonZeroU8 -> NonZeroNonMaxU8, do lint, target type has less validity - let _: Option = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); + // NonZero -> NonZeroNonMaxU8, do lint, target type has less validity + let _: Option = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); } fn main() {} diff --git a/tests/ui/eager_transmute.stderr b/tests/ui/eager_transmute.stderr index b9a4321d99efe..5cf7bd49a929e 100644 --- a/tests/ui/eager_transmute.stderr +++ b/tests/ui/eager_transmute.stderr @@ -155,36 +155,36 @@ LL | (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:90:60 + --> tests/ui/eager_transmute.rs:90:62 | -LL | let _: Option = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Option> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ | help: consider using `bool::then` to only transmute if the condition holds | -LL | let _: Option = (v1 > 0).then(|| unsafe { std::mem::transmute(v1) }); - | ~~~~ ++ +LL | let _: Option> = (v1 > 0).then(|| unsafe { std::mem::transmute(v1) }); + | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false --> tests/ui/eager_transmute.rs:96:86 | -LL | let _: Option = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); +LL | let _: Option = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ | help: consider using `bool::then` to only transmute if the condition holds | -LL | let _: Option = (v2 < NonZeroU8::new(255).unwrap()).then(|| unsafe { std::mem::transmute(v2) }); +LL | let _: Option = (v2 < NonZero::new(255u8).unwrap()).then(|| unsafe { std::mem::transmute(v2) }); | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false --> tests/ui/eager_transmute.rs:102:93 | -LL | let _: Option = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); +LL | let _: Option = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ | help: consider using `bool::then` to only transmute if the condition holds | -LL | let _: Option = (v2 < NonZeroU8::new(255).unwrap()).then(|| unsafe { std::mem::transmute(v2) }); +LL | let _: Option = (v2 < NonZero::new(255u8).unwrap()).then(|| unsafe { std::mem::transmute(v2) }); | ~~~~ ++ error: aborting due to 17 previous errors From 99a42bab30e27e30679a2bd8540475338788b034 Mon Sep 17 00:00:00 2001 From: shanretoo Date: Thu, 9 May 2024 09:29:18 +0800 Subject: [PATCH 35/58] fix wrong suggestions of assigning_clones when the assignment is a macro call --- clippy_lints/src/assigning_clones.rs | 26 ++++++++++------ tests/ui/assigning_clones.fixed | 20 ++++++++++++ tests/ui/assigning_clones.rs | 20 ++++++++++++ tests/ui/assigning_clones.stderr | 46 +++++++++++++++++++++------- 4 files changed, 92 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 64cf7755c878d..9202c46e8b6ba 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::{self, Instance, Mutability}; use rustc_session::impl_lint_pass; use rustc_span::def_id::DefId; use rustc_span::symbol::sym; -use rustc_span::ExpnKind; +use rustc_span::{ExpnKind, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -68,7 +68,8 @@ impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) { // Do not fire the lint in macros - let expn_data = assign_expr.span().ctxt().outer_expn_data(); + let ctxt = assign_expr.span().ctxt(); + let expn_data = ctxt.outer_expn_data(); match expn_data.kind { ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return, ExpnKind::Root => {}, @@ -83,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { }; if is_ok_to_suggest(cx, lhs, &call, &self.msrv) { - suggest(cx, assign_expr, lhs, &call); + suggest(cx, ctxt, assign_expr, lhs, &call); } } @@ -221,14 +222,20 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC implemented_fns.contains_key(&provided_fn.def_id) } -fn suggest<'tcx>(cx: &LateContext<'tcx>, assign_expr: &Expr<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) { +fn suggest<'tcx>( + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + assign_expr: &Expr<'tcx>, + lhs: &Expr<'tcx>, + call: &CallCandidate<'tcx>, +) { span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| { let mut applicability = Applicability::Unspecified; diag.span_suggestion( assign_expr.span, call.suggestion_msg(), - call.suggested_replacement(cx, lhs, &mut applicability), + call.suggested_replacement(cx, ctxt, lhs, &mut applicability), applicability, ); }); @@ -274,6 +281,7 @@ impl<'tcx> CallCandidate<'tcx> { fn suggested_replacement( &self, cx: &LateContext<'tcx>, + ctxt: SyntaxContext, lhs: &Expr<'tcx>, applicability: &mut Applicability, ) -> String { @@ -293,7 +301,7 @@ impl<'tcx> CallCandidate<'tcx> { // Determine whether we need to reference the argument to clone_from(). let clone_receiver_type = cx.typeck_results().expr_ty(receiver); let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver); - let mut arg_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + let mut arg_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability); if clone_receiver_type != clone_receiver_adj_type { // The receiver may have been a value type, so we need to add an `&` to // be sure the argument to clone_from will be a reference. @@ -311,7 +319,7 @@ impl<'tcx> CallCandidate<'tcx> { Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr() }; // The RHS had to be exactly correct before the call, there is no auto-deref for function calls. - let rhs_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + let rhs_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability); format!("Clone::clone_from({self_sugg}, {rhs_sugg})") }, @@ -340,11 +348,11 @@ impl<'tcx> CallCandidate<'tcx> { match self.kind { CallKind::MethodCall { receiver } => { - let receiver_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + let receiver_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability); format!("{receiver_sugg}.clone_into({rhs_sugg})") }, CallKind::FunctionCall { self_arg, .. } => { - let self_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + let self_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability); format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})") }, } diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed index 394d2a67ca3aa..70ab43b49b3a7 100644 --- a/tests/ui/assigning_clones.fixed +++ b/tests/ui/assigning_clones.fixed @@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr mut_thing.clone_from(ref_thing + ref_thing); } +fn clone_method_macro() { + let mut s = String::from(""); + s.clone_from(&format!("{} {}", "hello", "world")); +} + +fn clone_function_macro() { + let mut s = String::from(""); + Clone::clone_from(&mut s, &format!("{} {}", "hello", "world")); +} + fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { let mut a = HasCloneFrom; for _ in 1..10 { @@ -214,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) { ToOwned::clone_into(ref_str, &mut mut_thing); } +fn owned_method_macro() { + let mut s = String::from(""); + format!("{} {}", "hello", "world").clone_into(&mut s); +} + +fn owned_function_macro() { + let mut s = String::from(""); + ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s); +} + struct FakeToOwned; impl FakeToOwned { /// This looks just like `ToOwned::to_owned` diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs index df6b760d5fd98..9699fed100c8f 100644 --- a/tests/ui/assigning_clones.rs +++ b/tests/ui/assigning_clones.rs @@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr *mut_thing = (ref_thing + ref_thing).clone(); } +fn clone_method_macro() { + let mut s = String::from(""); + s = format!("{} {}", "hello", "world").clone(); +} + +fn clone_function_macro() { + let mut s = String::from(""); + s = Clone::clone(&format!("{} {}", "hello", "world")); +} + fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { let mut a = HasCloneFrom; for _ in 1..10 { @@ -214,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) { mut_thing = ToOwned::to_owned(ref_str); } +fn owned_method_macro() { + let mut s = String::from(""); + s = format!("{} {}", "hello", "world").to_owned(); +} + +fn owned_function_macro() { + let mut s = String::from(""); + s = ToOwned::to_owned(&format!("{} {}", "hello", "world")); +} + struct FakeToOwned; impl FakeToOwned { /// This looks just like `ToOwned::to_owned` diff --git a/tests/ui/assigning_clones.stderr b/tests/ui/assigning_clones.stderr index 87f63ca604fe5..a68516376abb5 100644 --- a/tests/ui/assigning_clones.stderr +++ b/tests/ui/assigning_clones.stderr @@ -62,64 +62,88 @@ LL | *mut_thing = (ref_thing + ref_thing).clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:68:9 + --> tests/ui/assigning_clones.rs:67:5 + | +LL | s = format!("{} {}", "hello", "world").clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `s.clone_from(&format!("{} {}", "hello", "world"))` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:72:5 + | +LL | s = Clone::clone(&format!("{} {}", "hello", "world")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"))` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:78:9 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:139:5 + --> tests/ui/assigning_clones.rs:149:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:146:5 + --> tests/ui/assigning_clones.rs:156:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:147:5 + --> tests/ui/assigning_clones.rs:157:5 | LL | a = c.to_owned(); | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:177:5 + --> tests/ui/assigning_clones.rs:187:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:181:5 + --> tests/ui/assigning_clones.rs:191:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:202:5 + --> tests/ui/assigning_clones.rs:212:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:206:5 + --> tests/ui/assigning_clones.rs:216:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:210:5 + --> tests/ui/assigning_clones.rs:220:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:214:5 + --> tests/ui/assigning_clones.rs:224:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` -error: aborting due to 20 previous errors +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:229:5 + | +LL | s = format!("{} {}", "hello", "world").to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `format!("{} {}", "hello", "world").clone_into(&mut s)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:234:5 + | +LL | s = ToOwned::to_owned(&format!("{} {}", "hello", "world")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s)` + +error: aborting due to 24 previous errors From 9b4ad016ec058f3de95a2492e2f3ef1c54f37714 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 9 May 2024 19:47:08 +0000 Subject: [PATCH 36/58] always use `GenericArgsRef` --- clippy_lints/src/default_union_representation.rs | 4 ++-- clippy_lints/src/needless_borrows_for_generic_args.rs | 4 ++-- clippy_utils/src/ty.rs | 8 ++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 3f87ed8df2bf3..b4290b6437f27 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, FieldDef, GenericArg, List}; +use rustc_middle::ty::{self, FieldDef}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -85,7 +85,7 @@ fn is_union_with_two_non_zst_fields<'tcx>(cx: &LateContext<'tcx>, item: &Item<'t } } -fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: &'tcx List>) -> bool { +fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: ty::GenericArgsRef<'tcx>) -> bool { let ty = field.ty(cx.tcx, args); if let Ok(layout) = cx.layout_of(ty) { layout.is_zst() diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index a24cd4f9c8a35..e6c6a15b8d48a 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::{ - self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamTy, ProjectionPredicate, Ty, + self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty, }; use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; @@ -161,7 +161,7 @@ fn needless_borrow_count<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, fn_id: DefId, - callee_args: &'tcx List>, + callee_args: ty::GenericArgsRef<'tcx>, arg_index: usize, param_ty: ParamTy, mut expr: &Expr<'tcx>, diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 23750ed4d1ba0..626d6a35307ce 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -19,7 +19,7 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, List, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt, + GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; @@ -961,7 +961,11 @@ pub struct AdtVariantInfo { impl AdtVariantInfo { /// Returns ADT variants ordered by size - pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List>) -> Vec { + pub fn new<'tcx>( + cx: &LateContext<'tcx>, + adt: AdtDef<'tcx>, + subst: GenericArgsRef<'tcx> + ) -> Vec { let mut variants_size = adt .variants() .iter() From dbeae8da7882ea24ed2002deb3bfa06d50cad985 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 10 May 2024 09:06:47 +1000 Subject: [PATCH 37/58] Use fewer origins when creating type variables. `InferCtxt::next_{ty,const}_var*` all take an origin, but the `param_def_id` is almost always `None`. This commit changes them to just take a `Span` and build the origin within the method, and adds new methods for the rare cases where `param_def_id` might not be `None`. This avoids a lot of tedious origin building. Specifically: - next_ty_var{,_id_in_universe,_in_universe}: now take `Span` instead of `TypeVariableOrigin` - next_ty_var_with_origin: added - next_const_var{,_in_universe}: takes Span instead of ConstVariableOrigin - next_const_var_with_origin: added - next_region_var, next_region_var_in_universe: these are unchanged, still take RegionVariableOrigin The API inconsistency (ty/const vs region) seems worth it for the large conciseness improvements. --- clippy_utils/src/ty.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 23750ed4d1ba0..69dd3ba2970e6 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -10,7 +10,6 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; -use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::Scalar; @@ -276,11 +275,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( .into_iter() .map(|arg| { arg.into().unwrap_or_else(|| { - let orig = TypeVariableOrigin { - span: DUMMY_SP, - param_def_id: None, - }; - infcx.next_ty_var(orig).into() + infcx.next_ty_var(DUMMY_SP).into() }) }) .collect::>(); From 9523b3fbf0a6318772b582fc4f5fa69d4b7f6e94 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 9 May 2024 20:56:44 -0400 Subject: [PATCH 38/58] Rename Generics::params to Generics::own_params --- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/derive.rs | 2 +- clippy_lints/src/implied_bounds_in_impls.rs | 2 +- clippy_lints/src/iter_without_into_iter.rs | 2 +- clippy_lints/src/mismatching_type_param_order.rs | 2 +- clippy_utils/src/ty.rs | 6 +++--- clippy_utils/src/ty/type_certainty/mod.rs | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index b936b28470b53..c6aef9ac2d606 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -382,7 +382,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { cx, impl_ty, trait_id, - &args[..cx.tcx.generics_of(trait_id).params.len() - 1], + &args[..cx.tcx.generics_of(trait_id).own_params.len() - 1], ) { false diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 42cd19fb8eca5..9662c8f4fe2fc 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -480,7 +480,7 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> // Vec<(param_def, needs_eq)> let mut params = tcx .generics_of(did) - .params + .own_params .iter() .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) .collect::>(); diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index 3bf8d61895585..dc935ed3d7fe7 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -148,7 +148,7 @@ fn try_resolve_type<'tcx>( match args.get(index - 1) { Some(GenericArg::Type(ty)) => Some(lower_ty(tcx, ty)), Some(_) => None, - None => Some(tcx.type_of(generics.params[index].def_id).skip_binder()), + None => Some(tcx.type_of(generics.own_params[index].def_id).skip_binder()), } } diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs index c749a71233033..a75dfaf286fdf 100644 --- a/clippy_lints/src/iter_without_into_iter.rs +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -225,7 +225,7 @@ impl {self_ty_without_ref} {{ && let ImplItemKind::Fn(sig, _) = item.kind && let FnRetTy::Return(ret) = sig.decl.output && is_nameable_in_impl_trait(ret) - && cx.tcx.generics_of(item_did).params.is_empty() + && cx.tcx.generics_of(item_did).own_params.is_empty() && sig.decl.implicit_self == expected_implicit_self && sig.decl.inputs.len() == 1 && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()) diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 0842a87282475..934b9f490addf 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { }; // get the names of the generic parameters in the type - let type_params = &cx.tcx.generics_of(defid).params; + let type_params = &cx.tcx.generics_of(defid).own_params; let type_param_names: Vec<_> = type_params .iter() .filter_map(|p| match p.kind { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 23750ed4d1ba0..ad17c7cefff05 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -1070,11 +1070,11 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[GenericArg<'tcx>]) { let g = tcx.generics_of(did); let parent = g.parent.map(|did| tcx.generics_of(did)); - let count = g.parent_count + g.params.len(); + let count = g.parent_count + g.own_params.len(); let params = parent - .map_or([].as_slice(), |p| p.params.as_slice()) + .map_or([].as_slice(), |p| p.own_params.as_slice()) .iter() - .chain(&g.params) + .chain(&g.own_params) .map(|x| &x.kind); assert!( diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 2241494b484a8..c2ff19931d5c3 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -176,7 +176,7 @@ fn qpath_certainty(cx: &LateContext<'_>, qpath: &QPath<'_>, resolves_to_type: bo .get(*lang_item) .map_or(Certainty::Uncertain, |def_id| { let generics = cx.tcx.generics_of(def_id); - if generics.parent_count == 0 && generics.params.is_empty() { + if generics.parent_count == 0 && generics.own_params.is_empty() { Certainty::Certain(if resolves_to_type { Some(def_id) } else { None }) } else { Certainty::Uncertain @@ -206,7 +206,7 @@ fn path_segment_certainty( // Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE. if cx.tcx.res_generics_def_id(path_segment.res).is_some() { let generics = cx.tcx.generics_of(def_id); - let count = generics.params.len() - usize::from(generics.host_effect_index.is_some()); + let count = generics.own_params.len() - usize::from(generics.host_effect_index.is_some()); let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 { Certainty::Certain(None) } else { @@ -299,7 +299,7 @@ fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bo let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder(); // Check that all type parameters appear in the functions input types. - (0..(generics.parent_count + generics.params.len()) as u32).all(|index| { + (0..(generics.parent_count + generics.own_params.len()) as u32).all(|index| { Some(index as usize) == generics.host_effect_index || fn_sig .inputs() From 51145a20bfec060b3a5015d922125e886af91d39 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 9 May 2024 22:45:14 -0400 Subject: [PATCH 39/58] Make builtin_deref just return a Ty --- clippy_lints/src/matches/significant_drop_in_scrutinee.rs | 8 ++++---- clippy_lints/src/operators/cmp_owned.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 10c3203725a81..f775ea072e1f0 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -6,7 +6,7 @@ use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LintContext}; -use rustc_middle::ty::{GenericArgKind, Ty, TypeAndMut}; +use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_span::Span; use super::SIGNIFICANT_DROP_IN_SCRUTINEE; @@ -234,9 +234,9 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { } let ty = self.sig_drop_checker.get_type(expr); if ty.is_ref() { - // We checked that the type was ref, so builtin_deref will return Some TypeAndMut, - // but let's avoid any chance of an ICE - if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) { + // We checked that the type was ref, so builtin_deref will return Some, + // but let's avoid any chance of an ICE. + if let Some(ty) = ty.builtin_deref(true) { if ty.is_trivially_pure_clone_copy() { self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy); } else if allow_move_and_clone { diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index ea8ed28ba62ba..208b20a7a0691 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -70,7 +70,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default(); let with_deref = arg_ty .builtin_deref(true) - .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty)) + .and_then(|ty| symmetric_partial_eq(cx, ty, other_ty)) .unwrap_or_default(); if !with_deref.is_implemented() && !without_deref.is_implemented() { diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 325c9bee05782..d8d26e21369a4 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -134,7 +134,7 @@ fn check_rvalue<'tcx>( ) => Err((span, "function pointer casts are not allowed in const fn".into())), Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => { let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) { - deref_ty.ty + deref_ty } else { // We cannot allow this for now. return Err((span, "unsizing casts are only allowed for references right now".into())); From 133549c61a946fd0eb80cd380e5aaee8a0aa69bb Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 10 May 2024 08:41:11 -0700 Subject: [PATCH 40/58] doc_lazy_continuation: change applicability to MaybeIncorrect --- clippy_lints/src/doc/lazy_continuation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs index 497e9e5d4d1b9..38bc58a550196 100644 --- a/clippy_lints/src/doc/lazy_continuation.rs +++ b/clippy_lints/src/doc/lazy_continuation.rs @@ -58,7 +58,7 @@ pub(super) fn check( span.shrink_to_hi(), "indent this line", std::iter::repeat(" ").take(indent).join(""), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways, ); diag.help("if this is supposed to be its own paragraph, add a blank line"); From 2baeb9be544d42b03044d5598ea66725d53fddf0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 10 May 2024 14:59:56 -0400 Subject: [PATCH 41/58] Lift `TraitRef` into `rustc_type_ir` --- clippy_lints/src/future_not_send.rs | 1 + clippy_utils/src/qualify_min_const_fn.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 18f4e51ebd663..2c2daac023492 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -5,6 +5,7 @@ use rustc_hir::{Body, FnDecl}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind}; +use rustc_middle::ty::print::PrintTraitRefExt; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{sym, Span}; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index d8d26e21369a4..95851a2eed814 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -402,7 +402,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> tcx, ObligationCause::dummy_with_span(body.span), ConstCx::new(tcx, body).param_env, - TraitRef::from_lang_item(tcx, LangItem::Destruct, body.span, [ty]), + TraitRef::new(tcx, tcx.require_lang_item(LangItem::Destruct, Some(body.span)), [ty]), ); let infcx = tcx.infer_ctxt().build(); From 904c99c163a53c4b9a49f172f9ceaa2ac8445473 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Sat, 11 May 2024 15:37:31 +0800 Subject: [PATCH 42/58] make [`from_str_radix_10`] skip constant context --- clippy_lints/src/from_str_radix_10.rs | 5 ++++- tests/ui/from_str_radix_10.fixed | 11 +++++++++++ tests/ui/from_str_radix_10.rs | 11 +++++++++++ tests/ui/from_str_radix_10.stderr | 16 ++++++++-------- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 633ed96d6a6d5..82ce501bac59b 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::{in_constant, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,6 +47,9 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind + // do not lint in constant context, because the suggestion won't work. + // NB: keep this check until a new `const_trait_impl` is available and stablized. + && !in_constant(cx, exp.hir_id) // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind diff --git a/tests/ui/from_str_radix_10.fixed b/tests/ui/from_str_radix_10.fixed index 8c253bfd99a57..f9ce1defda17c 100644 --- a/tests/ui/from_str_radix_10.fixed +++ b/tests/ui/from_str_radix_10.fixed @@ -1,3 +1,4 @@ +#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -59,3 +60,13 @@ fn main() -> Result<(), Box> { Ok(()) } + +fn issue_12732() { + const A: Result = u32::from_str_radix("123", 10); + const B: () = { + let _ = u32::from_str_radix("123", 10); + }; + const fn foo() { + let _ = u32::from_str_radix("123", 10); + } +} diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index e9d02215710cb..2d5b351f8da3e 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -1,3 +1,4 @@ +#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -59,3 +60,13 @@ fn main() -> Result<(), Box> { Ok(()) } + +fn issue_12732() { + const A: Result = u32::from_str_radix("123", 10); + const B: () = { + let _ = u32::from_str_radix("123", 10); + }; + const fn foo() { + let _ = u32::from_str_radix("123", 10); + } +} diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index 4aa84eca26120..01a1bf8940a12 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -1,5 +1,5 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:28:5 + --> tests/ui/from_str_radix_10.rs:29:5 | LL | u32::from_str_radix("30", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::()` @@ -8,43 +8,43 @@ LL | u32::from_str_radix("30", 10)?; = help: to override `-D warnings` add `#[allow(clippy::from_str_radix_10)]` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:31:5 + --> tests/ui/from_str_radix_10.rs:32:5 | LL | i64::from_str_radix("24", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:33:5 + --> tests/ui/from_str_radix_10.rs:34:5 | LL | isize::from_str_radix("100", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:35:5 + --> tests/ui/from_str_radix_10.rs:36:5 | LL | u8::from_str_radix("7", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:37:5 + --> tests/ui/from_str_radix_10.rs:38:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:39:5 + --> tests/ui/from_str_radix_10.rs:40:5 | LL | i128::from_str_radix(Test + Test, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:43:5 + --> tests/ui/from_str_radix_10.rs:44:5 | LL | i32::from_str_radix(string, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:47:5 + --> tests/ui/from_str_radix_10.rs:48:5 | LL | i32::from_str_radix(&stringier, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::()` From cb3fcbbcfe3d92ae822f5fba402dbb3d48f30470 Mon Sep 17 00:00:00 2001 From: Yusuf Raji Date: Sat, 11 May 2024 17:06:12 +0200 Subject: [PATCH 43/58] Improve clippy_lints/src/loops/mod.rs doc Co-authored-by: llogiq --- clippy_lints/src/loops/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index eb363a7039293..3dcb050d77e68 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -427,7 +427,7 @@ declare_clippy_lint! { /// /// ### Known problems /// The lint will catch all while loops comparing floating point - /// values no matter whether it's a threshold or something. + /// values without regarding the increment. /// /// ### Example /// ```no_run From 46659acdbde160324bae653e704203221df3b980 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Sun, 12 May 2024 22:13:17 +0800 Subject: [PATCH 44/58] add configuration to allow skipping on some certain traits & collect metadata --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 22 ++++++++ clippy_config/src/conf.rs | 23 +++++++++ clippy_lints/src/functions/mod.rs | 35 +++++++++---- .../src/functions/renamed_function_params.rs | 50 +++++++++++-------- clippy_lints/src/lib.rs | 2 + .../default/clippy.toml | 2 + .../extend/clippy.toml | 2 + .../renamed_function_params.default.stderr} | 26 ++++------ .../renamed_function_params.extend.stderr | 34 +++++++++++++ .../renamed_function_params.rs | 11 ++-- .../toml_unknown_key/conf_unknown_key.stderr | 3 ++ 12 files changed, 159 insertions(+), 52 deletions(-) create mode 100644 tests/ui-toml/renamed_function_params/default/clippy.toml create mode 100644 tests/ui-toml/renamed_function_params/extend/clippy.toml rename tests/{ui/renamed_function_params.stderr => ui-toml/renamed_function_params/renamed_function_params.default.stderr} (69%) create mode 100644 tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr rename tests/{ui => ui-toml/renamed_function_params}/renamed_function_params.rs (85%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce2dda0cc2ac8..2abe48afc274a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5942,6 +5942,7 @@ Released 2018-09-13 [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings [`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests [`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception +[`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests [`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index f6af9810ca166..bd8c6d14cd554 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -122,6 +122,28 @@ Whether to allow module inception if it's not public. * [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception) +## `allow-renamed-params-for` +List of trait paths to ignore when checking renamed function parameters. + +#### Example + +```toml +allow-renamed-params-for = [ "std::convert::From" ] +``` + +#### Noteworthy + +- By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` +- `".."` can be used as part of the list to indicate that the configured values should be appended to the +default configuration of Clippy. By default, any configuration will replace the default value. + +**Default Value:** `["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]` + +--- +**Affected lints:** +* [`renamed_function_params`](https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params) + + ## `allow-unwrap-in-tests` Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 5cfcbdb57d735..642abd4f3e7ac 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -40,6 +40,8 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"]; const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"]; const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"]; +const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] = + &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]; /// Conf with parse errors #[derive(Default)] @@ -613,6 +615,23 @@ define_Conf! { /// - Use `".."` as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value (allowed_prefixes: Vec = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()), + /// Lint: RENAMED_FUNCTION_PARAMS. + /// + /// List of trait paths to ignore when checking renamed function parameters. + /// + /// #### Example + /// + /// ```toml + /// allow-renamed-params-for = [ "std::convert::From" ] + /// ``` + /// + /// #### Noteworthy + /// + /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` + /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the + /// default configuration of Clippy. By default, any configuration will replace the default value. + (allow_renamed_params_for: Vec = + DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()), } /// Search for the configuration file. @@ -674,6 +693,10 @@ fn deserialize(file: &SourceFile) -> TryConf { extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES); + extend_vec_if_indicator_present( + &mut conf.conf.allow_renamed_params_for, + DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS, + ); // TODO: THIS SHOULD BE TESTED, this comment will be gone soon if conf.conf.allowed_idents_below_min_chars.contains("..") { conf.conf diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 9a09dbaed4adc..dfcaac9abefc7 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -7,11 +7,12 @@ mod result; mod too_many_arguments; mod too_many_lines; +use clippy_utils::def_path_def_ids; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; +use rustc_span::def_id::{DefIdSet, LocalDefId}; use rustc_span::Span; declare_clippy_lint! { @@ -373,9 +374,9 @@ declare_clippy_lint! { /// ```rust /// struct A(u32); /// - /// impl From for String { - /// fn from(a: A) -> Self { - /// a.0.to_string() + /// impl PartialEq for A { + /// fn eq(&self, b: &Self) -> bool { + /// self.0 == b.0 /// } /// } /// ``` @@ -383,9 +384,9 @@ declare_clippy_lint! { /// ```rust /// struct A(u32); /// - /// impl From for String { - /// fn from(value: A) -> Self { - /// value.0.to_string() + /// impl PartialEq for A { + /// fn eq(&self, other: &Self) -> bool { + /// self.0 == other.0 /// } /// } /// ``` @@ -395,13 +396,16 @@ declare_clippy_lint! { "renamed function parameters in trait implementation" } -#[derive(Copy, Clone)] -#[allow(clippy::struct_field_names)] +#[derive(Clone)] pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64, avoid_breaking_exported_api: bool, + allow_renamed_params_for: Vec, + /// A set of resolved `def_id` of traits that are configured to allow + /// function params renaming. + trait_ids: DefIdSet, } impl Functions { @@ -410,12 +414,15 @@ impl Functions { too_many_lines_threshold: u64, large_error_threshold: u64, avoid_breaking_exported_api: bool, + allow_renamed_params_for: Vec, ) -> Self { Self { too_many_arguments_threshold, too_many_lines_threshold, large_error_threshold, avoid_breaking_exported_api, + allow_renamed_params_for, + trait_ids: DefIdSet::default(), } } } @@ -461,7 +468,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { must_use::check_impl_item(cx, item); result::check_impl_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_impl_item(cx, item); - renamed_function_params::check_impl_item(cx, item); + renamed_function_params::check_impl_item(cx, item, &self.trait_ids); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { @@ -471,4 +478,12 @@ impl<'tcx> LateLintPass<'tcx> for Functions { result::check_trait_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); } + + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + for path in &self.allow_renamed_params_for { + let path_segments: Vec<&str> = path.split("::").collect(); + let ids = def_path_def_ids(cx, &path_segments); + self.trait_ids.extend(ids); + } + } } diff --git a/clippy_lints/src/functions/renamed_function_params.rs b/clippy_lints/src/functions/renamed_function_params.rs index cea76996f052a..c7de0385c021b 100644 --- a/clippy_lints/src/functions/renamed_function_params.rs +++ b/clippy_lints/src/functions/renamed_function_params.rs @@ -1,18 +1,26 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::hir_id::OwnerId; -use rustc_hir::{ImplItem, ImplItemKind, ItemKind, Node}; +use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef}; use rustc_lint::LateContext; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; use super::RENAMED_FUNCTION_PARAMS; -pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>) { +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored_traits: &DefIdSet) { if !item.span.from_expansion() && let ImplItemKind::Fn(_, body_id) = item.kind - && let Some(did) = trait_item_def_id_of_impl(cx, item.owner_id) + && let parent_node = cx.tcx.parent_hir_node(item.hir_id()) + && let Node::Item(parent_item) = parent_node + && let ItemKind::Impl(Impl { + items, + of_trait: Some(trait_ref), + .. + }) = &parent_item.kind + && let Some(did) = trait_item_def_id_of_impl(items, item.owner_id) + && !is_from_ignored_trait(trait_ref, ignored_traits) { let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id); let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied(); @@ -25,7 +33,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>) { cx, RENAMED_FUNCTION_PARAMS, multi_span, - &format!("renamed function parameter{plural} of trait impl"), + format!("renamed function parameter{plural} of trait impl"), |diag| { diag.multipart_suggestion( format!("consider using the default name{plural}"), @@ -83,20 +91,20 @@ fn is_unused_or_empty_symbol(symbol: Symbol) -> bool { symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_') } -/// Get the [`trait_item_def_id`](rustc_hir::hir::ImplItemRef::trait_item_def_id) of an impl item. -fn trait_item_def_id_of_impl(cx: &LateContext<'_>, impl_item_id: OwnerId) -> Option { - let trait_node = cx.tcx.parent_hir_node(impl_item_id.into()); - if let Node::Item(item) = trait_node - && let ItemKind::Impl(impl_) = &item.kind - { - impl_.items.iter().find_map(|item| { - if item.id.owner_id == impl_item_id { - item.trait_item_def_id - } else { - None - } - }) - } else { - None - } +/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item. +fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option { + items.iter().find_map(|item| { + if item.id.owner_id == target { + item.trait_item_def_id + } else { + None + } + }) +} + +fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool { + let Some(trait_did) = of_trait.trait_def_id() else { + return false; + }; + ignored_traits.contains(&trait_did) } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 34dd47855517d..c9bd40a711a29 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -595,6 +595,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { ref allowed_duplicate_crates, allow_comparison_to_zero, ref allowed_prefixes, + ref allow_renamed_params_for, blacklisted_names: _, cyclomatic_complexity_threshold: _, @@ -788,6 +789,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { too_many_lines_threshold, large_error_threshold, avoid_breaking_exported_api, + allow_renamed_params_for.clone(), )) }); store.register_late_pass(move |_| Box::new(doc::Documentation::new(doc_valid_idents, check_private_items))); diff --git a/tests/ui-toml/renamed_function_params/default/clippy.toml b/tests/ui-toml/renamed_function_params/default/clippy.toml new file mode 100644 index 0000000000000..5381e70a93915 --- /dev/null +++ b/tests/ui-toml/renamed_function_params/default/clippy.toml @@ -0,0 +1,2 @@ +# Ignore `From`, `TryFrom`, `FromStr` by default +# allow-renamed-params-for = [] diff --git a/tests/ui-toml/renamed_function_params/extend/clippy.toml b/tests/ui-toml/renamed_function_params/extend/clippy.toml new file mode 100644 index 0000000000000..9b3853e769615 --- /dev/null +++ b/tests/ui-toml/renamed_function_params/extend/clippy.toml @@ -0,0 +1,2 @@ +# Ignore `From`, `TryFrom`, `FromStr` by default +allow-renamed-params-for = [ "..", "std::ops::Add", "renamed_function_params::MyTrait" ] diff --git a/tests/ui/renamed_function_params.stderr b/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr similarity index 69% rename from tests/ui/renamed_function_params.stderr rename to tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr index 7193541edb626..2d700f6075926 100644 --- a/tests/ui/renamed_function_params.stderr +++ b/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr @@ -1,38 +1,32 @@ error: renamed function parameter of trait impl - --> tests/ui/renamed_function_params.rs:22:13 + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18 | -LL | fn from(b: B) -> Self { - | ^ help: consider using the default name: `value` +LL | fn eq(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` | = note: `-D clippy::renamed-function-params` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]` error: renamed function parameter of trait impl - --> tests/ui/renamed_function_params.rs:28:18 - | -LL | fn eq(&self, rhs: &Self) -> bool { - | ^^^ help: consider using the default name: `other` - -error: renamed function parameter of trait impl - --> tests/ui/renamed_function_params.rs:32:18 + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18 | LL | fn ne(&self, rhs: &Self) -> bool { | ^^^ help: consider using the default name: `other` error: renamed function parameter of trait impl - --> tests/ui/renamed_function_params.rs:46:19 + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:48:19 | -LL | fn foo(&self, i_dont_wanna_use_your_name: u8) {} +LL | fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the default name: `val` error: renamed function parameter of trait impl - --> tests/ui/renamed_function_params.rs:54:31 + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31 | LL | fn hash(&self, states: &mut H) { | ^^^^^^ help: consider using the default name: `state` error: renamed function parameters of trait impl - --> tests/ui/renamed_function_params.rs:58:30 + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30 | LL | fn hash_slice(date: &[Self], states: &mut H) { | ^^^^ ^^^^^^ @@ -43,10 +37,10 @@ LL | fn hash_slice(data: &[Self], state: &mut H) { | ~~~~ ~~~~~ error: renamed function parameter of trait impl - --> tests/ui/renamed_function_params.rs:79:18 + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:80:18 | LL | fn add(self, b: B) -> C { | ^ help: consider using the default name: `rhs` -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr b/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr new file mode 100644 index 0000000000000..e57554fa613ac --- /dev/null +++ b/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr @@ -0,0 +1,34 @@ +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18 + | +LL | fn eq(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + | + = note: `-D clippy::renamed-function-params` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18 + | +LL | fn ne(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31 + | +LL | fn hash(&self, states: &mut H) { + | ^^^^^^ help: consider using the default name: `state` + +error: renamed function parameters of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30 + | +LL | fn hash_slice(date: &[Self], states: &mut H) { + | ^^^^ ^^^^^^ + | +help: consider using the default names + | +LL | fn hash_slice(data: &[Self], state: &mut H) { + | ~~~~ ~~~~~ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/renamed_function_params.rs b/tests/ui-toml/renamed_function_params/renamed_function_params.rs similarity index 85% rename from tests/ui/renamed_function_params.rs rename to tests/ui-toml/renamed_function_params/renamed_function_params.rs index 4f06ae706dde0..f3eb910abbd67 100644 --- a/tests/ui/renamed_function_params.rs +++ b/tests/ui-toml/renamed_function_params/renamed_function_params.rs @@ -1,4 +1,7 @@ //@no-rustfix +//@revisions: default extend +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/default +//@[extend] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/extend #![warn(clippy::renamed_function_params)] #![allow(clippy::partialeq_ne_impl, clippy::to_string_trait_impl)] #![allow(unused)] @@ -18,9 +21,8 @@ impl ToString for A { } struct B(u32); -impl From for String { +impl std::convert::From for String { fn from(b: B) -> Self { - //~^ ERROR: renamed function parameter of trait impl b.0.to_string() } } @@ -43,8 +45,7 @@ trait MyTrait { } impl MyTrait for B { - fn foo(&self, i_dont_wanna_use_your_name: u8) {} - //~^ ERROR: renamed function parameter of trait impl + fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend` fn bar(_a: u8, _: u8) {} fn baz(self, val: u8) {} fn quz(&self, val: u8) {} @@ -77,7 +78,7 @@ enum C { impl std::ops::Add for C { type Output = C; fn add(self, b: B) -> C { - //~^ ERROR: renamed function parameter of trait impl + // only lint in `extend` C::B(b.0) } } diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 722e9b3bc8d46..e1ba60911fe9b 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -10,6 +10,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-one-hash-in-raw-strings allow-print-in-tests allow-private-module-inception + allow-renamed-params-for allow-unwrap-in-tests allow-useless-vec-in-tests allowed-dotfiles @@ -91,6 +92,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-one-hash-in-raw-strings allow-print-in-tests allow-private-module-inception + allow-renamed-params-for allow-unwrap-in-tests allow-useless-vec-in-tests allowed-dotfiles @@ -172,6 +174,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allow-one-hash-in-raw-strings allow-print-in-tests allow-private-module-inception + allow-renamed-params-for allow-unwrap-in-tests allow-useless-vec-in-tests allowed-dotfiles From 9747c80644fc9f705029c254ca1d14668a72f904 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 7 Jan 2024 00:42:31 +0100 Subject: [PATCH 45/58] new lint: `macro_metavars_in_unsafe` --- CHANGELOG.md | 2 + book/src/lint_configuration.md | 10 + clippy_config/src/conf.rs | 4 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 8 + clippy_lints/src/macro_metavars_in_unsafe.rs | 256 +++++++++++++++++ .../macro_metavars_in_unsafe/default/test.rs | 260 ++++++++++++++++++ .../default/test.stderr | 187 +++++++++++++ .../private/clippy.toml | 1 + .../macro_metavars_in_unsafe/private/test.rs | 15 + .../private/test.stderr | 17 ++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + 12 files changed, 764 insertions(+) create mode 100644 clippy_lints/src/macro_metavars_in_unsafe.rs create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/default/test.rs create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/private/test.rs create mode 100644 tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 75879a9995277..cfff0bb50a6ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5448,6 +5448,7 @@ Released 2018-09-13 [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal +[`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert @@ -6005,4 +6006,5 @@ Released 2018-09-13 [`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold [`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold [`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports +[`warn-unsafe-macro-metavars-in-private-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-unsafe-macro-metavars-in-private-macros diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index bd8c6d14cd554..b049a6106f560 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -922,3 +922,13 @@ Whether to allow certain wildcard imports (prelude, super in tests). * [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) +## `warn-unsafe-macro-metavars-in-private-macros` +Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. + +**Default Value:** `false` + +--- +**Affected lints:** +* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe) + + diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 642abd4f3e7ac..a4b29afa77798 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -632,6 +632,10 @@ define_Conf! { /// default configuration of Clippy. By default, any configuration will replace the default value. (allow_renamed_params_for: Vec = DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()), + /// Lint: MACRO_METAVARS_IN_UNSAFE. + /// + /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. + (warn_unsafe_macro_metavars_in_private_macros: bool = false), } /// Search for the configuration file. diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index ed94eaee23fe4..6850ec821e84f 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -296,6 +296,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::loops::WHILE_IMMUTABLE_CONDITION_INFO, crate::loops::WHILE_LET_LOOP_INFO, crate::loops::WHILE_LET_ON_ITERATOR_INFO, + crate::macro_metavars_in_unsafe::MACRO_METAVARS_IN_UNSAFE_INFO, crate::macro_use::MACRO_USE_IMPORTS_INFO, crate::main_recursion::MAIN_RECURSION_INFO, crate::manual_assert::MANUAL_ASSERT_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9bd40a711a29..c3ae36a559739 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -193,6 +193,7 @@ mod lifetimes; mod lines_filter_map_ok; mod literal_representation; mod loops; +mod macro_metavars_in_unsafe; mod macro_use; mod main_recursion; mod manual_assert; @@ -599,6 +600,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { blacklisted_names: _, cyclomatic_complexity_threshold: _, + warn_unsafe_macro_metavars_in_private_macros, } = *conf; let msrv = || msrv.clone(); @@ -1155,6 +1157,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); + store.register_late_pass(move |_| { + Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe { + warn_unsafe_macro_metavars_in_private_macros, + ..Default::default() + }) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs new file mode 100644 index 0000000000000..aea3d26e18788 --- /dev/null +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -0,0 +1,256 @@ +use std::collections::btree_map::Entry; +use std::collections::BTreeMap; + +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::is_lint_allowed; +use itertools::Itertools; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::{sym, Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Looks for macros that expand metavariables in an unsafe block. + /// + /// ### Why is this bad? + /// This hides an unsafe block and allows the user of the macro to write unsafe code without an explicit + /// unsafe block at callsite, making it possible to perform unsafe operations in seemingly safe code. + /// + /// The macro should be restructured so that these metavariables are referenced outside of unsafe blocks + /// and that the usual unsafety checks apply to the macro argument. + /// + /// This is usually done by binding it to a variable outside of the unsafe block + /// and then using that variable inside of the block as shown in the example, or by referencing it a second time + /// in a safe context, e.g. `if false { $expr }`. + /// + /// ### Known limitations + /// Due to how macros are represented in the compiler at the time Clippy runs its lints, + /// it's not possible to look for metavariables in macro definitions directly. + /// + /// Instead, this lint looks at expansions of macros. + /// This leads to false negatives for macros that are never actually invoked. + /// + /// By default, this lint is rather conservative and will only emit warnings on publicly-exported + /// macros from the same crate, because oftentimes private internal macros are one-off macros where + /// this lint would just be noise (e.g. macros that generate `impl` blocks). + /// The default behavior should help with preventing a high number of such false positives, + /// however it can be configured to also emit warnings in private macros if desired. + /// + /// ### Example + /// ```no_run + /// /// Gets the first element of a slice + /// macro_rules! first { + /// ($slice:expr) => { + /// unsafe { + /// let slice = $slice; // ⚠️ expansion inside of `unsafe {}` + /// + /// assert!(!slice.is_empty()); + /// // SAFETY: slice is checked to have at least one element + /// slice.first().unwrap_unchecked() + /// } + /// } + /// } + /// + /// assert_eq!(*first!(&[1i32]), 1); + /// + /// // This will compile as a consequence (note the lack of `unsafe {}`) + /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1); + /// ``` + /// Use instead: + /// ```compile_fail + /// macro_rules! first { + /// ($slice:expr) => {{ + /// let slice = $slice; // ✅ outside of `unsafe {}` + /// unsafe { + /// assert!(!slice.is_empty()); + /// // SAFETY: slice is checked to have at least one element + /// slice.first().unwrap_unchecked() + /// } + /// }} + /// } + /// + /// assert_eq!(*first!(&[1]), 1); + /// + /// // This won't compile: + /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1); + /// ``` + #[clippy::version = "1.80.0"] + pub MACRO_METAVARS_IN_UNSAFE, + suspicious, + "expanding macro metavariables in an unsafe block" +} +impl_lint_pass!(ExprMetavarsInUnsafe => [MACRO_METAVARS_IN_UNSAFE]); + +#[derive(Clone, Debug)] +pub enum MetavarState { + ReferencedInUnsafe { unsafe_blocks: Vec }, + ReferencedInSafe, +} + +#[derive(Default)] +pub struct ExprMetavarsInUnsafe { + pub warn_unsafe_macro_metavars_in_private_macros: bool, + /// A metavariable can be expanded more than once, potentially across multiple bodies, so it + /// requires some state kept across HIR nodes to make it possible to delay a warning + /// and later undo: + /// + /// ```ignore + /// macro_rules! x { + /// ($v:expr) => { + /// unsafe { $v; } // unsafe context, it might be possible to emit a warning here, so add it to the map + /// + /// $v; // `$v` expanded another time but in a safe context, set to ReferencedInSafe to suppress + /// } + /// } + /// ``` + pub metavar_expns: BTreeMap, +} + +struct BodyVisitor<'a, 'tcx> { + /// Stack of unsafe blocks -- the top item always represents the last seen unsafe block from + /// within a relevant macro. + macro_unsafe_blocks: Vec, + /// When this is >0, it means that the node currently being visited is "within" a + /// macro definition. This is not necessary for correctness, it merely helps reduce the number + /// of spans we need to insert into the map, since only spans from macros are relevant. + expn_depth: u32, + cx: &'a LateContext<'tcx>, + lint: &'a mut ExprMetavarsInUnsafe, +} + +fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export)) + && !cx.tcx.is_doc_hidden(def_id) +} + +impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { + fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { + let from_expn = s.span.from_expansion(); + if from_expn { + self.expn_depth += 1; + } + walk_stmt(self, s); + if from_expn { + self.expn_depth -= 1; + } + } + + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + let ctxt = e.span.ctxt(); + + if let ExprKind::Block(block, _) = e.kind + && let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules + && !ctxt.is_root() + && let Some(macro_def_id) = ctxt.outer_expn_data().macro_def_id + && let Some(macro_def_id) = macro_def_id.as_local() + && (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id)) + { + self.macro_unsafe_blocks.push(block.hir_id); + walk_block(self, block); + self.macro_unsafe_blocks.pop(); + } else if ctxt.is_root() && self.expn_depth > 0 { + let unsafe_block = self.macro_unsafe_blocks.last().copied(); + + match (self.lint.metavar_expns.entry(e.span), unsafe_block) { + (Entry::Vacant(e), None) => { + e.insert(MetavarState::ReferencedInSafe); + }, + (Entry::Vacant(e), Some(unsafe_block)) => { + e.insert(MetavarState::ReferencedInUnsafe { + unsafe_blocks: vec![unsafe_block], + }); + }, + (Entry::Occupied(mut e), None) => { + if let MetavarState::ReferencedInUnsafe { .. } = *e.get() { + e.insert(MetavarState::ReferencedInSafe); + } + }, + (Entry::Occupied(mut e), Some(unsafe_block)) => { + if let MetavarState::ReferencedInUnsafe { unsafe_blocks } = e.get_mut() + && !unsafe_blocks.contains(&unsafe_block) + { + unsafe_blocks.push(unsafe_block); + } + }, + } + + // NB: No need to visit descendant nodes. They're guaranteed to represent the same + // metavariable + } else { + walk_expr(self, e); + } + } +} + +impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx rustc_hir::Body<'tcx>) { + if is_lint_allowed(cx, MACRO_METAVARS_IN_UNSAFE, body.value.hir_id) { + return; + } + + // This BodyVisitor is separate and not part of the lint pass because there is no + // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span + + let mut vis = BodyVisitor { + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if body.value.span.from_expansion() { 1 } else { 0 }, + macro_unsafe_blocks: Vec::new(), + lint: self, + cx + }; + vis.visit_body(body); + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + // Aggregate all unsafe blocks from all spans: + // ``` + // macro_rules! x { + // ($w:expr, $x:expr, $y:expr) => { $w; unsafe { $w; $x; }; unsafe { $x; $y; }; } + // } + // $w: [] (unsafe#0 is never added because it was referenced in a safe context) + // $x: [unsafe#0, unsafe#1] + // $y: [unsafe#1] + // ``` + // We want to lint unsafe blocks #0 and #1 + let bad_unsafe_blocks = self + .metavar_expns + .iter() + .filter_map(|(_, state)| match state { + MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()), + MetavarState::ReferencedInSafe => None, + }) + .flatten() + .copied() + .map(|id| { + // Remove the syntax context to hide "in this macro invocation" in the diagnostic. + // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything + // related to the callsite. + let span = cx.tcx.hir().span(id); + + (id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None)) + }) + .dedup_by(|&(_, a), &(_, b)| a == b); + + for (id, span) in bad_unsafe_blocks { + span_lint_hir_and_then( + cx, + MACRO_METAVARS_IN_UNSAFE, + id, + span, + "this macro expands metavariables in an unsafe block", + |diag| { + diag.note("this allows the user of the macro to write unsafe code outside of an unsafe block"); + diag.help( + "consider expanding any metavariables outside of this block, e.g. by storing them in a variable", + ); + diag.help( + "... or also expand referenced metavariables in a safe context to require an unsafe block at callsite", + ); + }, + ); + } + } +} diff --git a/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs new file mode 100644 index 0000000000000..f5e01b431ad98 --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs @@ -0,0 +1,260 @@ +//! Tests macro_metavars_in_unsafe with default configuration +#![feature(decl_macro, lint_reasons)] +#![warn(clippy::macro_metavars_in_unsafe)] +#![allow(clippy::no_effect)] + +#[macro_export] +macro_rules! allow_works { + ($v:expr) => { + #[expect(clippy::macro_metavars_in_unsafe)] + unsafe { + $v; + }; + }; +} + +#[macro_export] +macro_rules! simple { + ($v:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + dbg!($v); + } + }; +} + +#[macro_export] +#[rustfmt::skip] // for some reason rustfmt rewrites $r#unsafe to r#u$nsafe, bug? +macro_rules! raw_symbol { + ($r#mod:expr, $r#unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $r#mod; + } + $r#unsafe; + }; +} + +#[macro_export] +macro_rules! multilevel_unsafe { + ($v:expr) => { + unsafe { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + } + }; +} + +#[macro_export] +macro_rules! in_function { + ($v:expr) => { + unsafe { + fn f() { + // function introduces a new body, so don't lint. + $v; + } + } + }; +} + +#[macro_export] +macro_rules! in_function_with_unsafe { + ($v:expr) => { + unsafe { + fn f() { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + } + } + }; +} + +#[macro_export] +macro_rules! const_static { + ($c:expr, $s:expr) => { + unsafe { + // const and static introduces new body, don't lint + const _X: i32 = $c; + static _Y: i32 = $s; + } + }; +} + +#[macro_export] +macro_rules! const_generic_in_struct { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + struct Ty< + const L: i32 = 1, + const M: i32 = { + 1; + unsafe { $inside_unsafe } + //~^ ERROR: this macro expands metavariables in an unsafe block + }, + const N: i32 = { $outside_unsafe }, + >; + } + }; +} + +#[macro_export] +macro_rules! fn_with_const_generic { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + fn f() { + $outside_unsafe; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + } + } + } + }; +} + +#[macro_export] +macro_rules! variables { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + let inside_unsafe = 1; + inside_unsafe; + } + $outside_unsafe; + let outside_unsafe = 1; + outside_unsafe; + }; +} + +#[macro_export] +macro_rules! multiple_matchers { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + } + $outside_unsafe; + }; + ($($v:expr, $x:expr),+) => { + $( + $v; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + } + );+ + }; +} + +#[macro_export] +macro_rules! multiple_unsafe_blocks { + ($w:expr, $x:expr, $y:expr) => { + $w; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + } + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + $y; + } + }; +} + +pub macro macro2_0($v:expr) { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } +} + +// don't lint private macros with the default configuration +macro_rules! private_mac { + ($v:expr) => { + unsafe { + $v; + } + }; +} + +// don't lint exported macros that are doc(hidden) because they also aren't part of the public API +#[macro_export] +#[doc(hidden)] +macro_rules! exported_but_hidden { + ($v:expr) => { + unsafe { + $v; + } + }; +} + +// don't lint if the same metavariable is expanded in an unsafe block and then outside of one: +// unsafe {} is still needed at callsite so not problematic +#[macro_export] +macro_rules! does_require_unsafe { + ($v:expr) => { + unsafe { + $v; + } + $v; + }; +} + +#[macro_export] +macro_rules! unsafe_from_root_ctxt { + ($v:expr) => { + // Expands to unsafe { 1 }, but the unsafe block is from the root ctxt and not this macro, + // so no warning. + $v; + }; +} + +// invoked from another macro, should still generate a warning +#[macro_export] +macro_rules! nested_macro_helper { + ($v:expr) => {{ + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + }}; +} + +#[macro_export] +macro_rules! nested_macros { + ($v:expr, $v2:expr) => {{ + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + nested_macro_helper!($v); + $v; + } + }}; +} + +fn main() { + allow_works!(1); + simple!(1); + raw_symbol!(1, 1); + multilevel_unsafe!(1); + in_function!(1); + in_function_with_unsafe!(1); + const_static!(1, 1); + const_generic_in_struct!(1, 1); + fn_with_const_generic!(1, 1); + variables!(1, 1); + multiple_matchers!(1, 1); + multiple_matchers!(1, 1, 1, 1); + macro2_0!(1); + private_mac!(1); + exported_but_hidden!(1); + does_require_unsafe!(1); + multiple_unsafe_blocks!(1, 1, 1); + unsafe_from_root_ctxt!(unsafe { 1 }); + nested_macros!(1, 1); +} diff --git a/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr b/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr new file mode 100644 index 0000000000000..d6b97f6fde1e1 --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr @@ -0,0 +1,187 @@ +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:19:9 + | +LL | / unsafe { +LL | | +LL | | dbg!($v); +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:30:9 + | +LL | / unsafe { +LL | | +LL | | $r#mod; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:42:13 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_____________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:67:17 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_________________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:95:21 + | +LL | unsafe { $inside_unsafe } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:110:17 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | } + | |_________________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:122:9 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | let inside_unsafe = 1; +LL | | inside_unsafe; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:137:9 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:146:13 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | } + | |_____________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:171:5 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_____^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:158:9 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:162:9 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | $y; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:222:9 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:232:9 + | +LL | / unsafe { +LL | | +LL | | nested_macro_helper!($v); +LL | | $v; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: aborting due to 14 previous errors + diff --git a/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml b/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml new file mode 100644 index 0000000000000..d4bbc2a1be894 --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml @@ -0,0 +1 @@ +warn-unsafe-macro-metavars-in-private-macros = true diff --git a/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs b/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs new file mode 100644 index 0000000000000..2bbe1fa7b7f94 --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs @@ -0,0 +1,15 @@ +//! Tests macro_metavars_in_unsafe with private (non-exported) macros +#![warn(clippy::macro_metavars_in_unsafe)] + +macro_rules! mac { + ($v:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + dbg!($v); + } + }; +} + +fn main() { + mac!(1); +} diff --git a/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr b/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr new file mode 100644 index 0000000000000..f9c418b221887 --- /dev/null +++ b/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr @@ -0,0 +1,17 @@ +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/private/test.rs:6:9 + | +LL | / unsafe { +LL | | +LL | | dbg!($v); +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index e1ba60911fe9b..e42046ddef9dd 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -75,6 +75,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1 | LL | foobar = 42 @@ -157,6 +158,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1 | LL | barfoo = 53 @@ -239,6 +241,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1 | LL | allow_mixed_uninlined_format_args = true From dfc9c9132b7087252fde2734a9ba32013f246a7a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 8 May 2024 13:10:50 -0400 Subject: [PATCH 46/58] Inline MemCategorization into ExprUseVisitor --- clippy_lints/src/operators/assign_op_pattern.rs | 4 ++-- clippy_lints/src/unwrap.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 435eb9048f587..8effe6ab54db5 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -120,7 +120,7 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { let mut s = S(HirIdSet::default()); let infcx = cx.tcx.infer_ctxt().build(); - let mut v = ExprUseVisitor::new( + let v = ExprUseVisitor::new( &mut s, &infcx, cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), @@ -152,7 +152,7 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { let mut s = S(HirIdSet::default()); let infcx = cx.tcx.infer_ctxt().build(); - let mut v = ExprUseVisitor::new( + let v = ExprUseVisitor::new( &mut s, &infcx, cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 2622abd59cbd6..6aec3dfa45cbb 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -253,7 +253,7 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { }; let infcx = self.cx.tcx.infer_ctxt().build(); - let mut vis = ExprUseVisitor::new( + let vis = ExprUseVisitor::new( &mut delegate, &infcx, cond.hir_id.owner.def_id, From db193c1c9da7aa97d25815c5d2020d2bf5bbc038 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 8 May 2024 14:08:18 -0400 Subject: [PATCH 47/58] Make LateCtxt be a type info delegate for EUV for clippy --- clippy_lints/src/escape.rs | 4 +--- clippy_lints/src/loops/mut_range_bound.rs | 10 +++------- .../src/methods/iter_overeager_cloned.rs | 9 +++------ clippy_lints/src/needless_pass_by_ref_mut.rs | 11 ++++------- clippy_lints/src/needless_pass_by_value.rs | 4 +--- .../src/operators/assign_op_pattern.rs | 19 ++----------------- clippy_lints/src/unwrap.rs | 10 +++------- clippy_utils/src/sugg.rs | 4 +--- clippy_utils/src/usage.rs | 10 +++------- 9 files changed, 21 insertions(+), 60 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 386d4c3c317f6..6392ca13df18a 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::{intravisit, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::layout::LayoutOf; @@ -105,8 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { too_large_for_stack: self.too_large_for_stack, }; - let infcx = cx.tcx.infer_ctxt().build(); - ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); + ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v).consume_body(body); for node in v.set { span_lint_hir( diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 5047092192f42..082c5977cbdee 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -4,7 +4,6 @@ use clippy_utils::{get_enclosing_block, higher, path_to_local}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Node, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; @@ -61,13 +60,10 @@ fn check_for_mutation( span_low: None, span_high: None, }; - let infcx = cx.tcx.infer_ctxt().build(); - ExprUseVisitor::new( - &mut delegate, - &infcx, + ExprUseVisitor::for_clippy( + cx, body.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), + &mut delegate, ) .walk_expr(body); diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index deac159457a66..d4c709de97f51 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -69,14 +69,11 @@ pub(super) fn check<'tcx>( let mut delegate = MoveDelegate { used_move: HirIdSet::default(), }; - let infcx = cx.tcx.infer_ctxt().build(); - ExprUseVisitor::new( + ExprUseVisitor::for_clippy( + cx, + closure.def_id, &mut delegate, - &infcx, - closure.body.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), ) .consume_body(body); diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 9e47c3ad0b7fb..5e786c1277acb 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -11,7 +11,6 @@ use rustc_hir::{ PatKind, }; use rustc_hir_typeck::expr_use_visitor as euv; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath}; @@ -102,7 +101,6 @@ fn should_skip<'tcx>( fn check_closures<'tcx>( ctx: &mut MutablyUsedVariablesCtxt<'tcx>, cx: &LateContext<'tcx>, - infcx: &InferCtxt<'tcx>, checked_closures: &mut FxHashSet, closures: FxHashSet, ) { @@ -119,7 +117,7 @@ fn check_closures<'tcx>( .associated_body() .map(|(_, body_id)| hir.body(body_id)) { - euv::ExprUseVisitor::new(ctx, infcx, closure, cx.param_env, cx.typeck_results()).consume_body(body); + euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx).consume_body(body); } } } @@ -196,8 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { async_closures: FxHashSet::default(), tcx: cx.tcx, }; - let infcx = cx.tcx.infer_ctxt().build(); - euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); + euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body); let mut checked_closures = FxHashSet::default(); @@ -210,13 +207,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { } ControlFlow::<()>::Continue(()) }); - check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); + check_closures(&mut ctx, cx, &mut checked_closures, closures); if is_async { while !ctx.async_closures.is_empty() { let async_closures = ctx.async_closures.clone(); ctx.async_closures.clear(); - check_closures(&mut ctx, cx, &infcx, &mut checked_closures, async_closures); + check_closures(&mut ctx, cx, &mut checked_closures, async_closures); } } ctx.generate_mutably_used_ids_from_aliases() diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 39d374d0d27fd..60523ae0d0e2e 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -13,7 +13,6 @@ use rustc_hir::{ TyKind, }; use rustc_hir_typeck::expr_use_visitor as euv; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -134,8 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // function body. let MovedVariablesCtxt { moved_vars } = { let mut ctx = MovedVariablesCtxt::default(); - let infcx = cx.tcx.infer_ctxt().build(); - euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); + euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body); ctx }; diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 8effe6ab54db5..6d617447bb5fb 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -11,7 +11,6 @@ use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, Pl use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::BorrowKind; -use rustc_trait_selection::infer::TyCtxtInferExt; use super::ASSIGN_OP_PATTERN; @@ -119,14 +118,7 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { } let mut s = S(HirIdSet::default()); - let infcx = cx.tcx.infer_ctxt().build(); - let v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); + let v = ExprUseVisitor::for_clippy(cx, e.hir_id.owner.def_id, &mut s); v.consume_expr(e); s.0 } @@ -151,14 +143,7 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { } let mut s = S(HirIdSet::default()); - let infcx = cx.tcx.infer_ctxt().build(); - let v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); + let v = ExprUseVisitor::for_clippy(cx, e.hir_id.owner.def_id, &mut s); v.consume_expr(e); s.0 } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 6aec3dfa45cbb..aa5555d65f63d 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -6,7 +6,6 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; @@ -252,13 +251,10 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { local_id: unwrap_info.local_id, }; - let infcx = self.cx.tcx.infer_ctxt().build(); - let vis = ExprUseVisitor::new( - &mut delegate, - &infcx, + let vis = ExprUseVisitor::for_clippy( + self.cx, cond.hir_id.owner.def_id, - self.cx.param_env, - self.cx.typeck_results(), + &mut delegate, ); vis.walk_expr(cond); vis.walk_expr(branch); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 8d6057272c4e7..49b0eb05d2acc 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -11,7 +11,6 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::{FakeReadCause, Mutability}; @@ -831,8 +830,7 @@ pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Opti applicability: Applicability::MachineApplicable, }; - let infcx = cx.tcx.infer_ctxt().build(); - ExprUseVisitor::new(&mut visitor, &infcx, def_id, cx.param_env, cx.typeck_results()).consume_body(closure_body); + ExprUseVisitor::for_clippy(cx, def_id, &mut visitor).consume_body(closure_body); if !visitor.suggestion_start.is_empty() { return Some(DerefClosure { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index a145920aa85ed..d0ab6d434aab6 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -5,7 +5,6 @@ use hir::def::Res; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir, Expr, ExprKind, HirId, HirIdSet}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, Place, PlaceBase, PlaceWithHirId}; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; @@ -17,13 +16,10 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> used_mutably: HirIdSet::default(), skip: false, }; - let infcx = cx.tcx.infer_ctxt().build(); - ExprUseVisitor::new( - &mut delegate, - &infcx, + ExprUseVisitor::for_clippy( + cx, expr.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), + &mut delegate, ) .walk_expr(expr); From e65cefcf6f22cb482bcd2ae40c99f6baab8d99bc Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 9 May 2024 11:33:53 -0400 Subject: [PATCH 48/58] Propagate errors rather than using return_if_err --- clippy_lints/src/escape.rs | 2 +- clippy_lints/src/lib.rs | 1 + clippy_lints/src/loops/mut_range_bound.rs | 2 +- clippy_lints/src/methods/iter_overeager_cloned.rs | 3 +-- clippy_lints/src/needless_pass_by_ref_mut.rs | 4 ++-- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/operators/assign_op_pattern.rs | 4 ++-- clippy_lints/src/unwrap.rs | 4 ++-- clippy_utils/src/lib.rs | 1 + clippy_utils/src/sugg.rs | 4 +++- clippy_utils/src/usage.rs | 3 ++- 11 files changed, 17 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 6392ca13df18a..6715de52649d4 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { too_large_for_stack: self.too_large_for_stack, }; - ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v).consume_body(body); + ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v).consume_body(body).into_ok(); for node in v.set { span_lint_hir( diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2c44c3881aa77..a8bfbbdd9ecab 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -8,6 +8,7 @@ #![feature(never_type)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] +#![feature(unwrap_infallible)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow( diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 082c5977cbdee..6b9ecf5f14130 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -65,7 +65,7 @@ fn check_for_mutation( body.hir_id.owner.def_id, &mut delegate, ) - .walk_expr(body); + .walk_expr(body).into_ok(); delegate.mutation_span() } diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index d4c709de97f51..a52d38790a2bd 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -9,7 +9,6 @@ use rustc_lint::LateContext; use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; use rustc_span::sym; -use rustc_trait_selection::infer::TyCtxtInferExt; use super::ITER_OVEREAGER_CLONED; use crate::redundant_clone::REDUNDANT_CLONE; @@ -75,7 +74,7 @@ pub(super) fn check<'tcx>( closure.def_id, &mut delegate, ) - .consume_body(body); + .consume_body(body).into_ok(); let mut to_be_discarded = false; diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 5e786c1277acb..9b852f52ea1eb 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -117,7 +117,7 @@ fn check_closures<'tcx>( .associated_body() .map(|(_, body_id)| hir.body(body_id)) { - euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx).consume_body(body); + euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx).consume_body(body).into_ok(); } } } @@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { async_closures: FxHashSet::default(), tcx: cx.tcx, }; - euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body); + euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok(); let mut checked_closures = FxHashSet::default(); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 60523ae0d0e2e..0986571d0f281 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // function body. let MovedVariablesCtxt { moved_vars } = { let mut ctx = MovedVariablesCtxt::default(); - euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body); + euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok(); ctx }; diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 6d617447bb5fb..910e584a7a0f9 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -119,7 +119,7 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { let mut s = S(HirIdSet::default()); let v = ExprUseVisitor::for_clippy(cx, e.hir_id.owner.def_id, &mut s); - v.consume_expr(e); + v.consume_expr(e).into_ok(); s.0 } @@ -144,6 +144,6 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { let mut s = S(HirIdSet::default()); let v = ExprUseVisitor::for_clippy(cx, e.hir_id.owner.def_id, &mut s); - v.consume_expr(e); + v.consume_expr(e).into_ok(); s.0 } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index aa5555d65f63d..5b2841dcd8330 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -256,8 +256,8 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { cond.hir_id.owner.def_id, &mut delegate, ); - vis.walk_expr(cond); - vis.walk_expr(branch); + vis.walk_expr(cond).into_ok(); + vis.walk_expr(branch).into_ok(); if delegate.is_mutated { // if the variable is mutated, we don't know whether it can be unwrapped. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a49414a058b1c..99d7aba2f7a15 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -7,6 +7,7 @@ #![feature(never_type)] #![feature(rustc_private)] #![feature(assert_matches)] +#![feature(unwrap_infallible)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow( diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 49b0eb05d2acc..bf03c6c16015c 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -830,7 +830,9 @@ pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Opti applicability: Applicability::MachineApplicable, }; - ExprUseVisitor::for_clippy(cx, def_id, &mut visitor).consume_body(closure_body); + ExprUseVisitor::for_clippy(cx, def_id, &mut visitor) + .consume_body(closure_body) + .into_ok(); if !visitor.suggestion_start.is_empty() { return Some(DerefClosure { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index d0ab6d434aab6..9abb4ef9b8d3a 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -21,7 +21,8 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> expr.hir_id.owner.def_id, &mut delegate, ) - .walk_expr(expr); + .walk_expr(expr) + .into_ok(); if delegate.skip { return None; From 12ec009f5a200c89f43f80615369daab1d28b812 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 12 May 2024 21:59:45 -0300 Subject: [PATCH 49/58] Fix #12760 --- clippy_lints/src/doc/missing_headers.rs | 9 ++++----- clippy_lints/src/doc/mod.rs | 13 ++++++++----- tests/ui/missing_panics_doc.rs | 8 ++++++++ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index f935ae2e3e489..679fae598974f 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -1,3 +1,4 @@ +use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_doc_hidden, return_ty}; @@ -6,15 +7,13 @@ use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::{sym, Span}; -use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; - pub fn check( cx: &LateContext<'_>, owner_id: OwnerId, sig: FnSig<'_>, headers: DocHeaders, body_id: Option, - panic_span: Option, + panic_info: Option<(Span, bool)>, check_private_items: bool, ) { if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) { @@ -48,13 +47,13 @@ pub fn check( ), _ => (), } - if !headers.panics && panic_span.is_some() { + if !headers.panics && panic_info.map_or(false, |el| !el.1) { span_lint_and_note( cx, MISSING_PANICS_DOC, span, "docs for function which may panic missing `# Panics` section", - panic_span, + panic_info.map(|el| el.0), "first possible panic found here", ); } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 29ccf4fceb5ba..9ee4aad337d21 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; -use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; +use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; @@ -461,14 +461,14 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { let body = cx.tcx.hir().body(body_id); - let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); + let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); missing_headers::check( cx, item.owner_id, sig, headers, Some(body_id), - panic_span, + panic_info, self.check_private_items, ); } @@ -806,6 +806,7 @@ fn check_doc<'a, Events: Iterator, Range { cx: &'a LateContext<'tcx>, + is_const: bool, panic_span: Option, typeck_results: &'tcx ty::TypeckResults<'tcx>, } @@ -815,14 +816,15 @@ impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { cx: &'a LateContext<'tcx>, typeck_results: &'tcx ty::TypeckResults<'tcx>, body: impl Visitable<'tcx>, - ) -> Option { + ) -> Option<(Span, bool)> { let mut vis = Self { cx, + is_const: false, panic_span: None, typeck_results, }; body.visit(&mut vis); - vis.panic_span + vis.panic_span.map(|el| (el, vis.is_const)) } } @@ -841,6 +843,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { "assert" | "assert_eq" | "assert_ne" ) { + self.is_const = in_constant(self.cx, expr.hir_id); self.panic_span = Some(macro_call.span); } } diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index 0e1533fc1ab10..b0fa8e9885988 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -191,3 +191,11 @@ fn from_declared_macro_should_lint_at_macrosite() { // Not here. some_macro_that_panics!() } + +pub fn issue_12760() { + const { + if N == 0 { + panic!(); + } + } +} From 760fbdf64ed90e958b6da891c21683a4d4f0bbe2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 13 May 2024 10:00:38 -0400 Subject: [PATCH 50/58] split out AliasTy -> AliasTerm --- clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- .../src/needless_borrows_for_generic_args.rs | 13 +++++-------- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_utils/src/ty.rs | 4 ++-- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index d3347466be982..ae9aa83efd685 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -417,7 +417,7 @@ fn get_input_traits_and_projections<'tcx>( } }, ClauseKind::Projection(projection_predicate) => { - if projection_predicate.projection_ty.self_ty() == input { + if projection_predicate.projection_term.self_ty() == input { projection_predicates.push(projection_predicate); } }, diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index e6c6a15b8d48a..8de5caf32b740 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -320,11 +320,11 @@ fn is_mixed_projection_predicate<'tcx>( && (term_param_ty.index as usize) < generics.parent_count { // The inner-most self type is a type parameter from the current function. - let mut projection_ty = projection_predicate.projection_ty; + let mut projection_ty = projection_predicate.projection_term; loop { - match projection_ty.self_ty().kind() { + match *projection_ty.self_ty().kind() { ty::Alias(ty::Projection, inner_projection_ty) => { - projection_ty = *inner_projection_ty; + projection_ty = inner_projection_ty.into(); }, ty::Param(param_ty) => { return (param_ty.index as usize) >= generics.parent_count; @@ -404,14 +404,11 @@ fn replace_types<'tcx>( // The `replaced.insert(...)` check provides some protection against infinite loops. if replaced.insert(param_ty.index) { for projection_predicate in projection_predicates { - if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx) + if projection_predicate.projection_term.self_ty() == param_ty.to_ty(cx.tcx) && let Some(term_ty) = projection_predicate.term.ty() && let ty::Param(term_param_ty) = term_ty.kind() { - let projection = cx.tcx.mk_ty_from_kind(ty::Alias( - ty::Projection, - projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty), - )); + let projection = projection_predicate.projection_term.with_self_ty(cx.tcx, new_ty).expect_ty(cx.tcx).to_ty(cx.tcx); if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) && args[term_param_ty.index as usize] != GenericArg::from(projected_ty) diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 214b69dc9250b..f0d1458a59b25 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -66,7 +66,7 @@ fn get_projection_pred<'tcx>( let projection_pred = cx .tcx .instantiate_bound_regions_with_erased(proj_pred.kind().rebind(pred)); - if projection_pred.projection_ty.args == trait_pred.trait_ref.args { + if projection_pred.projection_term.args == trait_pred.trait_ref.args { return Some(projection_pred); } } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 97bba8648c5f5..e3ab42c3107c5 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -795,7 +795,7 @@ fn sig_from_bounds<'tcx>( inputs = Some(i); }, ty::ClauseKind::Projection(p) - if Some(p.projection_ty.def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty => + if Some(p.projection_term.def_id) == lang_items.fn_once_output() && p.projection_term.self_ty() == ty => { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? @@ -834,7 +834,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option } inputs = Some(i); }, - ty::ClauseKind::Projection(p) if Some(p.projection_ty.def_id) == lang_items.fn_once_output() => { + ty::ClauseKind::Projection(p) if Some(p.projection_term.def_id) == lang_items.fn_once_output() => { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? return None; From 2701a4175fc5636c3b74ad268f587c100173b5c1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 13 May 2024 14:34:47 -0400 Subject: [PATCH 51/58] Apply nits --- clippy_lints/src/needless_borrows_for_generic_args.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index 8de5caf32b740..ae6cf992ef7b4 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -320,11 +320,11 @@ fn is_mixed_projection_predicate<'tcx>( && (term_param_ty.index as usize) < generics.parent_count { // The inner-most self type is a type parameter from the current function. - let mut projection_ty = projection_predicate.projection_term; + let mut projection_term = projection_predicate.projection_term; loop { - match *projection_ty.self_ty().kind() { + match *projection_term.self_ty().kind() { ty::Alias(ty::Projection, inner_projection_ty) => { - projection_ty = inner_projection_ty.into(); + projection_term = inner_projection_ty.into(); }, ty::Param(param_ty) => { return (param_ty.index as usize) >= generics.parent_count; From dc5b99b3b64c2ba6d2ef7e30c1b53abf80858470 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Mon, 13 May 2024 18:29:41 +0800 Subject: [PATCH 52/58] improve [`match_same_arms`] messages, enable rustfix test --- clippy_lints/src/matches/match_same_arms.rs | 37 ++- tests/ui/match_same_arms.stderr | 122 +++++---- tests/ui/match_same_arms2.fixed | 241 ++++++++++++++++ tests/ui/match_same_arms2.rs | 5 +- tests/ui/match_same_arms2.stderr | 257 +++++++++--------- tests/ui/match_same_arms_non_exhaustive.fixed | 61 +++++ tests/ui/match_same_arms_non_exhaustive.rs | 1 - .../ui/match_same_arms_non_exhaustive.stderr | 18 +- 8 files changed, 533 insertions(+), 209 deletions(-) create mode 100644 tests/ui/match_same_arms2.fixed create mode 100644 tests/ui/match_same_arms_non_exhaustive.fixed diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index cd61e733694bb..da8c918a62bb6 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash}; use core::cmp::Ordering; use core::{iter, slice}; @@ -9,9 +9,9 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_lint::LateContext; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; -use rustc_span::{ErrorGuaranteed, Symbol}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; use super::MATCH_SAME_ARMS; @@ -110,20 +110,22 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { && check_same_body() }; + let mut appl = Applicability::MaybeIncorrect; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) { if matches!(arm2.pat.kind, PatKind::Wild) { if !cx.tcx.features().non_exhaustive_omitted_patterns_lint || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id) { + let arm_span = adjusted_arm_span(cx, arm1.span); span_lint_hir_and_then( cx, MATCH_SAME_ARMS, arm1.hir_id, - arm1.span, + arm_span, "this match arm has an identical body to the `_` wildcard arm", |diag| { - diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect) + diag.span_suggestion(arm_span, "try removing the arm", "", appl) .help("or try changing either arm body") .span_note(arm2.span, "`_` wildcard arm here"); }, @@ -144,23 +146,36 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { keep_arm.span, "this match arm has an identical body to another arm", |diag| { - let move_pat_snip = snippet(cx, move_arm.pat.span, ""); - let keep_pat_snip = snippet(cx, keep_arm.pat.span, ""); + let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "", &mut appl); + let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "", &mut appl); diag.span_suggestion( keep_arm.pat.span, - "try merging the arm patterns", + "or try merging the arm patterns", format!("{keep_pat_snip} | {move_pat_snip}"), - Applicability::MaybeIncorrect, + appl, ) - .help("or try changing either arm body") - .span_note(move_arm.span, "other arm here"); + .span_suggestion( + adjusted_arm_span(cx, move_arm.span), + "and remove this obsolete arm", + "", + appl, + ) + .help("try changing either arm body"); }, ); } } } +/// Extend arm's span to include the comma and whitespaces after it. +fn adjusted_arm_span(cx: &LateContext<'_>, span: Span) -> Span { + let source_map = cx.sess().source_map(); + source_map + .span_extend_while(span, |c| c == ',' || c.is_ascii_whitespace()) + .unwrap_or(span) +} + #[derive(Clone, Copy)] enum NormalizedPat<'a> { Wild, diff --git a/tests/ui/match_same_arms.stderr b/tests/ui/match_same_arms.stderr index a926570b60ada..3c0382767c3fc 100644 --- a/tests/ui/match_same_arms.stderr +++ b/tests/ui/match_same_arms.stderr @@ -2,7 +2,7 @@ error: this match arm has an identical body to the `_` wildcard arm --> tests/ui/match_same_arms.rs:12:9 | LL | Abc::A => 0, - | ^^^^^^^^^^^ help: try removing the arm + | ^^^^^^^^^^^^^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here @@ -17,106 +17,114 @@ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:18:9 | LL | (1, .., 3) => 42, - | ----------^^^^^^ - | | - | help: try merging the arm patterns: `(1, .., 3) | (.., 3)` + | ^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:19:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (1, .., 3) | (.., 3) => 42, + | ~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (.., 3) => 42, | -LL | (.., 3) => 42, - | ^^^^^^^^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:25:9 | LL | 51 => 1, - | --^^^^^ - | | - | help: try merging the arm patterns: `51 | 42` + | ^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:24:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 51 | 42 => 1, + | ~~~~~~~ +help: and remove this obsolete arm + | +LL - 42 => 1, | -LL | 42 => 1, - | ^^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:26:9 | LL | 41 => 2, - | --^^^^^ - | | - | help: try merging the arm patterns: `41 | 52` + | ^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:27:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 41 | 52 => 2, + | ~~~~~~~ +help: and remove this obsolete arm + | +LL - 52 => 2, | -LL | 52 => 2, - | ^^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:33:9 | LL | 2 => 2, - | -^^^^^ - | | - | help: try merging the arm patterns: `2 | 1` + | ^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:32:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 2 | 1 => 2, + | ~~~~~ +help: and remove this obsolete arm + | +LL - 1 => 2, | -LL | 1 => 2, - | ^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:35:9 | LL | 3 => 2, - | -^^^^^ - | | - | help: try merging the arm patterns: `3 | 1` + | ^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:32:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 3 | 1 => 2, + | ~~~~~ +help: and remove this obsolete arm + | +LL - 1 => 2, | -LL | 1 => 2, - | ^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:33:9 | LL | 2 => 2, - | -^^^^^ - | | - | help: try merging the arm patterns: `2 | 3` + | ^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:35:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 2 | 3 => 2, + | ~~~~~ +help: and remove this obsolete arm + | +LL - 3 => 2, +LL + | -LL | 3 => 2, - | ^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:52:17 | LL | CommandInfo::External { name, .. } => name.to_string(), - | ----------------------------------^^^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:51:17 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - CommandInfo::BuiltIn { name, .. } => name.to_string(), | -LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 8 previous errors diff --git a/tests/ui/match_same_arms2.fixed b/tests/ui/match_same_arms2.fixed new file mode 100644 index 0000000000000..fba0cf33b3c26 --- /dev/null +++ b/tests/ui/match_same_arms2.fixed @@ -0,0 +1,241 @@ +#![warn(clippy::match_same_arms)] +#![allow( + clippy::disallowed_names, + clippy::diverging_sub_expression, + clippy::uninlined_format_args, + clippy::match_single_binding, + clippy::match_like_matches_macro +)] +fn bar(_: T) {} +fn foo() -> bool { + unimplemented!() +} + +fn match_same_arms() { + let _ = match 42 { + _ => { + foo(); + let mut a = 42 + [23].len() as i32; + if true { + a += 7; + } + a = -31 - a; + a + }, + }; + //~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm + + let _ = match 42 { + 51 | 42 => foo(), //~ ERROR: this match arm has an identical body to another arm + _ => true, + }; + + let _ = match Some(42) { + None | Some(_) => 24, //~ ERROR: this match arm has an identical body to another arm + }; + + let _ = match Some(42) { + Some(foo) => 24, + None => 24, + }; + + let _ = match Some(42) { + Some(42) => 24, + Some(a) => 24, // bindings are different + None => 0, + }; + + let _ = match Some(42) { + Some(a) if a > 0 => 24, + Some(a) => 24, // one arm has a guard + None => 0, + }; + + match (Some(42), Some(42)) { + (None, Some(a)) | (Some(a), None) => bar(a), //~ ERROR: this match arm has an identical body to another arm + _ => (), + } + + // No warning because guards are different + let _ = match Some(42) { + Some(a) if a == 42 => a, + Some(a) if a == 24 => a, + Some(_) => 24, + None => 0, + }; + + let _ = match (Some(42), Some(42)) { + (None, Some(a)) | (Some(a), None) if a == 42 => a, //~ ERROR: this match arm has an identical body to another arm + _ => 0, + }; + + match (Some(42), Some(42)) { + (Some(a), ..) | (.., Some(a)) => bar(a), //~ ERROR: this match arm has an identical body to another arm + _ => (), + } + + let _ = match Some(()) { + Some(()) => 0.0, + None => -0.0, + }; + + match (Some(42), Some("")) { + (Some(a), None) => bar(a), + (None, Some(a)) => bar(a), // bindings have different types + _ => (), + } + + let x: Result = Ok(3); + + // No warning because of the guard. + match x { + Ok(x) if x * x == 64 => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + // This used to be a false positive; see issue #1996. + match x { + Ok(3) => println!("ok"), + Ok(x) if x * x == 64 => println!("ok 64"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + match (x, Some(1i32)) { + (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), //~ ERROR: this match arm has an identical body to another arm + _ => println!("err"), + } + + // No warning; different types for `x`. + match (x, Some(1.0f64)) { + (Ok(x), Some(_)) => println!("ok {}", x), + (Ok(_), Some(x)) => println!("ok {}", x), + _ => println!("err"), + } + + // False negative #2251. + match x { + Ok(_tmp) => println!("ok"), + Ok(_) | Ok(3) => println!("ok"), //~ ERROR: this match arm has an identical body to another arm + Err(_) => { + unreachable!(); + }, + } + + // False positive #1390 + macro_rules! empty { + ($e:expr) => {}; + } + match 0 { + 0 => { + empty!(0); + }, + 1 => { + empty!(1); + }, + x => { + empty!(x); + }, + }; + + // still lint if the tokens are the same + match 0 { + 1 | 0 => { + empty!(0); + }, + x => { + empty!(x); + }, + } + //~^^^^^^^ ERROR: this match arm has an identical body to another arm + + match_expr_like_matches_macro_priority(); +} + +fn match_expr_like_matches_macro_priority() { + enum E { + A, + B, + C, + } + let x = E::A; + let _ans = match x { + E::A => false, + E::B => false, + _ => true, + }; +} + +fn main() { + let _ = match Some(0) { + Some(0) => 0, + Some(1) => 1, + #[cfg(feature = "foo")] + Some(2) => 2, + _ => 1, + }; + + enum Foo { + X(u32), + Y(u32), + Z(u32), + } + + // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Suggest moving `Foo::Z(_)` up. + let _ = match Foo::X(0) { + Foo::X(0) | Foo::Z(_) => 1, //~ ERROR: this match arm has an identical body to another arm + Foo::X(_) | Foo::Y(_) => 2, + _ => 0, + }; + + // Suggest moving `Foo::X(0)` down. + let _ = match Foo::X(0) { + Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) | Foo::X(0) => 1, //~ ERROR: this match arm has an identical body to another arm + _ => 0, + }; + + // Don't lint. + let _ = match 0 { + -2 => 1, + -5..=50 => 2, + -150..=88 => 1, + _ => 3, + }; + + struct Bar { + x: u32, + y: u32, + z: u32, + } + + // Lint. + let _ = match None { + Some(Bar { y: 10, z: 0, .. }) => 2, + None => 50, + Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, //~ ERROR: this match arm has an identical body to another arm + _ => 200, + }; + + let _ = match 0 { + 0 => todo!(), + 1 => todo!(), + 2 => core::convert::identity::(todo!()), + 3 => core::convert::identity::(todo!()), + _ => 5, + }; + + let _ = match 0 { + 1 | 0 => cfg!(not_enable), + _ => false, + }; +} diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index 85ad0962eb4bf..8a4e3b325bbff 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -2,9 +2,10 @@ #![allow( clippy::disallowed_names, clippy::diverging_sub_expression, - clippy::uninlined_format_args + clippy::uninlined_format_args, + clippy::match_single_binding, + clippy::match_like_matches_macro )] -//@no-rustfix fn bar(_: T) {} fn foo() -> bool { unimplemented!() diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index f4c38c1af8975..3d15176ccf997 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -1,18 +1,18 @@ error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms2.rs:15:9 + --> tests/ui/match_same_arms2.rs:16:9 | LL | / 42 => { LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; LL | | if true { ... | -LL | | a LL | | }, - | |_________^ help: try removing the arm +LL | | _ => { + | |________^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:24:9 + --> tests/ui/match_same_arms2.rs:25:9 | LL | / _ => { LL | | foo(); @@ -26,203 +26,200 @@ LL | | }, = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:38:9 + --> tests/ui/match_same_arms2.rs:39:9 | LL | 51 => foo(), - | --^^^^^^^^^ - | | - | help: try merging the arm patterns: `51 | 42` + | ^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:37:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 51 | 42 => foo(), + | ~~~~~~~ +help: and remove this obsolete arm + | +LL - 42 => foo(), | -LL | 42 => foo(), - | ^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:44:9 + --> tests/ui/match_same_arms2.rs:45:9 | LL | None => 24, - | ----^^^^^^ - | | - | help: try merging the arm patterns: `None | Some(_)` + | ^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:43:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | None | Some(_) => 24, + | ~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Some(_) => 24, | -LL | Some(_) => 24, - | ^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:66:9 + --> tests/ui/match_same_arms2.rs:67:9 | LL | (None, Some(a)) => bar(a), - | ---------------^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:65:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (None, Some(a)) | (Some(a), None) => bar(a), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (Some(a), None) => bar(a), | -LL | (Some(a), None) => bar(a), - | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:80:9 + --> tests/ui/match_same_arms2.rs:81:9 | LL | (None, Some(a)) if a == 42 => a, - | ---------------^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:79:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (None, Some(a)) | (Some(a), None) if a == 42 => a, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (Some(a), None) if a == 42 => a, | -LL | (Some(a), None) if a == 42 => a, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:85:9 + --> tests/ui/match_same_arms2.rs:86:9 | LL | (Some(a), ..) => bar(a), - | -------------^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))` + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:86:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (Some(a), ..) | (.., Some(a)) => bar(a), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (.., Some(a)) => bar(a), | -LL | (.., Some(a)) => bar(a), - | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:119:9 + --> tests/ui/match_same_arms2.rs:120:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), - | ----------------^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:120:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (Ok(_), Some(x)) => println!("ok {}", x), | -LL | (Ok(_), Some(x)) => println!("ok {}", x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:135:9 + --> tests/ui/match_same_arms2.rs:136:9 | LL | Ok(_) => println!("ok"), - | -----^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `Ok(_) | Ok(3)` + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:134:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Ok(_) | Ok(3) => println!("ok"), + | ~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Ok(3) => println!("ok"), | -LL | Ok(3) => println!("ok"), - | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:162:9 + --> tests/ui/match_same_arms2.rs:163:9 | -LL | 1 => { - | ^ help: try merging the arm patterns: `1 | 0` - | _________| - | | +LL | / 1 => { LL | | empty!(0); LL | | }, | |_________^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:159:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 1 | 0 => { + | ~~~~~ +help: and remove this obsolete arm + | +LL - 0 => { +LL - empty!(0); +LL - }, | -LL | / 0 => { -LL | | empty!(0); -LL | | }, - | |_________^ - -error: match expression looks like `matches!` macro - --> tests/ui/match_same_arms2.rs:181:16 - | -LL | let _ans = match x { - | ________________^ -LL | | E::A => false, -LL | | E::B => false, -LL | | _ => true, -LL | | }; - | |_____^ help: try: `!matches!(x, E::A | E::B)` - | - = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:213:9 + --> tests/ui/match_same_arms2.rs:214:9 | LL | Foo::X(0) => 1, - | ---------^^^^^ - | | - | help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)` + | ^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:215:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Foo::X(0) | Foo::Z(_) => 1, + | ~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Foo::Z(_) => 1, | -LL | Foo::Z(_) => 1, - | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:223:9 + --> tests/ui/match_same_arms2.rs:224:9 | LL | Foo::Z(_) => 1, - | ---------^^^^^ - | | - | help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)` + | ^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:221:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Foo::Z(_) | Foo::X(0) => 1, + | ~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Foo::X(0) => 1, | -LL | Foo::X(0) => 1, - | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:246:9 + --> tests/ui/match_same_arms2.rs:247:9 | LL | Some(Bar { y: 0, x: 5, .. }) => 1, - | ----------------------------^^^^^ - | | - | help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:243:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Some(Bar { x: 0, y: 5, .. }) => 1, | -LL | Some(Bar { x: 0, y: 5, .. }) => 1, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:260:9 + --> tests/ui/match_same_arms2.rs:261:9 | LL | 1 => cfg!(not_enable), - | -^^^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `1 | 0` + | ^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:259:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 1 | 0 => cfg!(not_enable), + | ~~~~~ +help: and remove this obsolete arm + | +LL - 0 => cfg!(not_enable), | -LL | 0 => cfg!(not_enable), - | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/match_same_arms_non_exhaustive.fixed b/tests/ui/match_same_arms_non_exhaustive.fixed new file mode 100644 index 0000000000000..804c0a869a9ff --- /dev/null +++ b/tests/ui/match_same_arms_non_exhaustive.fixed @@ -0,0 +1,61 @@ +#![feature(non_exhaustive_omitted_patterns_lint)] +#![warn(clippy::match_same_arms)] +#![no_main] +use std::sync::atomic::Ordering; // #[non_exhaustive] enum + +fn repeat() -> ! { + panic!() +} + +pub fn f(x: Ordering) { + #[deny(non_exhaustive_omitted_patterns)] + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel | Ordering::SeqCst => repeat(), + _ => repeat(), + } +} + +mod f { + #![deny(non_exhaustive_omitted_patterns)] + + use super::*; + + pub fn f(x: Ordering) { + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel | Ordering::SeqCst => repeat(), + _ => repeat(), + } + } +} + +// Below should still lint + +pub fn g(x: Ordering) { + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ ERROR: this match arm has an identical body to the `_` wildcard arm + _ => repeat(), + } +} + +mod g { + use super::*; + + pub fn g(x: Ordering) { + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ ERROR: this match arm has an identical body to the `_` wildcard arm + _ => repeat(), + } + } +} diff --git a/tests/ui/match_same_arms_non_exhaustive.rs b/tests/ui/match_same_arms_non_exhaustive.rs index 5c277f925a8f1..e50663932a1a6 100644 --- a/tests/ui/match_same_arms_non_exhaustive.rs +++ b/tests/ui/match_same_arms_non_exhaustive.rs @@ -1,7 +1,6 @@ #![feature(non_exhaustive_omitted_patterns_lint)] #![warn(clippy::match_same_arms)] #![no_main] -//@no-rustfix use std::sync::atomic::Ordering; // #[non_exhaustive] enum fn repeat() -> ! { diff --git a/tests/ui/match_same_arms_non_exhaustive.stderr b/tests/ui/match_same_arms_non_exhaustive.stderr index cf2a75354e15c..aa7f8c95dce84 100644 --- a/tests/ui/match_same_arms_non_exhaustive.stderr +++ b/tests/ui/match_same_arms_non_exhaustive.stderr @@ -1,12 +1,13 @@ error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:45:9 + --> tests/ui/match_same_arms_non_exhaustive.rs:44:9 | -LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm +LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), +LL | | + | |________^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:47:9 + --> tests/ui/match_same_arms_non_exhaustive.rs:46:9 | LL | _ => repeat(), | ^^^^^^^^^^^^^ @@ -14,14 +15,15 @@ LL | _ => repeat(), = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:59:13 + --> tests/ui/match_same_arms_non_exhaustive.rs:58:13 | -LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm +LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), +LL | | + | |____________^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:61:13 + --> tests/ui/match_same_arms_non_exhaustive.rs:60:13 | LL | _ => repeat(), | ^^^^^^^^^^^^^ From 9d790d640734c311182dc688596cad7a370d657e Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 14 May 2024 17:22:56 +0000 Subject: [PATCH 53/58] Don't lint path statements in no_effect --- clippy_lints/src/no_effect.rs | 8 +++-- tests/ui/no_effect.rs | 13 ------- tests/ui/no_effect.stderr | 68 +++++++++++++++-------------------- 3 files changed, 33 insertions(+), 56 deletions(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index f915145e794bc..87f886b1128d8 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -94,7 +94,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) { for hir_id in self.local_bindings.pop().unwrap() { - // FIXME(rust/#120456) - is `swap_remove` correct? if let Some(span) = self.underscore_bindings.swap_remove(&hir_id) { span_lint_hir( cx, @@ -109,7 +108,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(def_id) = path_to_local(expr) { - // FIXME(rust/#120456) - is `swap_remove` correct? self.underscore_bindings.swap_remove(&def_id); } } @@ -118,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { impl NoEffect { fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { if let StmtKind::Semi(expr) = stmt.kind { - // move `expr.span.from_expansion()` ahead + // Covered by rustc `path_statements` lint + if matches!(expr.kind, ExprKind::Path(_)) { + return true; + } + if expr.span.from_expansion() { return false; } diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index dabeda72f0c8c..0ea911c343488 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -1,6 +1,5 @@ #![feature(fn_traits, unboxed_closures)] #![warn(clippy::no_effect_underscore_binding)] -#![allow(dead_code, path_statements)] #![allow( clippy::deref_addrof, clippy::redundant_field_names, @@ -33,7 +32,6 @@ impl Neg for Cout { } } -struct Unit; struct Tuple(i32); struct Struct { field: i32, @@ -42,10 +40,6 @@ enum Enum { Tuple(i32), Struct { field: i32 }, } -struct DropUnit; -impl Drop for DropUnit { - fn drop(&mut self) {} -} struct DropStruct { field: i32, } @@ -117,15 +111,9 @@ impl FnOnce<(&str,)> for GreetStruct3 { fn main() { let s = get_struct(); - let s2 = get_struct(); 0; //~^ ERROR: statement with no effect - //~| NOTE: `-D clippy::no-effect` implied by `-D warnings` - s2; - //~^ ERROR: statement with no effect - Unit; - //~^ ERROR: statement with no effect Tuple(0); //~^ ERROR: statement with no effect Struct { field: 0 }; @@ -192,7 +180,6 @@ fn main() { unsafe { unsafe_fn() }; let _used = get_struct(); let _x = vec![1]; - DropUnit; DropStruct { field: 0 }; DropTuple(0); DropEnum::Tuple(0); diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index c7c8eecd054bc..48ec997d938cd 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -1,5 +1,5 @@ error: statement with no effect - --> tests/ui/no_effect.rs:122:5 + --> tests/ui/no_effect.rs:115:5 | LL | 0; | ^^ @@ -8,151 +8,139 @@ LL | 0; = help: to override `-D warnings` add `#[allow(clippy::no_effect)]` error: statement with no effect - --> tests/ui/no_effect.rs:125:5 - | -LL | s2; - | ^^^ - -error: statement with no effect - --> tests/ui/no_effect.rs:127:5 - | -LL | Unit; - | ^^^^^ - -error: statement with no effect - --> tests/ui/no_effect.rs:129:5 + --> tests/ui/no_effect.rs:117:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:131:5 + --> tests/ui/no_effect.rs:119:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:133:5 + --> tests/ui/no_effect.rs:121:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:135:5 + --> tests/ui/no_effect.rs:123:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:137:5 + --> tests/ui/no_effect.rs:125:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:139:5 + --> tests/ui/no_effect.rs:127:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:141:5 + --> tests/ui/no_effect.rs:129:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:143:5 + --> tests/ui/no_effect.rs:131:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:145:5 + --> tests/ui/no_effect.rs:133:5 | LL | &6; | ^^^ error: statement with no effect - --> tests/ui/no_effect.rs:147:5 + --> tests/ui/no_effect.rs:135:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:149:5 + --> tests/ui/no_effect.rs:137:5 | LL | ..; | ^^^ error: statement with no effect - --> tests/ui/no_effect.rs:151:5 + --> tests/ui/no_effect.rs:139:5 | LL | 5..; | ^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:153:5 + --> tests/ui/no_effect.rs:141:5 | LL | ..5; | ^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:155:5 + --> tests/ui/no_effect.rs:143:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:157:5 + --> tests/ui/no_effect.rs:145:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:159:5 + --> tests/ui/no_effect.rs:147:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:161:5 + --> tests/ui/no_effect.rs:149:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:163:5 + --> tests/ui/no_effect.rs:151:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:165:5 + --> tests/ui/no_effect.rs:153:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:167:5 + --> tests/ui/no_effect.rs:155:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:170:5 + --> tests/ui/no_effect.rs:158:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:173:5 + --> tests/ui/no_effect.rs:161:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:175:9 + --> tests/ui/no_effect.rs:163:9 | LL | let _unused = 1; | ^^^^^^^ @@ -161,22 +149,22 @@ 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 - --> tests/ui/no_effect.rs:178:9 + --> tests/ui/no_effect.rs:166:9 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:180:9 + --> tests/ui/no_effect.rs:168:9 | LL | let _duck = Struct { field: 0 }; | ^^^^^ error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:182:9 + --> tests/ui/no_effect.rs:170:9 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^ -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors From 1b7fc5f5aad72bc507e3efe9c7adb808a9835690 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Wed, 15 May 2024 16:38:57 +0800 Subject: [PATCH 54/58] add some msrv checks in `is_min_const_fn` --- clippy_config/src/msrvs.rs | 3 +- clippy_utils/src/qualify_min_const_fn.rs | 78 +++++++++++-------- .../ui/missing_const_for_fn/cant_be_const.rs | 18 ++++- .../ui/missing_const_for_fn/could_be_const.rs | 28 +++++++ .../could_be_const.stderr | 30 ++++++- 5 files changed, 121 insertions(+), 36 deletions(-) diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index 14808440d48d3..a3e7d0c3fa5fe 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -26,7 +26,8 @@ msrv_aliases! { 1,63,0 { CLONE_INTO } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST } - 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } + 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } + 1,56,0 { CONST_FN_UNION } 1,55,0 { SEEK_REWIND } 1,54,0 { INTO_KEYS } 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR } diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 325c9bee05782..dbda3c0dfe80a 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -3,7 +3,7 @@ // of terminologies might not be relevant in the context of Clippy. Note that its behavior might // differ from the time of `rustc` even if the name stays the same. -use clippy_config::msrvs::Msrv; +use clippy_config::msrvs::{self, Msrv}; use hir::LangItem; use rustc_attr::StableSince; use rustc_const_eval::transform::check_consts::ConstCx; @@ -42,7 +42,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) for bb in &*body.basic_blocks { check_terminator(tcx, body, bb.terminator(), msrv)?; for stmt in &bb.statements { - check_statement(tcx, body, def_id, stmt)?; + check_statement(tcx, body, def_id, stmt, msrv)?; } } Ok(()) @@ -102,13 +102,14 @@ fn check_rvalue<'tcx>( def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span, + msrv: &Msrv, ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { - check_place(tcx, *place, span, body) + check_place(tcx, *place, span, body, msrv) }, - Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body), + Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv), Rvalue::Repeat(operand, _) | Rvalue::Use(operand) | Rvalue::Cast( @@ -122,7 +123,7 @@ fn check_rvalue<'tcx>( | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer), operand, _, - ) => check_operand(tcx, operand, span, body), + ) => check_operand(tcx, operand, span, body, msrv), Rvalue::Cast( CastKind::PointerCoercion( PointerCoercion::UnsafeFnPointer @@ -141,7 +142,7 @@ fn check_rvalue<'tcx>( }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { - check_operand(tcx, op, span, body)?; + check_operand(tcx, op, span, body, msrv)?; // Casting/coercing things to slices is fine. Ok(()) } else { @@ -162,8 +163,8 @@ fn check_rvalue<'tcx>( )), // binops are fine on integers Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { - check_operand(tcx, lhs, span, body)?; - check_operand(tcx, rhs, span, body)?; + check_operand(tcx, lhs, span, body, msrv)?; + check_operand(tcx, rhs, span, body, msrv)?; let ty = lhs.ty(body, tcx); if ty.is_integral() || ty.is_bool() || ty.is_char() { Ok(()) @@ -179,14 +180,14 @@ fn check_rvalue<'tcx>( Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); if ty.is_integral() || ty.is_bool() { - check_operand(tcx, operand, span, body) + check_operand(tcx, operand, span, body, msrv) } else { Err((span, "only int and `bool` operations are stable in const fn".into())) } }, Rvalue::Aggregate(_, operands) => { for operand in operands { - check_operand(tcx, operand, span, body)?; + check_operand(tcx, operand, span, body, msrv)?; } Ok(()) }, @@ -198,28 +199,29 @@ fn check_statement<'tcx>( body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>, + msrv: &Msrv, ) -> McfResult { let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { - check_place(tcx, *place, span, body)?; - check_rvalue(tcx, body, def_id, rval, span) + check_place(tcx, *place, span, body, msrv)?; + check_rvalue(tcx, body, def_id, rval, span, msrv) }, - StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body), + StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body, msrv), // just an assignment StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { - check_place(tcx, **place, span, body) + check_place(tcx, **place, span, body, msrv) }, - StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body), + StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body, msrv), StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping( rustc_middle::mir::CopyNonOverlapping { dst, src, count }, )) => { - check_operand(tcx, dst, span, body)?; - check_operand(tcx, src, span, body)?; - check_operand(tcx, count, span, body) + check_operand(tcx, dst, span, body, msrv)?; + check_operand(tcx, src, span, body, msrv)?; + check_operand(tcx, count, span, body, msrv) }, // These are all NOPs StatementKind::StorageLive(_) @@ -233,7 +235,13 @@ fn check_statement<'tcx>( } } -fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_operand<'tcx>( + tcx: TyCtxt<'tcx>, + operand: &Operand<'tcx>, + span: Span, + body: &Body<'tcx>, + msrv: &Msrv, +) -> McfResult { match operand { Operand::Move(place) => { if !place.projection.as_ref().is_empty() @@ -245,9 +253,9 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b )); } - check_place(tcx, *place, span, body) + check_place(tcx, *place, span, body, msrv) }, - Operand::Copy(place) => check_place(tcx, *place, span, body), + Operand::Copy(place) => check_place(tcx, *place, span, body, msrv), Operand::Constant(c) => match c.check_static_ptr(tcx) { Some(_) => Err((span, "cannot access `static` items in const fn".into())), None => Ok(()), @@ -255,23 +263,27 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b } } -fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>, msrv: &Msrv) -> McfResult { for (base, elem) in place.as_ref().iter_projections() { match elem { ProjectionElem::Field(..) => { - let base_ty = base.ty(body, tcx).ty; - if let Some(def) = base_ty.ty_adt_def() { - // No union field accesses in `const fn` - if def.is_union() { - return Err((span, "accessing union fields is unstable".into())); - } + if base.ty(body, tcx).ty.is_union() && !msrv.meets(msrvs::CONST_FN_UNION) { + return Err((span, "accessing union fields is unstable".into())); } }, + ProjectionElem::Deref => match base.ty(body, tcx).ty.kind() { + ty::RawPtr(_, hir::Mutability::Mut) => { + return Err((span, "dereferencing raw mut pointer in const fn is unstable".into())); + }, + ty::RawPtr(_, hir::Mutability::Not) if !msrv.meets(msrvs::CONST_RAW_PTR_DEREF) => { + return Err((span, "dereferencing raw const pointer in const fn is unstable".into())); + }, + _ => (), + }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } - | ProjectionElem::Deref | ProjectionElem::Subtype(_) | ProjectionElem::Index(_) => {}, } @@ -304,7 +316,7 @@ fn check_terminator<'tcx>( } Ok(()) }, - TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body), + TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body, msrv), TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } => { Err((span, "const fn coroutines are unstable".into())) }, @@ -341,10 +353,10 @@ fn check_terminator<'tcx>( )); } - check_operand(tcx, func, span, body)?; + check_operand(tcx, func, span, body, msrv)?; for arg in args { - check_operand(tcx, &arg.node, span, body)?; + check_operand(tcx, &arg.node, span, body, msrv)?; } Ok(()) } else { @@ -357,7 +369,7 @@ fn check_terminator<'tcx>( msg: _, target: _, unwind: _, - } => check_operand(tcx, cond, span, body), + } => check_operand(tcx, cond, span, body, msrv), TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index d026e009684a4..2750e0cdf3f70 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -161,7 +161,23 @@ union U { f: u32, } -// Do not lint because accessing union fields from const functions is unstable +// Do not lint because accessing union fields from const functions is unstable in 1.55 +#[clippy::msrv = "1.55"] fn h(u: U) -> u32 { unsafe { u.f } } + +mod msrv { + struct Foo(*const u8, *mut u8); + + impl Foo { + #[clippy::msrv = "1.57"] + fn deref_ptr_cannot_be_const(self) -> usize { + unsafe { *self.0 as usize } + } + #[clippy::msrv = "1.58"] + fn deref_mut_ptr_cannot_be_const(self) -> usize { + unsafe { *self.1 as usize } + } + } +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 12a8320c8f329..06dbbeb31c0d5 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -113,3 +113,31 @@ impl const Drop for D { // Lint this, since it can be dropped in const contexts // FIXME(effects) fn d(this: D) {} + +mod msrv { + struct Foo(*const u8, &'static u8); + + impl Foo { + #[clippy::msrv = "1.58"] + fn deref_ptr_can_be_const(self) -> usize { + //~^ ERROR: this could be a `const fn` + unsafe { *self.0 as usize } + } + + fn deref_copied_val(self) -> usize { + //~^ ERROR: this could be a `const fn` + *self.1 as usize + } + } + + union Bar { + val: u8, + } + + #[clippy::msrv = "1.56"] + fn union_access_can_be_const() { + //~^ ERROR: this could be a `const fn` + let bar = Bar { val: 1 }; + let _ = unsafe { bar.val }; + } +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 082459fd82120..b2cade3056373 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -102,5 +102,33 @@ LL | | 46 LL | | } | |_^ -error: aborting due to 11 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:122:9 + | +LL | / fn deref_ptr_can_be_const(self) -> usize { +LL | | +LL | | unsafe { *self.0 as usize } +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:127:9 + | +LL | / fn deref_copied_val(self) -> usize { +LL | | +LL | | *self.1 as usize +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:138:5 + | +LL | / fn union_access_can_be_const() { +LL | | +LL | | let bar = Bar { val: 1 }; +LL | | let _ = unsafe { bar.val }; +LL | | } + | |_____^ + +error: aborting due to 14 previous errors From c342a61564ee2632a2896cc4a3b7bbecb5720bea Mon Sep 17 00:00:00 2001 From: Daniel Sedlak Date: Wed, 15 May 2024 10:59:35 +0000 Subject: [PATCH 55/58] Add configuration option for ignoring `panic!()` in tests --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 10 ++++ clippy_config/src/conf.rs | 4 ++ clippy_lints/src/lib.rs | 3 +- clippy_lints/src/panic_unimplemented.rs | 14 +++-- tests/ui-toml/panic/clippy.toml | 1 + tests/ui-toml/panic/panic.rs | 54 +++++++++++++++++++ tests/ui-toml/panic/panic.stderr | 11 ++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 ++ 9 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 tests/ui-toml/panic/clippy.toml create mode 100644 tests/ui-toml/panic/panic.rs create mode 100644 tests/ui-toml/panic/panic.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index cfff0bb50a6ab..e17d6b3cf2740 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5942,6 +5942,7 @@ Released 2018-09-13 [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests [`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings +[`allow-panic-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-panic-in-tests [`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests [`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception [`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index b049a6106f560..c8223007df7b3 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -101,6 +101,16 @@ Whether to allow `r#""#` when `r""` can be used * [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes) +## `allow-panic-in-tests` +Whether `panic` should be allowed in test functions or `#[cfg(test)]` + +**Default Value:** `false` + +--- +**Affected lints:** +* [`panic`](https://rust-lang.github.io/rust-clippy/master/index.html#panic) + + ## `allow-print-in-tests` Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index a4b29afa77798..cfdf620b7d074 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -457,6 +457,10 @@ define_Conf! { /// /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` (allow_unwrap_in_tests: bool = false), + /// Lint: PANIC. + /// + /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` + (allow_panic_in_tests: bool = false), /// Lint: DBG_MACRO. /// /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c3ae36a559739..553952f211f8b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -532,6 +532,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_expect_in_tests, allow_mixed_uninlined_format_args, allow_one_hash_in_raw_strings, + allow_panic_in_tests, allow_print_in_tests, allow_private_module_inception, allow_unwrap_in_tests, @@ -769,7 +770,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_in_test: allow_useless_vec_in_tests, }) }); - store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented)); + store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented { allow_panic_in_tests })); store.register_late_pass(|_| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|_| Box::new(derive::Derive)); store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv()))); diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index ef51a9a9a1c8a..75066c1f0d2e2 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,8 +1,14 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_in_test; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; + +#[derive(Clone)] +pub struct PanicUnimplemented { + pub allow_panic_in_tests: bool, +} declare_clippy_lint! { /// ### What it does @@ -77,7 +83,7 @@ declare_clippy_lint! { "usage of the `unreachable!` macro" } -declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); +impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -85,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { return; }; if is_panic(cx, macro_call.def_id) { - if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) + || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) + { return; } diff --git a/tests/ui-toml/panic/clippy.toml b/tests/ui-toml/panic/clippy.toml new file mode 100644 index 0000000000000..5d6230d092c07 --- /dev/null +++ b/tests/ui-toml/panic/clippy.toml @@ -0,0 +1 @@ +allow-panic-in-tests = true diff --git a/tests/ui-toml/panic/panic.rs b/tests/ui-toml/panic/panic.rs new file mode 100644 index 0000000000000..618a37ddfc555 --- /dev/null +++ b/tests/ui-toml/panic/panic.rs @@ -0,0 +1,54 @@ +//@compile-flags: --test +#![warn(clippy::panic)] + +fn main() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } +} + +#[test] +fn lonely_test() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } +} + +#[cfg(test)] +mod tests { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } + + bar(); + } + + fn bar() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } + } +} diff --git a/tests/ui-toml/panic/panic.stderr b/tests/ui-toml/panic/panic.stderr new file mode 100644 index 0000000000000..bf7503e086c9d --- /dev/null +++ b/tests/ui-toml/panic/panic.stderr @@ -0,0 +1,11 @@ +error: `panic` should not be present in production code + --> tests/ui-toml/panic/panic.rs:11:14 + | +LL | _ => panic!(""), + | ^^^^^^^^^^ + | + = note: `-D clippy::panic` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::panic)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index e42046ddef9dd..5cf9c0fb27102 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -8,6 +8,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-expect-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings + allow-panic-in-tests allow-print-in-tests allow-private-module-inception allow-renamed-params-for @@ -91,6 +92,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-expect-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings + allow-panic-in-tests allow-print-in-tests allow-private-module-inception allow-renamed-params-for @@ -174,6 +176,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allow-expect-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings + allow-panic-in-tests allow-print-in-tests allow-private-module-inception allow-renamed-params-for From 821dc871af2dfdab3f229bda2aa1ead25d6a0688 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 16 May 2024 21:11:58 +0100 Subject: [PATCH 56/58] Manually set library paths in .github/driver.sh --- .github/driver.sh | 19 +++++++++++-------- .github/workflows/clippy.yml | 6 +++--- .github/workflows/clippy_bors.yml | 4 +--- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/driver.sh b/.github/driver.sh index 2eafdd0fbc87b..09202b1878b23 100755 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -2,15 +2,18 @@ set -ex +sysroot="$(rustc --print sysroot)" +case $OS in + Linux) export LD_LIBRARY_PATH="$sysroot/lib" ;; + macOS) export DYLD_FALLBACK_LIBRARY_PATH="$sysroot/lib" ;; + Windows) export PATH="$(cygpath "$sysroot")/bin:$PATH" ;; + *) exit 1 +esac + # Check sysroot handling -sysroot=$(./target/debug/clippy-driver --print sysroot) -test "$sysroot" = "$(rustc --print sysroot)" - -if [[ ${OS} == "Windows" ]]; then - desired_sysroot=C:/tmp -else - desired_sysroot=/tmp -fi +test "$(./target/debug/clippy-driver --print sysroot)" = "$sysroot" + +desired_sysroot="target/sysroot" # Set --sysroot in command line sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot) test "$sysroot" = $desired_sysroot diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 8179e3e65b541..06bf3b6fdbfab 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -69,6 +69,6 @@ jobs: working-directory: clippy_dev - name: Test clippy-driver - run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') - rustup run $TOOLCHAIN bash .github/driver.sh + run: .github/driver.sh + env: + OS: ${{ runner.os }} diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 94515987eba4a..1f4bec9291823 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -116,9 +116,7 @@ jobs: working-directory: clippy_dev - name: Test clippy-driver - run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') - rustup run $TOOLCHAIN bash .github/driver.sh + run: .github/driver.sh env: OS: ${{ runner.os }} From 9f6280b1e7085bcf3b2b26c4a90209e8a1b923e5 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 16 May 2024 14:21:11 -0700 Subject: [PATCH 57/58] Bump nightly version -> 2024-05-16 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 055f305eb8e18..a0585ffdb45b0 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-05-02" +channel = "nightly-2024-05-16" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From b5cf8b8277fc39bf398f620c8f6d50b56821155c Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 18 May 2024 14:34:52 -0700 Subject: [PATCH 58/58] doc_lazy_continuation: do not warn on End events This avoids event spans that would otherwise cause crashes, since an End's span covers the range of the tag (which will be earlier than the line break within the tag). --- clippy_lints/src/doc/mod.rs | 3 ++- tests/ui/doc/doc_lazy_list.fixed | 35 +++++++++++++++++++++++++++++++ tests/ui/doc/doc_lazy_list.rs | 35 +++++++++++++++++++++++++++++++ tests/ui/doc/doc_lazy_list.stderr | 26 ++++++++++++++++++++++- 4 files changed, 97 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 9ee4aad337d21..81c47d07c3563 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -750,10 +750,11 @@ fn check_doc<'a, Events: Iterator, Range (), // We don't care about other tags SoftBreak | HardBreak => { if !containers.is_empty() - && let Some((_next_event, next_range)) = events.peek() + && let Some((next_event, next_range)) = events.peek() && let Some(next_span) = fragments.span(cx, next_range.clone()) && let Some(span) = fragments.span(cx, range.clone()) && !in_footnote_definition + && !matches!(next_event, End(_)) { lazy_continuation::check( cx, diff --git a/tests/ui/doc/doc_lazy_list.fixed b/tests/ui/doc/doc_lazy_list.fixed index e19371ea9aa22..409e6b0bc227f 100644 --- a/tests/ui/doc/doc_lazy_list.fixed +++ b/tests/ui/doc/doc_lazy_list.fixed @@ -40,3 +40,38 @@ fn six() {} /// /// this is not a lazy continuation fn seven() {} + +#[rustfmt::skip] +// https://github.com/rust-lang/rust-clippy/pull/12770#issuecomment-2118601768 +/// Returns a list of ProtocolDescriptors from a Serde JSON input. +/// +/// Defined Protocol Identifiers for the Protocol Descriptor +/// We intentionally omit deprecated profile identifiers. +/// From Bluetooth Assigned Numbers: +/// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery +/// +/// # Arguments +/// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors +/// to set up. Example: +/// 'protocol_descriptors': [ +//~^ ERROR: doc list item missing indentation +/// { +/// 'protocol': 25, # u64 Representation of ProtocolIdentifier::AVDTP +/// 'params': [ +/// { +/// 'data': 0x0103 # to indicate 1.3 +/// }, +/// { +/// 'data': 0x0105 # to indicate 1.5 +/// } +/// ] +/// }, +/// { +/// 'protocol': 1, # u64 Representation of ProtocolIdentifier::SDP +/// 'params': [{ +/// 'data': 0x0019 +/// }] +/// } +/// ] +//~^ ERROR: doc list item missing indentation +fn eight() {} diff --git a/tests/ui/doc/doc_lazy_list.rs b/tests/ui/doc/doc_lazy_list.rs index fdfe9afaf1d73..30ab448a1130c 100644 --- a/tests/ui/doc/doc_lazy_list.rs +++ b/tests/ui/doc/doc_lazy_list.rs @@ -40,3 +40,38 @@ fn six() {} /// /// this is not a lazy continuation fn seven() {} + +#[rustfmt::skip] +// https://github.com/rust-lang/rust-clippy/pull/12770#issuecomment-2118601768 +/// Returns a list of ProtocolDescriptors from a Serde JSON input. +/// +/// Defined Protocol Identifiers for the Protocol Descriptor +/// We intentionally omit deprecated profile identifiers. +/// From Bluetooth Assigned Numbers: +/// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery +/// +/// # Arguments +/// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors +/// to set up. Example: +/// 'protocol_descriptors': [ +//~^ ERROR: doc list item missing indentation +/// { +/// 'protocol': 25, # u64 Representation of ProtocolIdentifier::AVDTP +/// 'params': [ +/// { +/// 'data': 0x0103 # to indicate 1.3 +/// }, +/// { +/// 'data': 0x0105 # to indicate 1.5 +/// } +/// ] +/// }, +/// { +/// 'protocol': 1, # u64 Representation of ProtocolIdentifier::SDP +/// 'params': [{ +/// 'data': 0x0019 +/// }] +/// } +/// ] +//~^ ERROR: doc list item missing indentation +fn eight() {} diff --git a/tests/ui/doc/doc_lazy_list.stderr b/tests/ui/doc/doc_lazy_list.stderr index d0f6279f62d73..ddfdc49340c45 100644 --- a/tests/ui/doc/doc_lazy_list.stderr +++ b/tests/ui/doc/doc_lazy_list.stderr @@ -108,5 +108,29 @@ help: indent this line LL | /// and so should this | ++ -error: aborting due to 9 previous errors +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:56:5 + | +LL | /// 'protocol_descriptors': [ + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// 'protocol_descriptors': [ + | + + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:75:5 + | +LL | /// ] + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// ] + | + + +error: aborting due to 11 previous errors