diff --git a/CHANGELOG.md b/CHANGELOG.md index bc60b1c57f7d..7c0a7998c034 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6271,6 +6271,7 @@ Released 2018-09-13 [`needless_character_iteration`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_character_iteration [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue +[`needless_conversion_for_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_conversion_for_trait [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main [`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each @@ -6758,4 +6759,5 @@ Released 2018-09-13 [`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 +[`watched-functions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#watched-functions diff --git a/Cargo.toml b/Cargo.toml index 9e250774631f..2cea628fe5fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ ui_test = "0.30.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" +tempfile = "3.20" toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 05590ff7b1c9..9f61b14574aa 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -1135,3 +1135,13 @@ Whether to also emit warnings for unsafe blocks with metavariable expansions in --- **Affected lints:** * [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe) + + +## `watched-functions` +Non-standard functions `needless_conversion_for_trait` should warn about. + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`needless_conversion_for_trait`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_conversion_for_trait) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2ad3f2efcdd7..f3a2dcc96bef 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,7 +1,7 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, - Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, + ConfPath, ConfPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, + SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; @@ -190,17 +190,16 @@ macro_rules! deserialize { (value, value_span) }}; - ($map:expr, $ty:ty, $errors:expr, $file:expr, $replacements_allowed:expr) => {{ + ($map:expr, $ty:ty, $errors:expr, $file:expr, $replaceable:expr) => {{ let array = $map.next_value::>>()?; - let mut disallowed_paths_span = Range { + let mut conf_paths_span = Range { start: usize::MAX, end: usize::MIN, }; - let mut disallowed_paths = Vec::new(); + let mut conf_paths = Vec::new(); for raw_value in array { let value_span = raw_value.span(); - let mut disallowed_path = match DisallowedPath::<$replacements_allowed>::deserialize(raw_value.into_inner()) - { + let mut conf_path = match ConfPath::::deserialize(raw_value.into_inner()) { Err(e) => { $errors.push(ConfError::spanned( $file, @@ -210,13 +209,13 @@ macro_rules! deserialize { )); continue; }, - Ok(disallowed_path) => disallowed_path, + Ok(conf_path) => conf_path, }; - disallowed_paths_span = union(&disallowed_paths_span, &value_span); - disallowed_path.set_span(span_from_toml_range($file, value_span)); - disallowed_paths.push(disallowed_path); + conf_paths_span = union(&conf_paths_span, &value_span); + conf_path.set_span(span_from_toml_range($file, value_span)); + conf_paths.push(conf_path); } - (disallowed_paths, disallowed_paths_span) + (conf_paths, conf_paths_span) }}; } @@ -225,7 +224,7 @@ macro_rules! define_Conf { $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? - $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? + $(#[conf_paths_allow_replacements = $replaceable:expr])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -283,7 +282,7 @@ macro_rules! define_Conf { // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? let (value, value_span) = - deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); + deserialize!(map, $ty, errors, self.0 $(, $replaceable)?); // Was this field set previously? if $name.is_some() { errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); @@ -529,9 +528,9 @@ define_Conf! { )] avoid_breaking_exported_api: bool = true, /// The list of types which may not be held across an await point. - #[disallowed_paths_allow_replacements = false] + #[conf_paths_allow_replacements = false] #[lints(await_holding_invalid_type)] - await_holding_invalid_types: Vec = Vec::new(), + await_holding_invalid_types: Vec = Vec::new(), /// DEPRECATED LINT: BLACKLISTED_NAME. /// /// Use the Disallowed Names lint instead @@ -582,9 +581,9 @@ define_Conf! { /// - `replacement` (optional): suggested alternative macro /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry /// if the path doesn't exist, instead of emitting an error - #[disallowed_paths_allow_replacements = true] + #[conf_paths_allow_replacements = true] #[lints(disallowed_macros)] - disallowed_macros: Vec = Vec::new(), + disallowed_macros: Vec = Vec::new(), /// The list of disallowed methods, written as fully qualified paths. /// /// **Fields:** @@ -593,9 +592,9 @@ define_Conf! { /// - `replacement` (optional): suggested alternative method /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry /// if the path doesn't exist, instead of emitting an error - #[disallowed_paths_allow_replacements = true] + #[conf_paths_allow_replacements = true] #[lints(disallowed_methods)] - disallowed_methods: Vec = Vec::new(), + disallowed_methods: Vec = Vec::new(), /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value /// `".."` 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. @@ -609,9 +608,9 @@ define_Conf! { /// - `replacement` (optional): suggested alternative type /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry /// if the path doesn't exist, instead of emitting an error - #[disallowed_paths_allow_replacements = true] + #[conf_paths_allow_replacements = true] #[lints(disallowed_types)] - disallowed_types: Vec = Vec::new(), + disallowed_types: Vec = Vec::new(), /// The list of words this lint should not consider as identifiers needing ticks. The value /// `".."` 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. For example: @@ -878,6 +877,9 @@ define_Conf! { /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. #[lints(macro_metavars_in_unsafe)] warn_unsafe_macro_metavars_in_private_macros: bool = false, + /// Non-standard functions `needless_conversion_for_trait` should warn about. + #[lints(needless_conversion_for_trait)] + watched_functions: Vec = Vec::new(), } /// Search for the configuration file. diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index f64eefa0c232..733e2097b359 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -1,15 +1,17 @@ use clippy_utils::paths::{PathNS, find_crates, lookup_path}; +use itertools::Itertools; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag}; use rustc_hir::PrimTy; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty::TyCtxt; -use rustc_span::{Span, Symbol}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use serde::de::{self, Deserializer, Visitor}; use serde::{Deserialize, Serialize, ser}; use std::collections::HashMap; use std::fmt; +use std::ops::Deref; #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] @@ -18,11 +20,11 @@ pub struct Rename { pub rename: String, } -pub type DisallowedPathWithoutReplacement = DisallowedPath; +pub type ConfPathWithoutReplacement = ConfPath; #[derive(Debug, Serialize)] -pub struct DisallowedPath { - path: String, +pub struct ConfPath { + path: T, reason: Option, replacement: Option, /// Setting `allow_invalid` to true suppresses a warning if `path` does not refer to an existing @@ -31,20 +33,20 @@ pub struct DisallowedPath { /// This could be useful when conditional compilation is used, or when a clippy.toml file is /// shared among multiple projects. allow_invalid: bool, - /// The span of the `DisallowedPath`. + /// The span of the `ConfPath`. /// /// Used for diagnostics. #[serde(skip_serializing)] span: Span, } -impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath { +impl<'de, const REPLACEABLE: bool> Deserialize<'de> for ConfPath { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - let enum_ = DisallowedPathEnum::deserialize(deserializer)?; - if !REPLACEMENT_ALLOWED && enum_.replacement().is_some() { + let enum_ = ConfPathEnum::deserialize(deserializer)?; + if !REPLACEABLE && enum_.replacement().is_some() { return Err(de::Error::custom("replacement not allowed for this configuration")); } Ok(Self { @@ -57,11 +59,11 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath DisallowedPath { - pub fn path(&self) -> &str { +impl ConfPath { + pub fn path(&self) -> &T { &self.path } @@ -101,7 +103,17 @@ impl DisallowedPath { } } -impl DisallowedPathEnum { +impl ConfPath +where + T: Deref, + ::Target: ToSymPath, +{ + pub fn to_sym_path(&self) -> Vec { + self.path().to_sym_path() + } +} + +impl ConfPathEnum { pub fn path(&self) -> &str { let (Self::Simple(path) | Self::WithReason { path, .. }) = self; @@ -130,25 +142,71 @@ impl DisallowedPathEnum { } } +pub trait ToSymPath { + fn to_sym_path(&self) -> Vec; +} + +impl ToSymPath for str { + fn to_sym_path(&self) -> Vec { + self.split("::").map(Symbol::intern).collect() + } +} + +#[derive(Clone, Copy, Debug)] +pub struct SymPath<'a>(pub &'a [Symbol]); + +impl Deref for SymPath<'_> { + type Target = [Symbol]; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl fmt::Display for SymPath<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0.iter().map(Symbol::to_string).join("::")) + } +} + +impl ToSymPath for [Symbol] { + fn to_sym_path(&self) -> Vec { + self.to_owned() + } +} + +pub const fn conf_path_from_sym_path(sym_path: &'static [Symbol]) -> ConfPath, false> { + ConfPath { + path: SymPath(sym_path), + reason: None, + replacement: None, + allow_invalid: false, + span: DUMMY_SP, + } +} + /// Creates a map of disallowed items to the reason they were disallowed. #[allow(clippy::type_complexity)] -pub fn create_disallowed_map( +pub fn create_conf_path_map( tcx: TyCtxt<'_>, - disallowed_paths: &'static [DisallowedPath], + conf_paths: &'static [ConfPath], ns: PathNS, def_kind_predicate: impl Fn(DefKind) -> bool, predicate_description: &str, allow_prim_tys: bool, ) -> ( - DefIdMap<(&'static str, &'static DisallowedPath)>, - FxHashMap)>, -) { - let mut def_ids: DefIdMap<(&'static str, &'static DisallowedPath)> = DefIdMap::default(); - let mut prim_tys: FxHashMap)> = + DefIdMap<(&'static ::Target, &'static ConfPath)>, + FxHashMap::Target, &'static ConfPath)>, +) +where + T: Deref + fmt::Display, + ::Target: ToSymPath, +{ + let mut def_ids: DefIdMap<(&'static ::Target, &ConfPath)> = DefIdMap::default(); + let mut prim_tys: FxHashMap::Target, &ConfPath)> = FxHashMap::default(); - for disallowed_path in disallowed_paths { - let path = disallowed_path.path(); - let sym_path: Vec = path.split("::").map(Symbol::intern).collect(); + for conf_path in conf_paths { + let path = conf_path.path(); + let sym_path = path.to_sym_path(); let mut resolutions = lookup_path(tcx, ns, &sym_path); resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id))); @@ -162,7 +220,7 @@ pub fn create_disallowed_map( if resolutions.is_empty() && prim_ty.is_none() - && !disallowed_path.allow_invalid + && !conf_path.allow_invalid // Don't warn about unloaded crates: // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221 && (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty()) @@ -179,16 +237,16 @@ pub fn create_disallowed_map( }; tcx.sess .dcx() - .struct_span_warn(disallowed_path.span(), message) + .struct_span_warn(conf_path.span(), message) .with_help("add `allow-invalid = true` to the entry to suppress this warning") .emit(); } for def_id in resolutions { - def_ids.insert(def_id, (path, disallowed_path)); + def_ids.insert(def_id, (path, conf_path)); } if let Some(ty) = prim_ty { - prim_tys.insert(ty, (path, disallowed_path)); + prim_tys.insert(ty, (path, conf_path)); } } diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 4121daa85e6d..ceed4f7d0f8e 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -95,7 +95,7 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { } else { let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); - write_file(&lint_path, lint_contents.as_bytes())?; + write_file(&lint_path, &lint_contents)?; println!("Generated lint file: `{lint_path}`"); Ok(()) @@ -433,7 +433,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R ); } - write_file(lint_file_path.as_path(), lint_file_contents)?; + write_file(&lint_file_path, lint_file_contents)?; println!("Generated lint file: `clippy_lints/src/{ty}/{}.rs`", lint.name); println!( "Be sure to add a call to `{}::check` in `clippy_lints/src/{ty}/mod.rs`!", diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 89962a110341..76383c4a0efa 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -702,7 +702,7 @@ pub fn split_args_for_threads( let mut cmd = (self.make_cmd)(); let mut cmd_len = 0usize; for arg in self.args.by_ref().take(self.batch_size) { - cmd.arg(arg.as_ref()); + cmd.arg(&arg); // `+ 8` to account for the `argv` pointer on unix. // Windows is complicated since the arguments are first converted to UTF-16ish, // but this needs to account for the space between arguments and whatever additional diff --git a/clippy_lints/src/almost_complete_range.rs b/clippy_lints/src/almost_complete_range.rs index 4f55968d5625..640443117943 100644 --- a/clippy_lints/src/almost_complete_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -60,7 +60,7 @@ impl EarlyLintPass for AlmostCompleteRange { diag.span_suggestion( trim_span(cx.sess().source_map(), start.between(end)), "use an inclusive range", - "..=".to_owned(), + "..=", Applicability::MaybeIncorrect, ); } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 31cc004f6855..ad31b4042dac 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; +use clippy_config::types::{ConfPathWithoutReplacement, create_conf_path_map}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::{self, PathNS}; use rustc_hir as hir; @@ -174,12 +174,12 @@ declare_clippy_lint! { impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); pub struct AwaitHolding { - def_ids: DefIdMap<(&'static str, &'static DisallowedPathWithoutReplacement)>, + def_ids: DefIdMap<(&'static str, &'static ConfPathWithoutReplacement)>, } impl AwaitHolding { pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (def_ids, _) = create_disallowed_map( + let (def_ids, _) = create_conf_path_map( tcx, &conf.await_holding_invalid_types, PathNS::Type, @@ -252,8 +252,8 @@ impl AwaitHolding { ); }, ); - } else if let Some(&(path, disallowed_path)) = self.def_ids.get(&adt.did()) { - emit_invalid_type(cx, ty_cause.source_info.span, path, disallowed_path); + } else if let Some(&(path, conf_path)) = self.def_ids.get(&adt.did()) { + emit_invalid_type(cx, ty_cause.source_info.span, path, conf_path); } } } @@ -264,14 +264,14 @@ fn emit_invalid_type( cx: &LateContext<'_>, span: Span, path: &'static str, - disallowed_path: &'static DisallowedPathWithoutReplacement, + conf_path: &'static ConfPathWithoutReplacement, ) { span_lint_and_then( cx, AWAIT_HOLDING_INVALID_TYPE, span, format!("holding a disallowed type across an await point `{path}`"), - disallowed_path.diag_amendment(span), + conf_path.diag_amendment(span), ); } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index e1cb08e361ca..80737c5d6ec5 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -544,6 +544,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, crate::needless_borrows_for_generic_args::NEEDLESS_BORROWS_FOR_GENERIC_ARGS_INFO, crate::needless_continue::NEEDLESS_CONTINUE_INFO, + crate::needless_conversion_for_trait::NEEDLESS_CONVERSION_FOR_TRAIT_INFO, crate::needless_else::NEEDLESS_ELSE_INFO, crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, crate::needless_if::NEEDLESS_IF_INFO, diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 044903ce5753..ef0935bc58e0 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_config::types::{ConfPath, create_conf_path_map}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; use clippy_utils::paths::PathNS; @@ -63,7 +63,7 @@ declare_clippy_lint! { } pub struct DisallowedMacros { - disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, + disallowed: DefIdMap<(&'static str, &'static ConfPath)>, seen: FxHashSet, // Track the most recently seen node that can have a `derive` attribute. // Needed to use the correct lint level. @@ -76,7 +76,7 @@ pub struct DisallowedMacros { impl DisallowedMacros { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, early_macro_cache: AttrStorage) -> Self { - let (disallowed, _) = create_disallowed_map( + let (disallowed, _) = create_conf_path_map( tcx, &conf.disallowed_macros, PathNS::Macro, @@ -102,9 +102,9 @@ impl DisallowedMacros { return; } - if let Some(&(path, disallowed_path)) = self.disallowed.get(&mac.def_id) { + if let Some(&(path, conf_path)) = self.disallowed.get(&mac.def_id) { let msg = format!("use of a disallowed macro `{path}`"); - let add_note = disallowed_path.diag_amendment(mac.span); + let add_note = conf_path.diag_amendment(mac.span); if matches!(mac.kind, MacroKind::Derive) && let Some(derive_src) = derive_src { diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 8c067432cb4e..2af78b4f984f 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_config::types::{ConfPath, create_conf_path_map}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -62,12 +62,12 @@ declare_clippy_lint! { } pub struct DisallowedMethods { - disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, + disallowed: DefIdMap<(&'static str, &'static ConfPath)>, } impl DisallowedMethods { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (disallowed, _) = create_disallowed_map( + let (disallowed, _) = create_conf_path_map( tcx, &conf.disallowed_methods, PathNS::Value, @@ -95,13 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { + if let Some(&(path, conf_path)) = self.disallowed.get(&id) { span_lint_and_then( cx, DISALLOWED_METHODS, span, format!("use of a disallowed method `{path}`"), - disallowed_path.diag_amendment(span), + conf_path.diag_amendment(span), ); } } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 9a82327a0d7b..c1e8780759fe 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_config::types::{ConfPath, create_conf_path_map}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashMap; @@ -58,13 +58,13 @@ declare_clippy_lint! { } pub struct DisallowedTypes { - def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>, - prim_tys: FxHashMap, + def_ids: DefIdMap<(&'static str, &'static ConfPath)>, + prim_tys: FxHashMap, } impl DisallowedTypes { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (def_ids, prim_tys) = create_disallowed_map( + let (def_ids, prim_tys) = create_conf_path_map( tcx, &conf.disallowed_types, PathNS::Type, @@ -76,7 +76,7 @@ impl DisallowedTypes { } fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { - let (path, disallowed_path) = match res { + let (path, conf_path) = match res { Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, _ => return, @@ -86,7 +86,7 @@ impl DisallowedTypes { DISALLOWED_TYPES, span, format!("use of a disallowed type `{path}`"), - disallowed_path.diag_amendment(span), + conf_path.diag_amendment(span), ); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 844bc1b0e390..f9332f6dd1a9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -42,7 +42,6 @@ extern crate rustc_hir; extern crate rustc_hir_analysis; extern crate rustc_hir_pretty; extern crate rustc_hir_typeck; -extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; @@ -260,6 +259,7 @@ mod needless_bool; mod needless_borrowed_ref; mod needless_borrows_for_generic_args; mod needless_continue; +mod needless_conversion_for_trait; mod needless_else; mod needless_for_each; mod needless_if; @@ -829,5 +829,10 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); + store.register_late_pass(move |tcx| { + Box::new(needless_conversion_for_trait::NeedlessConversionForTrait::new( + tcx, conf, + )) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index bcf079b70070..b7599dc12056 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -395,13 +395,9 @@ impl<'a> PatState<'a> { PatKind::Tuple(pats, _) => self.add_product_pat(cx, pats), PatKind::Slice(head, _, tail) => self.add_product_pat(cx, head.iter().chain(tail)), - PatKind::TupleStruct(ref path, pats, _) => self.add_struct_pats( - cx, - pat, - path, - if let [pat] = pats { Some(pat) } else { None }, - pats.iter(), - ), + PatKind::TupleStruct(ref path, pats, _) => { + self.add_struct_pats(cx, pat, path, if let [pat] = pats { Some(pat) } else { None }, pats) + }, PatKind::Struct(ref path, pats, _) => self.add_struct_pats( cx, pat, diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index 37a8e25bef96..b172c246cd90 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -189,7 +189,7 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument, S diag.span_suggestion( create_span.shrink_to_hi(), "add", - ".truncate(true)".to_string(), + ".truncate(true)", rustc_errors::Applicability::MaybeIncorrect, ) .help("if you intend to overwrite an existing file entirely, call `.truncate(true)`") diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index c1f4904af7c4..28205e1a87f3 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,7 +3,10 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{ + get_callee_generic_args_and_args, get_iterator_item_ty, implements_trait, is_copy, is_to_string_on_string_like, + is_type_diagnostic_item, is_type_lang_item, +}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, @@ -18,7 +21,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ - self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, + self, ClauseKind, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; use rustc_span::Symbol; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -398,6 +401,11 @@ fn check_other_call_arg<'tcx>( && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) && (trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id) && let receiver_ty = cx.typeck_results().expr_ty(receiver) + // Verify the output type contains the input type to be replaced. + // `needless_conversion_for_trait` cannot change the output type (see + // `clippy_utils::ty::replace_types`). Hence, this check ensures that `unnecessary_to_owned` + // and `needless_conversion_for_trait` do not flag the same code. + && fn_sig.output().contains(input) // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match // `Target = T`. && let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { @@ -443,33 +451,6 @@ fn skip_addr_of_ancestors<'tcx>( None } -/// Checks whether an expression is a function or method call and, if so, returns its `DefId`, -/// `GenericArgs`, and arguments. -fn get_callee_generic_args_and_args<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, -) -> Option<( - DefId, - GenericArgsRef<'tcx>, - Option<&'tcx Expr<'tcx>>, - &'tcx [Expr<'tcx>], -)> { - if let ExprKind::Call(callee, args) = expr.kind - && let callee_ty = cx.typeck_results().expr_ty(callee) - && let ty::FnDef(callee_def_id, _) = callee_ty.kind() - { - let generic_args = cx.typeck_results().node_args(callee.hir_id); - return Some((*callee_def_id, generic_args, None, args)); - } - if let ExprKind::MethodCall(_, recv, args, _) = expr.kind - && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - { - let generic_args = cx.typeck_results().node_args(expr.hir_id); - return Some((method_def_id, generic_args, Some(recv), args)); - } - None -} - /// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type. fn get_input_traits_and_projections<'tcx>( cx: &LateContext<'tcx>, @@ -623,7 +604,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { is_cow_into_owned(cx, method_name, method_def_id) || (method_name != sym::to_string && is_clone_like(cx, method_name, method_def_id)) - || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) + || is_to_string_on_string_like(cx, call_expr, method_def_id) } /// Returns true if the named method is `Cow::into_owned`. @@ -631,32 +612,6 @@ fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: D method_name == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow) } -/// Returns true if the named method is `ToString::to_string` and it's called on a type that -/// is string-like i.e. implements `AsRef` or `Deref`. -fn is_to_string_on_string_like<'a>( - cx: &LateContext<'_>, - call_expr: &'a Expr<'a>, - method_name: Symbol, - method_def_id: DefId, -) -> bool { - if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) { - return false; - } - - if let Some(args) = cx.typeck_results().node_args_opt(call_expr.hir_id) - && let [generic_arg] = args.as_slice() - && let GenericArgKind::Type(ty) = generic_arg.kind() - && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) - && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) - && (cx.get_associated_type(ty, deref_trait_id, sym::Target) == Some(cx.tcx.types.str_) - || implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) - { - true - } else { - false - } -} - fn std_map_key<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { match ty.kind() { ty::Adt(adt, args) diff --git a/clippy_lints/src/methods/vec_resize_to_zero.rs b/clippy_lints/src/methods/vec_resize_to_zero.rs index bfb481f4fc09..25ebec1d6553 100644 --- a/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -39,7 +39,7 @@ pub(super) fn check<'tcx>( db.span_suggestion( method_call_span, "...or you can empty the vector with", - "clear()".to_string(), + "clear()", Applicability::MaybeIncorrect, ); }, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 09ee6f7037c6..f7adbd1fc74e 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -292,9 +292,9 @@ fn used_underscore_items<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { cx, USED_UNDERSCORE_ITEMS, expr.span, - "used underscore-prefixed item".to_string(), + "used underscore-prefixed item", |diag| { - diag.span_note(definition_span, "item is defined here".to_string()); + diag.span_note(definition_span, "item is defined here"); }, ); } @@ -337,9 +337,9 @@ fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { cx, USED_UNDERSCORE_BINDING, expr.span, - "used underscore-prefixed binding".to_string(), + "used underscore-prefixed binding", |diag| { - diag.span_note(definition_span, "binding is defined here".to_string()); + diag.span_note(definition_span, "binding is defined here"); }, ); } diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 61f4684c9e37..77e83c46d06e 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -16,7 +16,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) { diag.span_suggestion( lit_span, "if you mean to use a decimal constant, remove the `0` to avoid confusion", - trimmed_lit_snip.to_string(), + trimmed_lit_snip, Applicability::MaybeIncorrect, ); // do not advise to use octal form if the literal cannot be expressed in base 8. diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index 120a4b98a65a..a4f4155898a8 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -1,26 +1,18 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir, expr_local, local_assignments, used_exactly_once}; -use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{implements_trait, is_copy}; +use clippy_utils::ty::{build_check_predicates_with_new_ty_closure, implements_trait, is_copy}; use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_n_hir_expr_refs}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; -use rustc_index::bit_set::DenseBitSet; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_hir::{Body, Expr, ExprKind, Path, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; -use rustc_middle::ty::{ - self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty, -}; +use rustc_middle::ty::{self, ParamTy}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{Obligation, ObligationCause}; -use std::collections::VecDeque; declare_clippy_lint! { /// ### What it does @@ -161,7 +153,7 @@ fn path_has_args(p: &QPath<'_>) -> bool { /// - `Copy` itself, or /// - the only use of a mutable reference, or /// - not a variable (created by a function call) -#[expect(clippy::too_many_arguments, clippy::too_many_lines)] +#[expect(clippy::too_many_arguments)] fn needless_borrow_count<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, @@ -172,122 +164,33 @@ fn needless_borrow_count<'tcx>( mut expr: &Expr<'tcx>, msrv: Msrv, ) -> usize { - let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); - let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); - let meta_sized_trait_def_id = cx.tcx.lang_items().meta_sized_trait(); - let drop_trait_def_id = cx.tcx.lang_items().drop_trait(); - - let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder(); - let predicates = cx.tcx.param_env(fn_id).caller_bounds(); - let projection_predicates = predicates - .iter() - .filter_map(|predicate| { - if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() { - Some(projection_predicate) - } else { - None - } - }) - .collect::>(); - - let mut trait_with_ref_mut_self_method = false; - - // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return. - if predicates - .iter() - .filter_map(|predicate| { - if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) - { - Some(trait_predicate.trait_ref.def_id) - } else { - None - } - }) - .inspect(|trait_def_id| { - trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id); - }) - .all(|trait_def_id| { - Some(trait_def_id) == destruct_trait_def_id - || Some(trait_def_id) == sized_trait_def_id - || Some(trait_def_id) == meta_sized_trait_def_id - || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) - }) - { - return 0; - } - - // See: - // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 - // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 - if projection_predicates - .iter() - .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate)) - { + let Some(mut check_referent_ty) = + build_check_predicates_with_new_ty_closure(cx, fn_id, callee_args, arg_index, param_ty, true, msrv) + else { return 0; - } + }; - // `args_with_referent_ty` can be constructed outside of `check_referent` because the same - // elements are modified each time `check_referent` is called. - let mut args_with_referent_ty = callee_args.to_vec(); + let drop_trait_def_id = cx.tcx.lang_items().drop_trait(); - let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| { - if let ExprKind::Field(base, _) = &referent.kind - && let base_ty = cx.typeck_results().expr_ty(base) - && drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) - { - return false; + let mut count = 0; + while let ExprKind::AddrOf(_, _, referent) = expr.kind { + if let ExprKind::Field(base, _) = &referent.kind { + let base_ty = cx.typeck_results().expr_ty(base); + if drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) { + break; + } } let referent_ty = cx.typeck_results().expr_ty(referent); if !(is_copy(cx, referent_ty) - || referent_ty.is_ref() && referent_used_exactly_once(cx, possible_borrowers, reference) + || referent_ty.is_ref() && referent_used_exactly_once(cx, possible_borrowers, expr) || matches!(referent.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))) { - return false; - } - - // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 - if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) { - return false; - } - - if !replace_types( - cx, - param_ty, - referent_ty, - fn_sig, - arg_index, - &projection_predicates, - &mut args_with_referent_ty, - ) { - return false; + break; } - predicates.iter().all(|predicate| { - if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && cx - .tcx - .is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) - && let ty::Param(param_ty) = trait_predicate.self_ty().kind() - && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].kind() - && ty.is_array() - && !msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) - { - return false; - } - - let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty[..]); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - infcx.predicate_must_hold_modulo_regions(&obligation) - }) - }; - - let mut count = 0; - while let ExprKind::AddrOf(_, _, referent) = expr.kind { - if !check_reference_and_referent(expr, referent) { + if !check_referent_ty(referent_ty) { break; } expr = referent; @@ -296,56 +199,6 @@ fn needless_borrow_count<'tcx>( count } -fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { - cx.tcx - .associated_items(trait_def_id) - .in_definition_order() - .any(|assoc_item| { - if assoc_item.is_method() { - let self_ty = cx - .tcx - .fn_sig(assoc_item.def_id) - .instantiate_identity() - .skip_binder() - .inputs()[0]; - matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) - } else { - false - } - }) -} - -fn is_mixed_projection_predicate<'tcx>( - cx: &LateContext<'tcx>, - callee_def_id: DefId, - projection_predicate: &ProjectionPredicate<'tcx>, -) -> bool { - let generics = cx.tcx.generics_of(callee_def_id); - // The predicate requires the projected type to equal a type parameter from the parent context. - if let Some(term_ty) = projection_predicate.term.as_type() - && let ty::Param(term_param_ty) = term_ty.kind() - && (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_term = projection_predicate.projection_term; - loop { - match *projection_term.self_ty().kind() { - ty::Alias(ty::Projection, inner_projection_ty) => { - projection_term = inner_projection_ty.into(); - }, - ty::Param(param_ty) => { - return (param_ty.index as usize) >= generics.parent_count; - }, - _ => { - return false; - }, - } - } - } else { - false - } -} - fn referent_used_exactly_once<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, @@ -376,60 +229,3 @@ fn referent_used_exactly_once<'tcx>( 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`. -// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 -fn replace_types<'tcx>( - cx: &LateContext<'tcx>, - param_ty: ParamTy, - new_ty: Ty<'tcx>, - fn_sig: FnSig<'tcx>, - arg_index: usize, - projection_predicates: &[ProjectionPredicate<'tcx>], - args: &mut [GenericArg<'tcx>], -) -> bool { - let mut replaced = DenseBitSet::new_empty(args.len()); - - let mut deque = VecDeque::with_capacity(args.len()); - deque.push_back((param_ty, new_ty)); - - while let Some((param_ty, new_ty)) = deque.pop_front() { - // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in. - if !fn_sig - .inputs_and_output - .iter() - .enumerate() - .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx))) - { - return false; - } - - args[param_ty.index as usize] = GenericArg::from(new_ty); - - // 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_term.self_ty() == param_ty.to_ty(cx.tcx) - && let Some(term_ty) = projection_predicate.term.as_type() - && let ty::Param(term_param_ty) = term_ty.kind() - { - let projection = projection_predicate - .projection_term - .with_replaced_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.typing_env(), projection) - && args[term_param_ty.index as usize] != GenericArg::from(projected_ty) - { - deque.push_back((*term_param_ty, projected_ty)); - } - } - } - } - } - - true -} diff --git a/clippy_lints/src/needless_conversion_for_trait/check_inherent_functions.rs b/clippy_lints/src/needless_conversion_for_trait/check_inherent_functions.rs new file mode 100644 index 000000000000..cb77ba25ac62 --- /dev/null +++ b/clippy_lints/src/needless_conversion_for_trait/check_inherent_functions.rs @@ -0,0 +1,261 @@ +//! Tries to determine whether there are "of interest" methods that are not mentioned in +//! [`super::WATCHED_INHERENT_FUNCTIONS`] or [`super::IGNORED_INHERENT_FUNCTIONS`]. +//! +//! For example, [rust-lang/rust#115443] added [`std::ffi::OsStr::as_encoded_bytes`]. Once this +//! lint's toolchain was upgraded to one that included that PR, this test failed. The function was +//! then added to [`super::WATCHED_INHERENT_FUNCTIONS`] and the test passed again. +//! +//! When Clippy is built in debug mode, this test is run after each crate has been linted. See the +//! implementation of `check_crate_post` for [`super::NeedlessConversionForTrait`]. +//! +//! [rust-lang/rust#115443]: https://github.com/rust-lang/rust/pull/115443 + +use super::{IGNORED_INHERENT_FUNCTIONS, NeedlessConversionForTrait, WATCHED_INHERENT_FUNCTIONS}; +use clippy_config::types::{ConfPath, ToSymPath, create_conf_path_map}; +use clippy_utils::paths::{PathNS, lookup_path}; +use clippy_utils::{is_no_core_crate, is_no_std_crate, sym}; +use rustc_hir::Safety; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::{self, Ty, TypeFolder}; +use rustc_span::Symbol; +use std::ops::Deref; + +#[expect(clippy::too_many_lines)] +pub fn check_inherent_functions(cx: &LateContext<'_>, lint: &NeedlessConversionForTrait) { + if is_no_core_crate(cx) || is_no_std_crate(cx) { + return; + } + + let into_iterator_def_id = cx.tcx.get_diagnostic_item(sym::IntoIterator).unwrap(); + let iterator_def_id = cx.tcx.get_diagnostic_item(sym::Iterator).unwrap(); + + let watched_type_paths = type_paths(WATCHED_INHERENT_FUNCTIONS); + + // Create a `ConfPath` map of the ignored inherent functions to make them easier to check. + let (ignored_inherent_functions, _) = create_conf_path_map( + cx.tcx, + IGNORED_INHERENT_FUNCTIONS, + PathNS::Value, + |def_kind| matches!(def_kind, DefKind::Fn | DefKind::AssocFn), + "function", + false, + ); + + // To be "of interest", a function must be trait-preserving, publicly visible, and not `unsafe`. + let of_interest = |def_id| -> bool { + if cx.tcx.visibility(def_id) != ty::Visibility::Public { + return false; + } + + let assoc_item = cx.tcx.associated_item(def_id); + if !matches!(assoc_item.kind, ty::AssocKind::Fn { .. }) { + return false; + } + + let fn_sig = cx.tcx.fn_sig(assoc_item.def_id).skip_binder(); + if fn_sig.safety() == Safety::Unsafe || fn_sig.skip_binder().inputs().len() != 1 { + return false; + } + + let input_ty = fn_sig.input(0).skip_binder(); + let output_ty = fn_sig.output().skip_binder(); + + if let Some(input_item_ty) = implements_trait_with_item(cx, input_ty, into_iterator_def_id) { + // `Option` and `Result` implement `IntoIterator`, but not `Iterator`. So, requiring the output type + // to implement `Iterator` filters out functions that return an `Option` or `Result`. + if let Some(output_item_ty) = implements_trait_with_item(cx, output_ty, iterator_def_id) + && input_item_ty == output_item_ty + { + return true; + } + } else { + // Sanity. Because of the special precautions taken below (see `replace_ty_params_with_global_ty`), + // we should not get here with `std::vec::Vec`. + assert!(!input_ty.to_string().starts_with("std::vec::Vec"), "{input_ty}"); + } + + [input_ty, output_ty].into_iter().all(|ty| { + let ty = peel_unwanted(cx, def_id, ty); + ty.is_slice() + || ty.is_str() + || ty.ty_adt_def().is_some_and(|adt_def| { + watched_type_paths + .iter() + .any(|path| lookup_path(cx.tcx, PathNS::Type, path).contains(&adt_def.did())) + }) + }) + }; + + let slice_incoherent_impl_def_ids = cx + .tcx + .incoherent_impls(SimplifiedType::Slice) + .iter() + .filter(|&impl_def_id| { + // Filter out cases like `core::slice::ascii::::trim_ascii`. + let ty::Slice(ty) = cx.tcx.type_of(impl_def_id).skip_binder().kind() else { + panic!("impl is not for a slice"); + }; + matches!(ty.kind(), ty::Param(_)) + }); + + let str_incoherent_impl_def_ids = cx.tcx.incoherent_impls(SimplifiedType::Str); + + let watched_type_path_impl_def_ids = watched_type_paths + .iter() + .flat_map(|type_path| lookup_path(cx.tcx, PathNS::Type, type_path)) + .flat_map(|def_id| cx.tcx.inherent_impls(def_id)); + + let watched_impl_def_ids = slice_incoherent_impl_def_ids + .chain(str_incoherent_impl_def_ids) + .chain(watched_type_path_impl_def_ids) + .copied() + .collect::>(); + + // Verify that watched and ignored inherent functions are "of interest". + let watched_and_ignored_inherent_functions = WATCHED_INHERENT_FUNCTIONS + .iter() + .chain(IGNORED_INHERENT_FUNCTIONS.iter()) + .filter_map(|conf_path| { + let sym_path = conf_path.to_sym_path(); + + if sym_path.first() == Some(&sym::slice) || sym_path.first() == Some(&sym::str) { + return None; + } + + let def_id = lookup_path(cx.tcx, PathNS::Value, &sym_path).into_iter().next(); + + Some((sym_path, def_id)) + }) + .collect::>(); + for (_, def_id) in &watched_and_ignored_inherent_functions { + let &Some(def_id) = def_id else { + panic!("`lookup_path` failed for some paths: {watched_and_ignored_inherent_functions:#?}") + }; + + assert!(of_interest(def_id), "{:?} is not of interest", cx.get_def_path(def_id)); + } + + // Verify that watched inherent functions are complete(ish). + for impl_def_id in &watched_impl_def_ids { + for assoc_item_def_id in cx.tcx.associated_item_def_ids(impl_def_id) { + if of_interest(*assoc_item_def_id) { + assert!( + lint.watched_inherent_functions_builtin.contains_key(assoc_item_def_id) + || ignored_inherent_functions.contains_key(assoc_item_def_id), + "{:?} is missing", + cx.get_def_path(*assoc_item_def_id) + ); + } + } + } + + // Verify that every non-primitive, watched inherent function is associated with a `type_paths` + // impl. + let mut watched_inherent_functions = lint.watched_inherent_functions_builtin.clone(); + for &impl_def_id in &watched_impl_def_ids { + for assoc_item_def_id in cx.tcx.associated_item_def_ids(impl_def_id) { + watched_inherent_functions.remove(assoc_item_def_id); + } + } + assert!(watched_inherent_functions.is_empty(), "{watched_inherent_functions:?}"); +} + +fn type_paths(conf_paths: &[ConfPath]) -> Vec> +where + T: Deref, + ::Target: ToSymPath, +{ + let mut type_paths = conf_paths + .iter() + .map(|conf_path| conf_path.to_sym_path().split_last().unwrap().1.to_owned()) + .collect::>(); + + type_paths.dedup(); + + type_paths +} + +// See comment preceding `replace_ty_params_with_global_ty` re type parameters. If `ty` contains any +// constant parameters, `implements_trait_with_item` returns `None`. +fn implements_trait_with_item<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, trait_id: DefId) -> Option> { + if let Some(adt_def) = ty.ty_adt_def() + && cx + .tcx + .generics_of(adt_def.did()) + .own_params + .iter() + .any(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) + { + return None; + } + + cx.get_associated_type(replace_ty_params_with_global_ty(cx, ty), trait_id, sym::Item) +} + +// This is a hack. For `get_associated_type` to return `Some(..)`, all of its argument type's type +// parameters must be substituted for. One of the types of interest is `Vec`, and its second type +// parameter must implement `alloc::alloc::Allocator`. So we instantiate all type parameters with +// the default `Allocator`, `alloc::alloc::Global`. A more robust solution would at least consider +// trait bounds and alert when a trait other than `Allocator` was encountered. +fn replace_ty_params_with_global_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + let global_def_id = cx.tcx.lang_items().global_alloc_ty().unwrap(); + let global_adt_def = cx.tcx.adt_def(global_def_id); + let global_ty = Ty::new_adt(cx.tcx, global_adt_def, ty::List::empty()); + ty::BottomUpFolder { + tcx: cx.tcx, + ty_op: |ty| { + if matches!(ty.kind(), ty::Param(_)) { + global_ty + } else { + ty + } + }, + lt_op: std::convert::identity, + ct_op: std::convert::identity, + } + .fold_ty(ty) +} + +fn peel_unwanted<'tcx>(cx: &LateContext<'tcx>, def_id: DefId, mut ty: Ty<'tcx>) -> Ty<'tcx> { + let owned_box = cx.tcx.lang_items().owned_box().unwrap(); + loop { + match ty.kind() { + ty::Ref(_, referent_ty, _) => { + ty = *referent_ty; + continue; + }, + ty::Adt(adt_def, generic_args) if adt_def.did() == owned_box => { + ty = generic_args[0].expect_ty(); + continue; + }, + _ => {}, + } + + if let Some(as_ref_ty) = strip_as_ref(cx, def_id, ty) { + ty = as_ref_ty; + continue; + } + + break; + } + + ty +} + +fn strip_as_ref<'tcx>(cx: &LateContext<'tcx>, def_id: DefId, ty: Ty<'tcx>) -> Option> { + cx.tcx.param_env(def_id).caller_bounds().iter().find_map(|predicate| { + if let ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, .. }) = predicate.kind().skip_binder() + && cx.tcx.get_diagnostic_item(sym::AsRef) == Some(trait_ref.def_id) + && let [self_arg, generic_arg] = trait_ref.args.as_slice() + && self_arg.kind() == ty::GenericArgKind::Type(ty) + && let ty::GenericArgKind::Type(subst_ty) = generic_arg.kind() + { + Some(subst_ty) + } else { + None + } + }) +} diff --git a/clippy_lints/src/needless_conversion_for_trait/mod.rs b/clippy_lints/src/needless_conversion_for_trait/mod.rs new file mode 100644 index 000000000000..66d8e511dc26 --- /dev/null +++ b/clippy_lints/src/needless_conversion_for_trait/mod.rs @@ -0,0 +1,401 @@ +use clippy_config::Conf; +use clippy_config::types::{ + ConfPath, ConfPathWithoutReplacement, SymPath, conf_path_from_sym_path, create_conf_path_map, +}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::msrvs::Msrv; +use clippy_utils::paths::{self, PathNS}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::{ + build_check_predicates_with_new_ty_closure, get_callee_generic_args_and_args, is_copy, is_on_string_like, + is_to_string, +}; +use clippy_utils::{get_parent_expr, sym}; +use rustc_errors::Applicability; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::{FnDef, GenericArgsRef, Param, ParamTy, Ty, TyCtxt}; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::Symbol; + +#[cfg(debug_assertions)] +mod check_inherent_functions; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for trait-behavior-preserving calls in positions where a trait implementation is + /// expected. + /// + /// ### Why is this bad? + /// + /// Such unnecessary calls make the code more verbose and could impact performance. + /// + /// ### Example + /// + /// ```rust + /// # use std::{path::Path, process::Command}; + /// let _ = Command::new("ls").args(["-a", "-l"].iter()); + /// let _ = Path::new("/").join(Path::new(".")); + /// ``` + /// + /// Use instead: + /// + /// ```rust + /// # use std::{path::Path, process::Command}; + /// let _ = Command::new("ls").args(["-a", "-l"]); + /// let _ = Path::new("/").join("."); + /// ``` + #[clippy::version = "1.89.0"] + pub NEEDLESS_CONVERSION_FOR_TRAIT, + complexity, + "unnecessary calls that preserve trait behavior" +} + +impl_lint_pass!(NeedlessConversionForTrait => [NEEDLESS_CONVERSION_FOR_TRAIT]); + +pub struct NeedlessConversionForTrait { + watched_inherent_functions_builtin: DefIdMap<(&'static [Symbol], &'static ConfPath, false>)>, + watched_trait_methods_builtin: DefIdMap<(&'static [Symbol], &'static ConfPath, false>)>, + watched_functions_from_conf: DefIdMap<(&'static str, &'static ConfPathWithoutReplacement)>, + msrv: Msrv, +} + +impl NeedlessConversionForTrait { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + let (watched_inherent_functions_builtin, _) = create_conf_path_map( + tcx, + WATCHED_INHERENT_FUNCTIONS, + PathNS::Value, + |def_kind| matches!(def_kind, DefKind::Fn | DefKind::AssocFn), + "function", + false, + ); + let (watched_trait_methods_builtin, _) = create_conf_path_map( + tcx, + WATCHED_TRAIT_METHODS, + PathNS::Value, + |def_kind| matches!(def_kind, DefKind::Fn | DefKind::AssocFn), + "function", + false, + ); + let (watched_functions_from_conf, _) = create_conf_path_map( + tcx, + &conf.watched_functions, + PathNS::Value, + |def_kind| matches!(def_kind, DefKind::Fn | DefKind::AssocFn), + "function", + false, + ); + Self { + watched_inherent_functions_builtin, + watched_trait_methods_builtin, + watched_functions_from_conf, + msrv: conf.msrv, + } + } +} + +macro_rules! conf_paths { + ($($sym_path:expr,)*) => { + &[ + $(conf_path_from_sym_path($sym_path),)* + ] + }; +} + +/// Inherent functions that are trait preserving. +/// +/// Intuitively, a function is trait preserving if it changes only its argument's type, but not the +/// argument's value. +/// +/// For example, [`str::as_bytes`] is trait preserving because it changes the type of its argument +/// from `&str` to `&[u8]`, but does not change the argument's value. +/// +/// On the other hand, [`str::to_lowercase`] is not trait preserving because it changes uppercase +/// characters to lowercase characters. +/// +/// [`str::to_lowercase`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase +const WATCHED_INHERENT_FUNCTIONS: &[ConfPath, false>] = conf_paths!( + &[sym::slice, sym::into_vec], + &[sym::slice, sym::to_vec], + &[sym::str, sym::into_boxed_bytes], + &[sym::str, sym::into_string], + &[sym::alloc, sym::string, sym::String, sym::as_bytes], + &[sym::alloc, sym::string, sym::String, sym::as_mut_str], + &[sym::alloc, sym::string, sym::String, sym::as_str], + &[sym::alloc, sym::string, sym::String, sym::into_boxed_str], + &[sym::alloc, sym::string, sym::String, sym::into_bytes], + &[sym::alloc, sym::vec, sym::Vec, sym::as_mut_slice], + &[sym::alloc, sym::vec, sym::Vec, sym::as_slice], + &[sym::alloc, sym::vec, sym::Vec, sym::into_boxed_slice], + &[sym::slice, sym::iter], + &[sym::slice, sym::iter_mut], + &[sym::str, sym::as_bytes], + &[sym::str, sym::as_str], + &[sym::std, sym::ffi, sym::os_str, sym::OsStr, sym::as_encoded_bytes], + &[sym::std, sym::ffi, sym::os_str, sym::OsStr, sym::into_os_string], + &[sym::std, sym::ffi, sym::os_str, sym::OsStr, sym::new], + &[sym::std, sym::ffi, sym::os_str, sym::OsStr, sym::to_os_string], + &[sym::std, sym::ffi, sym::os_str, sym::OsString, sym::as_os_str], + &[sym::std, sym::ffi, sym::os_str, sym::OsString, sym::into_boxed_os_str], + &[sym::std, sym::ffi, sym::os_str, sym::OsString, sym::into_encoded_bytes], + &[sym::std, sym::path, sym::Path, sym::as_mut_os_str], + &[sym::std, sym::path, sym::Path, sym::as_os_str], + &[sym::std, sym::path, sym::Path, sym::into_path_buf], + &[sym::std, sym::path, sym::Path, sym::iter], + &[sym::std, sym::path, sym::Path, sym::new], + &[sym::std, sym::path, sym::Path, sym::to_path_buf], + &[sym::std, sym::path, sym::PathBuf, sym::as_mut_os_string], + &[sym::std, sym::path, sym::PathBuf, sym::as_path], + &[sym::std, sym::path, sym::PathBuf, sym::into_boxed_path], + &[sym::std, sym::path, sym::PathBuf, sym::into_os_string], +); + +/// Inherent functions that are not trait preserving. +/// +/// See [`WATCHED_INHERENT_FUNCTIONS`] for an explanation of "trait preserving". +const IGNORED_INHERENT_FUNCTIONS: &[ConfPath, false>] = conf_paths!( + &[sym::str, sym::to_ascii_lowercase], + &[sym::str, sym::to_ascii_uppercase], + &[sym::str, sym::to_lowercase], + &[sym::str, sym::to_uppercase], + &[sym::alloc, sym::string, sym::String, sym::from_utf16_lossy], + &[sym::alloc, sym::string, sym::String, sym::from_utf16be_lossy], + &[sym::alloc, sym::string, sym::String, sym::from_utf16le_lossy], + &[sym::alloc, sym::string, sym::String, sym::from_utf8_lossy_owned], + &[sym::alloc, sym::string, sym::String, sym::leak], + &[sym::alloc, sym::vec, sym::Vec, sym::into_chunks], + &[sym::alloc, sym::vec, sym::Vec, sym::into_flattened], + &[sym::alloc, sym::vec, sym::Vec, sym::leak], + &[sym::alloc, sym::vec, sym::Vec, sym::spare_capacity_mut], + &[sym::str, sym::trim], + &[sym::str, sym::trim_ascii], + &[sym::str, sym::trim_ascii_end], + &[sym::str, sym::trim_ascii_start], + &[sym::str, sym::trim_end], + &[sym::str, sym::trim_left], + &[sym::str, sym::trim_right], + &[sym::str, sym::trim_start], + &[sym::std, sym::ffi, sym::os_str, sym::OsStr, sym::to_ascii_lowercase], + &[sym::std, sym::ffi, sym::os_str, sym::OsStr, sym::to_ascii_uppercase], + &[sym::std, sym::ffi, sym::os_str, sym::OsString, sym::leak], + &[sym::std, sym::path, sym::PathBuf, sym::leak], +); + +/// Trait methods that are trait preserving. +/// +/// See [`WATCHED_INHERENT_FUNCTIONS`] for an explanation of "trait preserving". +const WATCHED_TRAIT_METHODS: &[ConfPath, false>] = conf_paths!( + &[sym::alloc, sym::borrow, sym::ToOwned, sym::to_owned], + &[sym::alloc, sym::string, sym::ToString, sym::to_string], + &[sym::core, sym::borrow, sym::Borrow, sym::borrow], + &[sym::core, sym::borrow, sym::BorrowMut, sym::borrow_mut], + &[sym::core, sym::convert, sym::AsMut, sym::as_mut], + &[sym::core, sym::convert, sym::AsRef, sym::as_ref], + &[sym::core, sym::ops, sym::deref, sym::Deref, sym::deref], + &[sym::core, sym::ops, sym::deref, sym::DerefMut, sym::deref_mut], +); + +impl<'tcx> LateLintPass<'tcx> for NeedlessConversionForTrait { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let Some((maybe_call, maybe_arg, ancestor_mutabilities)) = ancestor_addr_of_mutabilities(cx, expr) + && let Some((outer_callee_def_id, outer_generic_args, outer_receiver, outer_args)) = + get_callee_generic_args_and_args(cx, maybe_call) + && let outer_args = std::iter::once(outer_receiver) + .flatten() + .chain(outer_args) + .collect::>() + && let outer_fn_sig = cx.tcx.fn_sig(outer_callee_def_id).skip_binder().skip_binder() + && let Some(i) = outer_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id) + && let Some(input) = outer_fn_sig.inputs().get(i) + && let Param(param_ty) = input.kind() + { + let strip_unnecessary_conversions = |mut expr, mut mutabilities| { + let mut refs_prefix = None; + + loop { + if let Some((inner_callee_def_id, _inner_generic_args, inner_receiver, inner_args)) = + get_callee_generic_args_and_args(cx, expr) + && let inner_args = std::iter::once(inner_receiver) + .flatten() + .chain(inner_args) + .collect::>() + && let &[maybe_boxed_inner_arg] = inner_args.as_slice() + && let inner_arg = peel_boxes(cx, maybe_boxed_inner_arg) + && let inner_arg_ty = cx.typeck_results().expr_ty(inner_arg) + && let adjustment_mutabilities = adjustment_mutabilities(cx, inner_arg) + && let new_mutabilities = [adjustment_mutabilities, mutabilities].concat() + && let (new_ty, new_refs_prefix) = build_ty_and_refs_prefix(cx, inner_arg_ty, &new_mutabilities) + && inner_arg_implements_traits( + cx, + outer_callee_def_id, + outer_generic_args, + i, + *param_ty, + new_ty, + self.msrv, + ) + // For `unnecessary_to_owned` to eliminate a call to `ToString::to_string`, + // the receiver's type must implement `Deref` or `AsRef`. + // The same restriction is applied here. See: + // https://github.com/rust-lang/rust-clippy/blob/e6dc2e9be026e9d26296a51e496d1b25b5e0721e/clippy_lints/src/methods/unnecessary_to_owned.rs#L651-L652 + && (!is_to_string(cx, expr, inner_callee_def_id) || is_on_string_like(cx, expr)) + { + if !self + .watched_inherent_functions_builtin + .contains_key(&inner_callee_def_id) + && !self.watched_trait_methods_builtin.contains_key(&inner_callee_def_id) + && !self.watched_functions_from_conf.contains_key(&inner_callee_def_id) + { + break; + } + expr = inner_arg; + mutabilities = new_mutabilities; + refs_prefix = Some(new_refs_prefix); + continue; + } + break; + } + + Some(expr).zip(refs_prefix) + }; + + if let Some((inner_arg, refs_prefix)) = strip_unnecessary_conversions(expr, ancestor_mutabilities) { + let (is_bare_method_call, subject) = if matches!(expr.kind, ExprKind::MethodCall(..)) { + (maybe_arg.hir_id == expr.hir_id, "receiver") + } else { + (false, "inner argument") + }; + let msg = format!("the {subject} implements the required traits"); + if is_bare_method_call && refs_prefix.is_empty() && !maybe_arg.span.from_expansion() { + span_lint_and_sugg( + cx, + NEEDLESS_CONVERSION_FOR_TRAIT, + maybe_arg.span.with_lo(inner_arg.span.hi()), + msg, + "remove this", + String::new(), + Applicability::MachineApplicable, + ); + } else if maybe_arg.span.from_expansion() + && let Some(span) = maybe_arg.span.parent_callsite() + { + span_lint_and_help( + cx, + NEEDLESS_CONVERSION_FOR_TRAIT, + span, + msg, + None, + "use the macro arguments directly", + ); + } else if let Some(snippet) = snippet_opt(cx, inner_arg.span) { + span_lint_and_sugg( + cx, + NEEDLESS_CONVERSION_FOR_TRAIT, + maybe_arg.span, + msg, + "use", + format!("{refs_prefix}{snippet}"), + Applicability::MachineApplicable, + ); + } + } + } + } + + #[cfg(debug_assertions)] + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + check_inherent_functions::check_inherent_functions(cx, self); + } +} + +/// `inner_arg_implements_traits` is similar to `needless_borrow_count` from +/// [`needless_borrows_for_generic_args`]. The core logic of both functions is in +/// [`clippy_utils::ty::build_check_predicates_with_new_ty_closure`]. +/// +/// [`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args +fn inner_arg_implements_traits<'tcx>( + cx: &LateContext<'tcx>, + fn_id: DefId, + callee_generic_args: GenericArgsRef<'tcx>, + arg_index: usize, + param_ty: ParamTy, + new_ty: Ty<'tcx>, + msrv: Msrv, +) -> bool { + build_check_predicates_with_new_ty_closure(cx, fn_id, callee_generic_args, arg_index, param_ty, false, msrv) + .is_some_and(|mut f| f(new_ty)) +} + +fn ancestor_addr_of_mutabilities<'tcx>( + cx: &LateContext<'tcx>, + mut expr: &'tcx Expr<'tcx>, +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Vec)> { + let mut mutabilities = Vec::new(); + while let Some(parent) = get_parent_expr(cx, expr) { + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = parent.kind { + mutabilities.push(mutability); + expr = parent; + } else { + return Some((parent, expr, mutabilities)); + } + } + None +} + +fn peel_boxes<'tcx>(cx: &LateContext<'tcx>, mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + loop { + if let ExprKind::Call(callee, args) = expr.kind + && let callee_ty = cx.typeck_results().expr_ty(callee) + && let FnDef(callee_def_id, _) = callee_ty.kind() + && paths::BOX_NEW.matches(cx, *callee_def_id) + && let [inner_arg] = args + { + expr = inner_arg; + continue; + } + + break; + } + + expr +} + +fn adjustment_mutabilities<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Vec { + cx.typeck_results() + .expr_adjustments(expr) + .iter() + .map_while(|adjustment| { + if let Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(mutability)), + target: _, + } = adjustment + { + Some((*mutability).into()) + } else { + None + } + }) + .collect() +} + +fn build_ty_and_refs_prefix<'tcx>( + cx: &LateContext<'tcx>, + mut ty: Ty<'tcx>, + mutabilities: &[Mutability], +) -> (Ty<'tcx>, String) { + let mut refs_prefix = String::new(); + for &mutability in mutabilities { + // If the type is already copy, don't bother adding any more refs. + if is_copy(cx, ty) { + break; + } + ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, ty, mutability); + refs_prefix = "&".to_owned() + mutability.prefix_str() + &refs_prefix; + } + (ty, refs_prefix) +} diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 7052e1d0fbe5..4dc63784d643 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -279,7 +279,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { |diag| { diag.span_suggestion( sp, - "consider changing to".to_string(), + "consider changing to", format!("&{}", snippet(cx, cx.tcx.hir_span(inner_ty.ty.hir_id), "_"),), Applicability::Unspecified, ); diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 3828aff4164e..7ae9c0d730e9 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -63,12 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { span, format!("pub(crate) {descr} inside private module"), |diag| { - diag.span_suggestion( - item.vis_span, - "consider using", - "pub".to_string(), - Applicability::MachineApplicable, - ); + diag.span_suggestion(item.vis_span, "consider using", "pub", Applicability::MachineApplicable); }, ); } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ea8cfc59356a..475e782b595e 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -126,6 +126,9 @@ path_macros! { macro_path: PathNS::Macro, } +// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. +pub static BOX_NEW: PathLookup = value_path!(alloc::boxed::box_new); + // Paths in external crates pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index a63333c9b48f..845c03988096 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -698,6 +698,7 @@ pub trait DiagExt { fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability); } +#[allow(clippy::needless_conversion_for_trait)] impl DiagExt for rustc_errors::Diag<'_, ()> { fn suggest_item_with_attr( &mut self, diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index ce7cc9348fbd..91687753fa77 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -44,6 +44,7 @@ generate! { Cargo_toml: "Cargo.toml", Current, DOUBLE_QUOTE: "\"", + DerefMut, Deserialize, EarlyLintPass, IntoIter, @@ -80,12 +81,20 @@ generate! { as_bytes, as_deref, as_deref_mut, + as_encoded_bytes, as_mut, + as_mut_os_str, + as_mut_os_string, + as_mut_slice, + as_mut_str, + as_os_str, as_path, + as_slice, assert_failed, author, borrow, borrow_mut, + boxed, build_hasher, by_ref, bytes, @@ -114,6 +123,7 @@ generate! { collect, const_ptr, contains, + convert, copied, copy_from, copy_from_nonoverlapping, @@ -159,6 +169,10 @@ generate! { from_raw, from_str, from_str_radix, + from_utf16_lossy, + from_utf16be_lossy, + from_utf16le_lossy, + from_utf8_lossy_owned, fs, fuse, futures_util, @@ -175,9 +189,21 @@ generate! { inspect, int_roundings, into, + into_boxed_bytes, + into_boxed_os_str, + into_boxed_path, + into_boxed_slice, + into_boxed_str, into_bytes, + into_chunks, + into_encoded_bytes, + into_flattened, into_ok, + into_os_string, into_owned, + into_path_buf, + into_string, + into_vec, io, is_ascii, is_char_boundary, @@ -196,6 +222,7 @@ generate! { kw, last, lazy_static, + leak, lint_vec, ln, lock, @@ -240,6 +267,7 @@ generate! { or_else, or_insert, or_insert_with, + os_str, outer_expn, panic_cold_display, panic_cold_explicit, @@ -305,6 +333,7 @@ generate! { sort_by, sort_unstable_by, span_lint_and_then, + spare_capacity_mut, split, split_at, split_at_checked, @@ -319,6 +348,7 @@ generate! { sqrt, starts_with, step_by, + string, strlen, style, subsec_micros, @@ -342,8 +372,13 @@ generate! { to_uppercase, tokio, trim, + trim_ascii, + trim_ascii_end, + trim_ascii_start, trim_end, trim_end_matches, + trim_left, + trim_right, trim_start, trim_start_matches, truncate, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index d79773f83211..4daa940c2d27 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -2,6 +2,7 @@ #![allow(clippy::module_name_repetitions)] +use crate::msrvs::{self, Msrv}; use core::ops::ControlFlow; use itertools::Itertools; use rustc_abi::VariantIdx; @@ -13,6 +14,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, FnDecl, LangItem, TyKind, find_attr}; use rustc_hir_analysis::lower_ty; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; @@ -21,7 +23,7 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + GenericParamDefKind, IntTy, ParamTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; @@ -30,11 +32,12 @@ 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::VecDeque; use std::collections::hash_map::Entry; use std::iter; -use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; +use crate::{is_diag_trait_item, path_res}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -1392,3 +1395,286 @@ pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { _ => None, } } + +/// Checks whether an expression is a function or method call and, if so, returns its `DefId`, +/// `GenericArgs`, and arguments. +pub fn get_callee_generic_args_and_args<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option<( + DefId, + GenericArgsRef<'tcx>, + Option<&'tcx Expr<'tcx>>, + &'tcx [Expr<'tcx>], +)> { + if let hir::ExprKind::Call(callee, args) = expr.kind + && let callee_ty = cx.typeck_results().expr_ty(callee) + && let ty::FnDef(callee_def_id, _) = callee_ty.kind() + { + let generic_args = cx.typeck_results().node_args(callee.hir_id); + return Some((*callee_def_id, generic_args, None, args)); + } + if let hir::ExprKind::MethodCall(_, recv, args, _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + { + let generic_args = cx.typeck_results().node_args(expr.hir_id); + return Some((method_def_id, generic_args, Some(recv), args)); + } + None +} + +/// Returns true if the named method is `ToString::to_string` and it's called on a type that +/// is string-like i.e. implements `AsRef` or `Deref`. +pub fn is_to_string_on_string_like<'a>(cx: &LateContext<'_>, call_expr: &'a Expr<'a>, method_def_id: DefId) -> bool { + is_to_string(cx, call_expr, method_def_id) && is_on_string_like(cx, call_expr) +} + +/// Returns true if the named method is `ToString::to_string`. +pub fn is_to_string(cx: &LateContext<'_>, call_expr: &Expr<'_>, method_def_id: DefId) -> bool { + let hir::ExprKind::MethodCall(method_name, _, _, _) = call_expr.kind else { + return false; + }; + + if method_name.ident.name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) { + return false; + } + + true +} + +/// Returns true if `call_expr` is on a type that is string-like i.e. implements `AsRef` or +/// `Deref`. +pub fn is_on_string_like<'a>(cx: &LateContext<'_>, call_expr: &'a Expr<'a>) -> bool { + if let Some(args) = cx.typeck_results().node_args_opt(call_expr.hir_id) + && let [generic_arg] = args.as_slice() + && let GenericArgKind::Type(ty) = generic_arg.kind() + && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) + && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) + && (cx.get_associated_type(ty, deref_trait_id, sym::Target) == Some(cx.tcx.types.str_) + || implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) + { + true + } else { + false + } +} + +/// Builds a closure to check whether predicates over `param_ty` would be satisfied if `param_ty` +/// were replaced with `new_ty`. +/// +/// Used by `inner_arg_implements_trait` and `needless_borrow_count`. +pub fn build_check_predicates_with_new_ty_closure<'tcx>( + cx: &LateContext<'tcx>, + fn_id: DefId, + callee_generic_args: GenericArgsRef<'tcx>, + arg_index: usize, + param_ty: ParamTy, + check_for_ref_mut_self_methods: bool, + msrv: Msrv, +) -> Option) -> bool> { + let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); + let meta_sized_trait_def_id = cx.tcx.lang_items().meta_sized_trait(); + let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); + + let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder(); + let predicates = cx.tcx.param_env(fn_id).caller_bounds(); + let projection_predicates = predicates + .iter() + .filter_map(|predicate| { + if let ty::ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() { + Some(projection_predicate) + } else { + None + } + }) + .collect::>(); + + let mut trait_with_ref_mut_self_method = false; + + // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return. + if predicates + .iter() + .filter_map(|predicate| { + if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) + { + Some(trait_predicate.trait_ref.def_id) + } else { + None + } + }) + .inspect(|trait_def_id| { + trait_with_ref_mut_self_method |= + check_for_ref_mut_self_methods && has_ref_mut_self_method(cx, *trait_def_id); + }) + .all(|trait_def_id| { + Some(trait_def_id) == destruct_trait_def_id + || Some(trait_def_id) == meta_sized_trait_def_id + || Some(trait_def_id) == sized_trait_def_id + || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) + }) + { + return None; + } + + // See: + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 + if projection_predicates + .iter() + .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate)) + { + return None; + } + + // `args_with_new_ty` can be constructed outside of the closure because the same elements are + // modified each time the closure is called. + let mut args_with_new_ty = callee_generic_args.to_vec(); + + Some(move |new_ty: Ty<'tcx>| { + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + if trait_with_ref_mut_self_method && !matches!(new_ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + return false; + } + + if !replace_types( + cx, + param_ty, + new_ty, + fn_sig, + arg_index, + &projection_predicates, + &mut args_with_new_ty, + ) { + return false; + } + + predicates.iter().all(|predicate| { + if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && cx + .tcx + .is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) + && let ty::Param(param_ty) = trait_predicate.self_ty().kind() + && let GenericArgKind::Type(ty) = args_with_new_ty[param_ty.index as usize].kind() + && ty.is_array() + && !msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) + { + return false; + } + + let predicate = ty::EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_new_ty[..]); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); + let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); + infcx.predicate_must_hold_modulo_regions(&obligation) + }) + }) +} + +fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { + cx.tcx + .associated_items(trait_def_id) + .in_definition_order() + .any(|assoc_item| { + if assoc_item.is_method() { + let self_ty = cx + .tcx + .fn_sig(assoc_item.def_id) + .instantiate_identity() + .skip_binder() + .inputs()[0]; + matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) + } else { + false + } + }) +} + +fn is_mixed_projection_predicate<'tcx>( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + projection_predicate: &ty::ProjectionPredicate<'tcx>, +) -> bool { + let generics = cx.tcx.generics_of(callee_def_id); + // The predicate requires the projected type to equal a type parameter from the parent context. + if let Some(term_ty) = projection_predicate.term.as_type() + && let ty::Param(term_param_ty) = term_ty.kind() + && (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_term = projection_predicate.projection_term; + loop { + match *projection_term.self_ty().kind() { + ty::Alias(ty::Projection, inner_projection_ty) => { + projection_term = inner_projection_ty.into(); + }, + ty::Param(param_ty) => { + return (param_ty.index as usize) >= generics.parent_count; + }, + _ => { + return 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`. +// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 +fn replace_types<'tcx>( + cx: &LateContext<'tcx>, + param_ty: ParamTy, + new_ty: Ty<'tcx>, + fn_sig: FnSig<'tcx>, + arg_index: usize, + projection_predicates: &[ty::ProjectionPredicate<'tcx>], + args: &mut [GenericArg<'tcx>], +) -> bool { + let mut replaced = DenseBitSet::new_empty(args.len()); + + let mut deque = VecDeque::with_capacity(args.len()); + deque.push_back((param_ty, new_ty)); + + while let Some((param_ty, new_ty)) = deque.pop_front() { + // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in. Note that + // the types the `all` iterates over include the output type. Thus, if replacing `param_ty` with + // `new_ty` would change the output type, this check will fail. + if !fn_sig + .inputs_and_output + .iter() + .enumerate() + .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx))) + { + return false; + } + + args[param_ty.index as usize] = GenericArg::from(new_ty); + + // 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_term.self_ty() == param_ty.to_ty(cx.tcx) + && let Some(term_ty) = projection_predicate.term.as_type() + && let ty::Param(term_param_ty) = term_ty.kind() + { + let projection = projection_predicate + .projection_term + .with_replaced_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.typing_env(), projection) + && args[term_param_ty.index as usize] != GenericArg::from(projected_ty) + { + deque.push_back((*term_param_ty, projected_ty)); + } + } + } + } + } + + true +} diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 3a60cfa79f41..1ad96f02e72c 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -180,7 +180,7 @@ impl Crate { let shared_target_dir = shared_target_dir(&format!("_{thread_index:?}")); let all_output = cmd // use the looping index to create individual target dirs - .env("CARGO_TARGET_DIR", shared_target_dir.as_os_str()) + .env("CARGO_TARGET_DIR", &shared_target_dir) // Roughly equivalent to `cargo clippy`/`cargo clippy --fix` .env("RUSTC_WORKSPACE_WRAPPER", clippy_driver_path) .output() diff --git a/needless_conversion_for_trait_unnecessary_to_owned b/needless_conversion_for_trait_unnecessary_to_owned new file mode 100755 index 000000000000..a826a122c292 Binary files /dev/null and b/needless_conversion_for_trait_unnecessary_to_owned differ diff --git a/tests/ui-internal/collapsible_span_lint_calls.fixed b/tests/ui-internal/collapsible_span_lint_calls.fixed index 76f68686ee2a..9b43233c9c42 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.fixed +++ b/tests/ui-internal/collapsible_span_lint_calls.fixed @@ -1,5 +1,5 @@ #![deny(clippy::collapsible_span_lint_calls)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::needless_conversion_for_trait)] #![feature(rustc_private)] extern crate clippy_utils; diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs index 214c8783a669..c20edc3e05ae 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.rs +++ b/tests/ui-internal/collapsible_span_lint_calls.rs @@ -1,5 +1,5 @@ #![deny(clippy::collapsible_span_lint_calls)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::needless_conversion_for_trait)] #![feature(rustc_private)] extern crate clippy_utils; diff --git a/tests/ui-toml/needless_conversion_for_trait_tempfile/clippy.toml b/tests/ui-toml/needless_conversion_for_trait_tempfile/clippy.toml new file mode 100644 index 000000000000..42a6d0ca2655 --- /dev/null +++ b/tests/ui-toml/needless_conversion_for_trait_tempfile/clippy.toml @@ -0,0 +1,4 @@ +watched-functions = [ + "tempfile::dir::TempDir::path", + "tempfile::file::NamedTempFile::path", +] diff --git a/tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.fixed b/tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.fixed new file mode 100644 index 000000000000..2b5e99949c51 --- /dev/null +++ b/tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.fixed @@ -0,0 +1,13 @@ +#![feature(rustc_private)] + +extern crate tempfile; + +use tempfile::{NamedTempFile, TempDir}; + +fn main() { + let tempdir = TempDir::new().unwrap(); + let tempfile = NamedTempFile::new().unwrap(); + + let _ = std::fs::write(&tempdir, ""); //~ needless_conversion_for_trait + let _ = std::fs::write(&tempfile, ""); //~ needless_conversion_for_trait +} diff --git a/tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.rs b/tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.rs new file mode 100644 index 000000000000..79cbb968afd6 --- /dev/null +++ b/tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.rs @@ -0,0 +1,13 @@ +#![feature(rustc_private)] + +extern crate tempfile; + +use tempfile::{NamedTempFile, TempDir}; + +fn main() { + let tempdir = TempDir::new().unwrap(); + let tempfile = NamedTempFile::new().unwrap(); + + let _ = std::fs::write(tempdir.path(), ""); //~ needless_conversion_for_trait + let _ = std::fs::write(tempfile.path(), ""); //~ needless_conversion_for_trait +} diff --git a/tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.stderr b/tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.stderr new file mode 100644 index 000000000000..493b733272d0 --- /dev/null +++ b/tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.stderr @@ -0,0 +1,17 @@ +error: the receiver implements the required traits + --> tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.rs:11:28 + | +LL | let _ = std::fs::write(tempdir.path(), ""); + | ^^^^^^^^^^^^^^ help: use: `&tempdir` + | + = note: `-D clippy::needless-conversion-for-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_conversion_for_trait)]` + +error: the receiver implements the required traits + --> tests/ui-toml/needless_conversion_for_trait_tempfile/needless_conversion_for_trait_tempfile.rs:12:28 + | +LL | let _ = std::fs::write(tempfile.path(), ""); + | ^^^^^^^^^^^^^^^ help: use: `&tempfile` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs index 9c770c31f6f8..c24ded9a4c97 100644 --- a/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs +++ b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs @@ -1,5 +1,5 @@ #[rustfmt::skip] -//@error-in-other-file: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum +//@error-in-other-file: error reading Clippy's configuration file: data did not match any variant of untagged enum ConfPathEnum fn main() { panic!(); } diff --git a/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr index b564709721d5..b4f247335482 100644 --- a/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr +++ b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum +error: error reading Clippy's configuration file: data did not match any variant of untagged enum ConfPathEnum --> $DIR/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml:3:5 | LL | { path = "std::panic", recommendation = "return a `std::result::Result::Error` instead" }, 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 6ee77ebd8ece..85385585b86c 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -87,6 +87,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect verbose-bit-mask-threshold warn-on-all-wildcard-imports warn-unsafe-macro-metavars-in-private-macros + watched-functions --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1 | LL | foobar = 42 @@ -181,6 +182,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect verbose-bit-mask-threshold warn-on-all-wildcard-imports warn-unsafe-macro-metavars-in-private-macros + watched-functions --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1 | LL | barfoo = 53 @@ -275,6 +277,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni verbose-bit-mask-threshold warn-on-all-wildcard-imports warn-unsafe-macro-metavars-in-private-macros + watched-functions --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1 | LL | allow_mixed_uninlined_format_args = true diff --git a/tests/ui/cognitive_complexity.rs b/tests/ui/cognitive_complexity.rs index 8080c6775e0b..eb2e002c3c37 100644 --- a/tests/ui/cognitive_complexity.rs +++ b/tests/ui/cognitive_complexity.rs @@ -2,6 +2,7 @@ #![allow( clippy::eq_op, clippy::needless_borrows_for_generic_args, + clippy::needless_conversion_for_trait, clippy::needless_return, clippy::nonminimal_bool, clippy::uninlined_format_args diff --git a/tests/ui/cognitive_complexity.stderr b/tests/ui/cognitive_complexity.stderr index 67ef4e5655bd..d295b856e9ff 100644 --- a/tests/ui/cognitive_complexity.stderr +++ b/tests/ui/cognitive_complexity.stderr @@ -1,5 +1,5 @@ error: the function has a cognitive complexity of (28/25) - --> tests/ui/cognitive_complexity.rs:11:4 + --> tests/ui/cognitive_complexity.rs:12:4 | LL | fn main() { | ^^^^ @@ -9,7 +9,7 @@ LL | fn main() { = help: to override `-D warnings` add `#[allow(clippy::cognitive_complexity)]` error: the function has a cognitive complexity of (7/1) - --> tests/ui/cognitive_complexity.rs:98:4 + --> tests/ui/cognitive_complexity.rs:99:4 | LL | fn kaboom() { | ^^^^^^ @@ -17,7 +17,7 @@ LL | fn kaboom() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:158:4 + --> tests/ui/cognitive_complexity.rs:159:4 | LL | fn baa() { | ^^^ @@ -25,7 +25,7 @@ LL | fn baa() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:161:13 + --> tests/ui/cognitive_complexity.rs:162:13 | LL | let x = || match 99 { | ^^ @@ -33,7 +33,7 @@ LL | let x = || match 99 { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:179:4 + --> tests/ui/cognitive_complexity.rs:180:4 | LL | fn bar() { | ^^^ @@ -41,7 +41,7 @@ LL | fn bar() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:192:4 + --> tests/ui/cognitive_complexity.rs:193:4 | LL | fn dont_warn_on_tests() { | ^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | fn dont_warn_on_tests() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:202:4 + --> tests/ui/cognitive_complexity.rs:203:4 | LL | fn barr() { | ^^^^ @@ -57,7 +57,7 @@ LL | fn barr() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (3/1) - --> tests/ui/cognitive_complexity.rs:214:4 + --> tests/ui/cognitive_complexity.rs:215:4 | LL | fn barr2() { | ^^^^^ @@ -65,7 +65,7 @@ LL | fn barr2() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:232:4 + --> tests/ui/cognitive_complexity.rs:233:4 | LL | fn barrr() { | ^^^^^ @@ -73,7 +73,7 @@ LL | fn barrr() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (3/1) - --> tests/ui/cognitive_complexity.rs:244:4 + --> tests/ui/cognitive_complexity.rs:245:4 | LL | fn barrr2() { | ^^^^^^ @@ -81,7 +81,7 @@ LL | fn barrr2() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:262:4 + --> tests/ui/cognitive_complexity.rs:263:4 | LL | fn barrrr() { | ^^^^^^ @@ -89,7 +89,7 @@ LL | fn barrrr() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (3/1) - --> tests/ui/cognitive_complexity.rs:274:4 + --> tests/ui/cognitive_complexity.rs:275:4 | LL | fn barrrr2() { | ^^^^^^^ @@ -97,7 +97,7 @@ LL | fn barrrr2() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:292:4 + --> tests/ui/cognitive_complexity.rs:293:4 | LL | fn cake() { | ^^^^ @@ -105,7 +105,7 @@ LL | fn cake() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (4/1) - --> tests/ui/cognitive_complexity.rs:304:8 + --> tests/ui/cognitive_complexity.rs:305:8 | LL | pub fn read_file(input_path: &str) -> String { | ^^^^^^^^^ @@ -113,7 +113,7 @@ LL | pub fn read_file(input_path: &str) -> String { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:337:4 + --> tests/ui/cognitive_complexity.rs:338:4 | LL | fn void(void: Void) { | ^^^^ @@ -121,7 +121,7 @@ LL | fn void(void: Void) { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (8/1) - --> tests/ui/cognitive_complexity.rs:390:4 + --> tests/ui/cognitive_complexity.rs:391:4 | LL | fn early_ret() -> i32 { | ^^^^^^^^^ @@ -129,7 +129,7 @@ LL | fn early_ret() -> i32 { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:413:13 + --> tests/ui/cognitive_complexity.rs:414:13 | LL | let x = |a: i32, b: i32| -> i32 { | ^^^^^^^^^^^^^^^^ @@ -137,7 +137,7 @@ LL | let x = |a: i32, b: i32| -> i32 { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:428:8 + --> tests/ui/cognitive_complexity.rs:429:8 | LL | fn moo(&self) { | ^^^ @@ -145,7 +145,7 @@ LL | fn moo(&self) { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:439:14 + --> tests/ui/cognitive_complexity.rs:440:14 | LL | async fn a() { | ^ @@ -153,7 +153,7 @@ LL | async fn a() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:448:22 + --> tests/ui/cognitive_complexity.rs:449:22 | LL | pub async fn async_method() { | ^^^^^^^^^^^^ @@ -161,7 +161,7 @@ LL | pub async fn async_method() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:459:8 + --> tests/ui/cognitive_complexity.rs:460:8 | LL | fn foo() { | ^^^ @@ -169,7 +169,7 @@ LL | fn foo() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:466:8 + --> tests/ui/cognitive_complexity.rs:467:8 | LL | fn bar() { | ^^^ diff --git a/tests/ui/needless_borrows_for_generic_args.fixed b/tests/ui/needless_borrows_for_generic_args.fixed index 8dea0d259772..5f61d9e9676a 100644 --- a/tests/ui/needless_borrows_for_generic_args.fixed +++ b/tests/ui/needless_borrows_for_generic_args.fixed @@ -2,7 +2,8 @@ #![allow( clippy::unnecessary_to_owned, clippy::unnecessary_literal_unwrap, - clippy::needless_borrow + clippy::needless_borrow, + clippy::needless_conversion_for_trait )] use core::ops::Deref; diff --git a/tests/ui/needless_borrows_for_generic_args.rs b/tests/ui/needless_borrows_for_generic_args.rs index bc2db6774e96..6407900e86ba 100644 --- a/tests/ui/needless_borrows_for_generic_args.rs +++ b/tests/ui/needless_borrows_for_generic_args.rs @@ -2,7 +2,8 @@ #![allow( clippy::unnecessary_to_owned, clippy::unnecessary_literal_unwrap, - clippy::needless_borrow + clippy::needless_borrow, + clippy::needless_conversion_for_trait )] use core::ops::Deref; diff --git a/tests/ui/needless_borrows_for_generic_args.stderr b/tests/ui/needless_borrows_for_generic_args.stderr index 8829854e3073..b14f80f5d8e2 100644 --- a/tests/ui/needless_borrows_for_generic_args.stderr +++ b/tests/ui/needless_borrows_for_generic_args.stderr @@ -1,5 +1,5 @@ error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:16:37 + --> tests/ui/needless_borrows_for_generic_args.rs:17:37 | LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` @@ -8,61 +8,61 @@ LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); = help: to override `-D warnings` add `#[allow(clippy::needless_borrows_for_generic_args)]` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:18:33 + --> tests/ui/needless_borrows_for_generic_args.rs:19:33 | LL | let _ = Path::new(".").join(&&"."); | ^^^^^ help: change this to: `"."` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:23:33 + --> tests/ui/needless_borrows_for_generic_args.rs:24:33 | LL | let _ = std::fs::write("x", &"".to_string()); | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:39:27 + --> tests/ui/needless_borrows_for_generic_args.rs:40:27 | LL | deref_target_is_x(&X); | ^^ help: change this to: `X` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:53:30 + --> tests/ui/needless_borrows_for_generic_args.rs:54:30 | LL | multiple_constraints(&[[""]]); | ^^^^^^^ help: change this to: `[[""]]` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:74:49 + --> tests/ui/needless_borrows_for_generic_args.rs:75:49 | LL | multiple_constraints_normalizes_to_same(&X, X); | ^^ help: change this to: `X` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:133:24 + --> tests/ui/needless_borrows_for_generic_args.rs:134:24 | LL | takes_iter(&mut x) | ^^^^^^ help: change this to: `x` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:143:41 + --> tests/ui/needless_borrows_for_generic_args.rs:144:41 | 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:255:13 + --> tests/ui/needless_borrows_for_generic_args.rs:256:13 | LL | foo(&a); | ^^ help: change this to: `a` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:340:11 + --> tests/ui/needless_borrows_for_generic_args.rs:341:11 | 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:345:11 + --> tests/ui/needless_borrows_for_generic_args.rs:346:11 | LL | f(&"".to_owned()); // Lint | ^^^^^^^^^^^^^^ help: change this to: `"".to_owned()` diff --git a/tests/ui/needless_conversion_for_trait.fixed b/tests/ui/needless_conversion_for_trait.fixed new file mode 100644 index 000000000000..ab837043fbab --- /dev/null +++ b/tests/ui/needless_conversion_for_trait.fixed @@ -0,0 +1,109 @@ +#![allow(unused_imports, unused_parens, clippy::let_unit_value, clippy::manual_async_fn)] +#![feature(str_as_str)] + +use std::borrow::{Borrow, BorrowMut}; +use std::ffi::{OsStr, OsString}; +use std::io::Read; +use std::ops::{Deref, DerefMut}; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() { + let mut s = String::new(); + let mut vec = Vec::::new(); + let mut path_buf = PathBuf::from("x"); + let osstr = OsStr::new(""); + let osstring = OsString::new(); + let path = Path::new("x"); + let mut readable = Box::new(&[] as &[u8]); + + // inherent functions + + let _ = std::fs::write("x", (Box::new([]) as Box<[u8]>)); //~ needless_conversion_for_trait + let _ = std::fs::write("x", (&[] as &[u8])); //~ needless_conversion_for_trait + + let _ = is_empty(s.clone()); //~ needless_conversion_for_trait + let _ = is_empty(s.clone()); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", &s); //~ needless_conversion_for_trait + let _ = std::fs::write("x", &mut s); //~ needless_conversion_for_trait + let _ = std::fs::write("x", &s); //~ needless_conversion_for_trait + let _ = is_empty(s.clone()); //~ needless_conversion_for_trait + let _ = std::fs::write("x", s.clone()); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", &mut vec); //~ needless_conversion_for_trait + let _ = std::fs::write("x", &vec); //~ needless_conversion_for_trait + let _ = std::fs::write("x", vec); //~ needless_conversion_for_trait + + let _ = Command::new("ls").args(["-a", "-l"]); //~ needless_conversion_for_trait + let _ = Command::new("ls").args(["-a", "-l"]); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", ""); //~ needless_conversion_for_trait + let _ = std::fs::write("x", ""); //~ needless_conversion_for_trait + + let _ = os_str_or_bytes(osstr); //~ needless_conversion_for_trait + let _ = is_empty_os(osstring.clone()); //~ needless_conversion_for_trait + let _ = std::fs::write("x", ""); //~ needless_conversion_for_trait + let _ = std::fs::write(osstr, ""); //~ needless_conversion_for_trait + + let _ = std::fs::write(&osstring, ""); //~ needless_conversion_for_trait + let _ = is_empty_os(osstring.clone()); //~ needless_conversion_for_trait + let _ = os_string_or_bytes(osstring.clone()); //~ needless_conversion_for_trait + + let _ = std::fs::write(PathBuf::from("x"), ""); //~ needless_conversion_for_trait + let _ = std::fs::write(path, ""); //~ needless_conversion_for_trait + let _ = std::fs::write(PathBuf::from("x"), ""); //~ needless_conversion_for_trait + let _ = Command::new("ls").args(path); //~ needless_conversion_for_trait + let _ = std::fs::write("x", ""); //~ needless_conversion_for_trait + let _ = std::fs::write(path, ""); //~ needless_conversion_for_trait + + let _ = std::fs::write(&mut path_buf, ""); //~ needless_conversion_for_trait + let _ = std::fs::write(&path_buf, ""); //~ needless_conversion_for_trait + let _ = std::fs::write(path_buf.clone(), ""); //~ needless_conversion_for_trait + + // trait methods + + let _ = std::fs::write("x", ""); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", ""); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", &s); //~ needless_conversion_for_trait + + read(&mut readable); //~ needless_conversion_for_trait + read(&mut readable); //~ needless_conversion_for_trait + + read(&mut readable); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", ""); //~ needless_conversion_for_trait + let _ = std::fs::write("x", ""); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", &s); //~ needless_conversion_for_trait + + read(&mut readable); //~ needless_conversion_for_trait +} + +#[must_use] +fn is_empty> + PartialEq>(x: T) -> bool { + x == T::from(String::new().into_boxed_str()) +} + +#[must_use] +fn is_empty_os> + PartialEq>(x: T) -> bool { + x == T::from(OsString::new().into_boxed_os_str()) +} + +// This is a hack, but I can't readily think of a trait that both `&OsStr` and `&[u8]` implement. +// Reference: https://github.com/rust-lang/rust/issues/111544 +trait OsStrOrBytes {} +impl OsStrOrBytes for &OsStr {} +impl OsStrOrBytes for &[u8] {} +fn os_str_or_bytes(_: impl OsStrOrBytes) {} + +// Similar hack for `OsString` and `Vec`. +// Reference: https://github.com/rust-lang/rust/issues/111544 +trait OsStringOrBytes {} +impl OsStringOrBytes for OsString {} +impl OsStringOrBytes for Vec {} +fn os_string_or_bytes(_: impl OsStringOrBytes) {} + +fn read(_: impl Read) {} diff --git a/tests/ui/needless_conversion_for_trait.rs b/tests/ui/needless_conversion_for_trait.rs new file mode 100644 index 000000000000..d16ccf3fd7a2 --- /dev/null +++ b/tests/ui/needless_conversion_for_trait.rs @@ -0,0 +1,109 @@ +#![allow(unused_imports, unused_parens, clippy::let_unit_value, clippy::manual_async_fn)] +#![feature(str_as_str)] + +use std::borrow::{Borrow, BorrowMut}; +use std::ffi::{OsStr, OsString}; +use std::io::Read; +use std::ops::{Deref, DerefMut}; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() { + let mut s = String::new(); + let mut vec = Vec::::new(); + let mut path_buf = PathBuf::from("x"); + let osstr = OsStr::new(""); + let osstring = OsString::new(); + let path = Path::new("x"); + let mut readable = Box::new(&[] as &[u8]); + + // inherent functions + + let _ = std::fs::write("x", (Box::new([]) as Box<[u8]>).into_vec()); //~ needless_conversion_for_trait + let _ = std::fs::write("x", (&[] as &[u8]).to_vec()); //~ needless_conversion_for_trait + + let _ = is_empty(s.clone().into_boxed_str().into_boxed_bytes()); //~ needless_conversion_for_trait + let _ = is_empty(s.clone().into_boxed_str().into_string()); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", s.as_bytes()); //~ needless_conversion_for_trait + let _ = std::fs::write("x", s.as_mut_str()); //~ needless_conversion_for_trait + let _ = std::fs::write("x", s.as_str()); //~ needless_conversion_for_trait + let _ = is_empty(s.clone().into_boxed_str()); //~ needless_conversion_for_trait + let _ = std::fs::write("x", s.clone().into_bytes()); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", vec.as_mut_slice()); //~ needless_conversion_for_trait + let _ = std::fs::write("x", vec.as_slice()); //~ needless_conversion_for_trait + let _ = std::fs::write("x", vec.into_boxed_slice()); //~ needless_conversion_for_trait + + let _ = Command::new("ls").args(["-a", "-l"].iter()); //~ needless_conversion_for_trait + let _ = Command::new("ls").args(["-a", "-l"].iter_mut()); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", "".as_bytes()); //~ needless_conversion_for_trait + let _ = std::fs::write("x", "".as_str()); //~ needless_conversion_for_trait + + let _ = os_str_or_bytes(osstr.as_encoded_bytes()); //~ needless_conversion_for_trait + let _ = is_empty_os(osstring.clone().into_boxed_os_str().into_os_string()); //~ needless_conversion_for_trait + let _ = std::fs::write(OsStr::new("x"), ""); //~ needless_conversion_for_trait + let _ = std::fs::write(osstr.to_os_string(), ""); //~ needless_conversion_for_trait + + let _ = std::fs::write(osstring.as_os_str(), ""); //~ needless_conversion_for_trait + let _ = is_empty_os(osstring.clone().into_boxed_os_str()); //~ needless_conversion_for_trait + let _ = os_string_or_bytes(osstring.clone().into_encoded_bytes()); //~ needless_conversion_for_trait + + let _ = std::fs::write(PathBuf::from("x").as_mut_os_str(), ""); //~ needless_conversion_for_trait + let _ = std::fs::write(path.as_os_str(), ""); //~ needless_conversion_for_trait + let _ = std::fs::write(PathBuf::from("x").into_boxed_path().into_path_buf(), ""); //~ needless_conversion_for_trait + let _ = Command::new("ls").args(path.iter()); //~ needless_conversion_for_trait + let _ = std::fs::write(Path::new("x"), ""); //~ needless_conversion_for_trait + let _ = std::fs::write(path.to_path_buf(), ""); //~ needless_conversion_for_trait + + let _ = std::fs::write(path_buf.as_mut_os_string(), ""); //~ needless_conversion_for_trait + let _ = std::fs::write(path_buf.as_path(), ""); //~ needless_conversion_for_trait + let _ = std::fs::write(path_buf.clone().into_os_string(), ""); //~ needless_conversion_for_trait + + // trait methods + + let _ = std::fs::write("x", "".to_owned()); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", "".to_string()); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", <_ as Borrow>::borrow(&s)); //~ needless_conversion_for_trait + + read(<_ as BorrowMut<&[u8]>>::borrow_mut(&mut readable)); //~ needless_conversion_for_trait + read(<_ as BorrowMut>>::borrow_mut(&mut readable)); //~ needless_conversion_for_trait + + read(readable.as_mut()); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", <_ as AsRef<[u8]>>::as_ref("")); //~ needless_conversion_for_trait + let _ = std::fs::write("x", <_ as AsRef>::as_ref("")); //~ needless_conversion_for_trait + + let _ = std::fs::write("x", s.deref()); //~ needless_conversion_for_trait + + read(readable.deref_mut()); //~ needless_conversion_for_trait +} + +#[must_use] +fn is_empty> + PartialEq>(x: T) -> bool { + x == T::from(String::new().into_boxed_str()) +} + +#[must_use] +fn is_empty_os> + PartialEq>(x: T) -> bool { + x == T::from(OsString::new().into_boxed_os_str()) +} + +// This is a hack, but I can't readily think of a trait that both `&OsStr` and `&[u8]` implement. +// Reference: https://github.com/rust-lang/rust/issues/111544 +trait OsStrOrBytes {} +impl OsStrOrBytes for &OsStr {} +impl OsStrOrBytes for &[u8] {} +fn os_str_or_bytes(_: impl OsStrOrBytes) {} + +// Similar hack for `OsString` and `Vec`. +// Reference: https://github.com/rust-lang/rust/issues/111544 +trait OsStringOrBytes {} +impl OsStringOrBytes for OsString {} +impl OsStringOrBytes for Vec {} +fn os_string_or_bytes(_: impl OsStringOrBytes) {} + +fn read(_: impl Read) {} diff --git a/tests/ui/needless_conversion_for_trait.stderr b/tests/ui/needless_conversion_for_trait.stderr new file mode 100644 index 000000000000..c9e0f56a2db3 --- /dev/null +++ b/tests/ui/needless_conversion_for_trait.stderr @@ -0,0 +1,257 @@ +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:22:60 + | +LL | let _ = std::fs::write("x", (Box::new([]) as Box<[u8]>).into_vec()); + | ^^^^^^^^^^^ help: remove this + | + = note: `-D clippy::needless-conversion-for-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_conversion_for_trait)]` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:23:47 + | +LL | let _ = std::fs::write("x", (&[] as &[u8]).to_vec()); + | ^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:25:31 + | +LL | let _ = is_empty(s.clone().into_boxed_str().into_boxed_bytes()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:26:31 + | +LL | let _ = is_empty(s.clone().into_boxed_str().into_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:28:33 + | +LL | let _ = std::fs::write("x", s.as_bytes()); + | ^^^^^^^^^^^^ help: use: `&s` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:29:33 + | +LL | let _ = std::fs::write("x", s.as_mut_str()); + | ^^^^^^^^^^^^^^ help: use: `&mut s` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:30:33 + | +LL | let _ = std::fs::write("x", s.as_str()); + | ^^^^^^^^^^ help: use: `&s` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:31:31 + | +LL | let _ = is_empty(s.clone().into_boxed_str()); + | ^^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:32:42 + | +LL | let _ = std::fs::write("x", s.clone().into_bytes()); + | ^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:34:33 + | +LL | let _ = std::fs::write("x", vec.as_mut_slice()); + | ^^^^^^^^^^^^^^^^^^ help: use: `&mut vec` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:35:33 + | +LL | let _ = std::fs::write("x", vec.as_slice()); + | ^^^^^^^^^^^^^^ help: use: `&vec` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:36:36 + | +LL | let _ = std::fs::write("x", vec.into_boxed_slice()); + | ^^^^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:38:49 + | +LL | let _ = Command::new("ls").args(["-a", "-l"].iter()); + | ^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:39:49 + | +LL | let _ = Command::new("ls").args(["-a", "-l"].iter_mut()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:41:35 + | +LL | let _ = std::fs::write("x", "".as_bytes()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:42:35 + | +LL | let _ = std::fs::write("x", "".as_str()); + | ^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:44:34 + | +LL | let _ = os_str_or_bytes(osstr.as_encoded_bytes()); + | ^^^^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:45:41 + | +LL | let _ = is_empty_os(osstring.clone().into_boxed_os_str().into_os_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this + +error: the inner argument implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:46:28 + | +LL | let _ = std::fs::write(OsStr::new("x"), ""); + | ^^^^^^^^^^^^^^^ help: use: `"x"` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:47:33 + | +LL | let _ = std::fs::write(osstr.to_os_string(), ""); + | ^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:49:28 + | +LL | let _ = std::fs::write(osstring.as_os_str(), ""); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `&osstring` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:50:41 + | +LL | let _ = is_empty_os(osstring.clone().into_boxed_os_str()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:51:48 + | +LL | let _ = os_string_or_bytes(osstring.clone().into_encoded_bytes()); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:53:46 + | +LL | let _ = std::fs::write(PathBuf::from("x").as_mut_os_str(), ""); + | ^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:54:32 + | +LL | let _ = std::fs::write(path.as_os_str(), ""); + | ^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:55:46 + | +LL | let _ = std::fs::write(PathBuf::from("x").into_boxed_path().into_path_buf(), ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:56:41 + | +LL | let _ = Command::new("ls").args(path.iter()); + | ^^^^^^^ help: remove this + +error: the inner argument implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:57:28 + | +LL | let _ = std::fs::write(Path::new("x"), ""); + | ^^^^^^^^^^^^^^ help: use: `"x"` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:58:32 + | +LL | let _ = std::fs::write(path.to_path_buf(), ""); + | ^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:60:28 + | +LL | let _ = std::fs::write(path_buf.as_mut_os_string(), ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `&mut path_buf` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:61:28 + | +LL | let _ = std::fs::write(path_buf.as_path(), ""); + | ^^^^^^^^^^^^^^^^^^ help: use: `&path_buf` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:62:44 + | +LL | let _ = std::fs::write(path_buf.clone().into_os_string(), ""); + | ^^^^^^^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:66:35 + | +LL | let _ = std::fs::write("x", "".to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:68:35 + | +LL | let _ = std::fs::write("x", "".to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: the inner argument implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:70:33 + | +LL | let _ = std::fs::write("x", <_ as Borrow>::borrow(&s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `&s` + +error: the inner argument implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:72:10 + | +LL | read(<_ as BorrowMut<&[u8]>>::borrow_mut(&mut readable)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `&mut readable` + +error: the inner argument implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:73:10 + | +LL | read(<_ as BorrowMut>>::borrow_mut(&mut readable)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `&mut readable` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:75:10 + | +LL | read(readable.as_mut()); + | ^^^^^^^^^^^^^^^^^ help: use: `&mut readable` + +error: the inner argument implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:77:33 + | +LL | let _ = std::fs::write("x", <_ as AsRef<[u8]>>::as_ref("")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `""` + +error: the inner argument implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:78:33 + | +LL | let _ = std::fs::write("x", <_ as AsRef>::as_ref("")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `""` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:80:33 + | +LL | let _ = std::fs::write("x", s.deref()); + | ^^^^^^^^^ help: use: `&s` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait.rs:82:10 + | +LL | read(readable.deref_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `&mut readable` + +error: aborting due to 42 previous errors + diff --git a/tests/ui/needless_conversion_for_trait_unnecessary_to_owned.fixed b/tests/ui/needless_conversion_for_trait_unnecessary_to_owned.fixed new file mode 100644 index 000000000000..00bba9eec74c --- /dev/null +++ b/tests/ui/needless_conversion_for_trait_unnecessary_to_owned.fixed @@ -0,0 +1,163 @@ +#![allow(clippy::needless_borrows_for_generic_args)] +#![warn(clippy::needless_conversion_for_trait)] + +use std::ffi::{CStr, OsStr}; +use std::ops::Deref; + +#[derive(Clone)] +struct X(String); + +impl Deref for X { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0.as_bytes() + } +} + +impl AsRef for X { + fn as_ref(&self) -> &str { + self.0.as_str() + } +} + +#[allow(clippy::to_string_trait_impl)] +impl ToString for X { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl X { + fn join(&self, other: impl AsRef) -> Self { + let mut s = self.0.clone(); + s.push_str(other.as_ref()); + Self(s) + } +} + +fn main() { + let c_str = CStr::from_bytes_with_nul(&[0]).unwrap(); + let os_str = OsStr::new("x"); + let path = std::path::Path::new("x"); + let s = "x"; + let array = ["x"]; + let array_ref = &["x"]; + let slice = &["x"][..]; + let x = X(String::from("x")); + let x_ref = &x; + + require_deref_c_str(c_str); + //~^ needless_conversion_for_trait + require_deref_os_str(os_str); + //~^ needless_conversion_for_trait + require_deref_path(path); + //~^ needless_conversion_for_trait + require_deref_str(s); + //~^ needless_conversion_for_trait + require_deref_slice(slice); + //~^ needless_conversion_for_trait + + require_impl_deref_c_str(c_str); + //~^ needless_conversion_for_trait + require_impl_deref_os_str(os_str); + //~^ needless_conversion_for_trait + require_impl_deref_path(path); + //~^ needless_conversion_for_trait + require_impl_deref_str(s); + //~^ needless_conversion_for_trait + require_impl_deref_slice(slice); + //~^ needless_conversion_for_trait + + require_deref_str_slice(s, slice); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_deref_slice_str(slice, s); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + + require_as_ref_c_str(c_str); + //~^ needless_conversion_for_trait + require_as_ref_os_str(os_str); + //~^ needless_conversion_for_trait + require_as_ref_path(path); + //~^ needless_conversion_for_trait + require_as_ref_str(s); + //~^ needless_conversion_for_trait + require_as_ref_str(&x); + //~^ needless_conversion_for_trait + require_as_ref_slice(array); + //~^ needless_conversion_for_trait + require_as_ref_slice(array_ref); + //~^ needless_conversion_for_trait + require_as_ref_slice(slice); + //~^ needless_conversion_for_trait + + require_impl_as_ref_c_str(c_str); + //~^ needless_conversion_for_trait + require_impl_as_ref_os_str(os_str); + //~^ needless_conversion_for_trait + require_impl_as_ref_path(path); + //~^ needless_conversion_for_trait + require_impl_as_ref_str(s); + //~^ needless_conversion_for_trait + require_impl_as_ref_str(&x); + //~^ needless_conversion_for_trait + require_impl_as_ref_slice(array); + //~^ needless_conversion_for_trait + require_impl_as_ref_slice(array_ref); + //~^ needless_conversion_for_trait + require_impl_as_ref_slice(slice); + //~^ needless_conversion_for_trait + + require_as_ref_str_slice(s, array); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_str_slice(s, array_ref); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_str_slice(s, slice); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_slice_str(array, s); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_slice_str(array_ref, s); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_slice_str(slice, s); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + + let _ = x.join(x_ref); + //~^ needless_conversion_for_trait +} + +fn require_deref_c_str>(_: T) {} +fn require_deref_os_str>(_: T) {} +fn require_deref_path>(_: T) {} +fn require_deref_str>(_: T) {} +fn require_deref_slice>(_: U) {} + +fn require_impl_deref_c_str(_: impl Deref) {} +fn require_impl_deref_os_str(_: impl Deref) {} +fn require_impl_deref_path(_: impl Deref) {} +fn require_impl_deref_str(_: impl Deref) {} +fn require_impl_deref_slice(_: impl Deref) {} + +fn require_deref_str_slice, U, V: Deref>(_: T, _: V) {} +fn require_deref_slice_str, V: Deref>(_: U, _: V) {} + +fn require_as_ref_c_str>(_: T) {} +fn require_as_ref_os_str>(_: T) {} +fn require_as_ref_path>(_: T) {} +fn require_as_ref_str>(_: T) {} +fn require_as_ref_slice>(_: U) {} + +fn require_impl_as_ref_c_str(_: impl AsRef) {} +fn require_impl_as_ref_os_str(_: impl AsRef) {} +fn require_impl_as_ref_path(_: impl AsRef) {} +fn require_impl_as_ref_str(_: impl AsRef) {} +fn require_impl_as_ref_slice(_: impl AsRef<[T]>) {} + +fn require_as_ref_str_slice, U, V: AsRef<[U]>>(_: T, _: V) {} +fn require_as_ref_slice_str, V: AsRef>(_: U, _: V) {} diff --git a/tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs b/tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs new file mode 100644 index 000000000000..a00b3927799c --- /dev/null +++ b/tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs @@ -0,0 +1,163 @@ +#![allow(clippy::needless_borrows_for_generic_args)] +#![warn(clippy::needless_conversion_for_trait)] + +use std::ffi::{CStr, OsStr}; +use std::ops::Deref; + +#[derive(Clone)] +struct X(String); + +impl Deref for X { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0.as_bytes() + } +} + +impl AsRef for X { + fn as_ref(&self) -> &str { + self.0.as_str() + } +} + +#[allow(clippy::to_string_trait_impl)] +impl ToString for X { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl X { + fn join(&self, other: impl AsRef) -> Self { + let mut s = self.0.clone(); + s.push_str(other.as_ref()); + Self(s) + } +} + +fn main() { + let c_str = CStr::from_bytes_with_nul(&[0]).unwrap(); + let os_str = OsStr::new("x"); + let path = std::path::Path::new("x"); + let s = "x"; + let array = ["x"]; + let array_ref = &["x"]; + let slice = &["x"][..]; + let x = X(String::from("x")); + let x_ref = &x; + + require_deref_c_str(c_str.to_owned()); + //~^ needless_conversion_for_trait + require_deref_os_str(os_str.to_owned()); + //~^ needless_conversion_for_trait + require_deref_path(path.to_owned()); + //~^ needless_conversion_for_trait + require_deref_str(s.to_owned()); + //~^ needless_conversion_for_trait + require_deref_slice(slice.to_owned()); + //~^ needless_conversion_for_trait + + require_impl_deref_c_str(c_str.to_owned()); + //~^ needless_conversion_for_trait + require_impl_deref_os_str(os_str.to_owned()); + //~^ needless_conversion_for_trait + require_impl_deref_path(path.to_owned()); + //~^ needless_conversion_for_trait + require_impl_deref_str(s.to_owned()); + //~^ needless_conversion_for_trait + require_impl_deref_slice(slice.to_owned()); + //~^ needless_conversion_for_trait + + require_deref_str_slice(s.to_owned(), slice.to_owned()); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_deref_slice_str(slice.to_owned(), s.to_owned()); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + + require_as_ref_c_str(c_str.to_owned()); + //~^ needless_conversion_for_trait + require_as_ref_os_str(os_str.to_owned()); + //~^ needless_conversion_for_trait + require_as_ref_path(path.to_owned()); + //~^ needless_conversion_for_trait + require_as_ref_str(s.to_owned()); + //~^ needless_conversion_for_trait + require_as_ref_str(x.to_owned()); + //~^ needless_conversion_for_trait + require_as_ref_slice(array.to_owned()); + //~^ needless_conversion_for_trait + require_as_ref_slice(array_ref.to_owned()); + //~^ needless_conversion_for_trait + require_as_ref_slice(slice.to_owned()); + //~^ needless_conversion_for_trait + + require_impl_as_ref_c_str(c_str.to_owned()); + //~^ needless_conversion_for_trait + require_impl_as_ref_os_str(os_str.to_owned()); + //~^ needless_conversion_for_trait + require_impl_as_ref_path(path.to_owned()); + //~^ needless_conversion_for_trait + require_impl_as_ref_str(s.to_owned()); + //~^ needless_conversion_for_trait + require_impl_as_ref_str(x.to_owned()); + //~^ needless_conversion_for_trait + require_impl_as_ref_slice(array.to_owned()); + //~^ needless_conversion_for_trait + require_impl_as_ref_slice(array_ref.to_owned()); + //~^ needless_conversion_for_trait + require_impl_as_ref_slice(slice.to_owned()); + //~^ needless_conversion_for_trait + + require_as_ref_str_slice(s.to_owned(), array.to_owned()); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_str_slice(s.to_owned(), slice.to_owned()); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_slice_str(array.to_owned(), s.to_owned()); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + require_as_ref_slice_str(slice.to_owned(), s.to_owned()); + //~^ needless_conversion_for_trait + //~| needless_conversion_for_trait + + let _ = x.join(&x_ref.to_string()); + //~^ needless_conversion_for_trait +} + +fn require_deref_c_str>(_: T) {} +fn require_deref_os_str>(_: T) {} +fn require_deref_path>(_: T) {} +fn require_deref_str>(_: T) {} +fn require_deref_slice>(_: U) {} + +fn require_impl_deref_c_str(_: impl Deref) {} +fn require_impl_deref_os_str(_: impl Deref) {} +fn require_impl_deref_path(_: impl Deref) {} +fn require_impl_deref_str(_: impl Deref) {} +fn require_impl_deref_slice(_: impl Deref) {} + +fn require_deref_str_slice, U, V: Deref>(_: T, _: V) {} +fn require_deref_slice_str, V: Deref>(_: U, _: V) {} + +fn require_as_ref_c_str>(_: T) {} +fn require_as_ref_os_str>(_: T) {} +fn require_as_ref_path>(_: T) {} +fn require_as_ref_str>(_: T) {} +fn require_as_ref_slice>(_: U) {} + +fn require_impl_as_ref_c_str(_: impl AsRef) {} +fn require_impl_as_ref_os_str(_: impl AsRef) {} +fn require_impl_as_ref_path(_: impl AsRef) {} +fn require_impl_as_ref_str(_: impl AsRef) {} +fn require_impl_as_ref_slice(_: impl AsRef<[T]>) {} + +fn require_as_ref_str_slice, U, V: AsRef<[U]>>(_: T, _: V) {} +fn require_as_ref_slice_str, V: AsRef>(_: U, _: V) {} diff --git a/tests/ui/needless_conversion_for_trait_unnecessary_to_owned.stderr b/tests/ui/needless_conversion_for_trait_unnecessary_to_owned.stderr new file mode 100644 index 000000000000..47693d003c5c --- /dev/null +++ b/tests/ui/needless_conversion_for_trait_unnecessary_to_owned.stderr @@ -0,0 +1,263 @@ +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:49:30 + | +LL | require_deref_c_str(c_str.to_owned()); + | ^^^^^^^^^^^ help: remove this + | + = note: `-D clippy::needless-conversion-for-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_conversion_for_trait)]` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:51:32 + | +LL | require_deref_os_str(os_str.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:53:28 + | +LL | require_deref_path(path.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:55:24 + | +LL | require_deref_str(s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:57:30 + | +LL | require_deref_slice(slice.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:60:35 + | +LL | require_impl_deref_c_str(c_str.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:62:37 + | +LL | require_impl_deref_os_str(os_str.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:64:33 + | +LL | require_impl_deref_path(path.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:66:29 + | +LL | require_impl_deref_str(s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:68:35 + | +LL | require_impl_deref_slice(slice.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:71:30 + | +LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:71:48 + | +LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:74:34 + | +LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:74:48 + | +LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:78:31 + | +LL | require_as_ref_c_str(c_str.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:80:33 + | +LL | require_as_ref_os_str(os_str.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:82:29 + | +LL | require_as_ref_path(path.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:84:25 + | +LL | require_as_ref_str(s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:86:24 + | +LL | require_as_ref_str(x.to_owned()); + | ^^^^^^^^^^^^ help: use: `&x` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:88:31 + | +LL | require_as_ref_slice(array.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:90:35 + | +LL | require_as_ref_slice(array_ref.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:92:31 + | +LL | require_as_ref_slice(slice.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:95:36 + | +LL | require_impl_as_ref_c_str(c_str.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:97:38 + | +LL | require_impl_as_ref_os_str(os_str.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:99:34 + | +LL | require_impl_as_ref_path(path.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:101:30 + | +LL | require_impl_as_ref_str(s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:103:29 + | +LL | require_impl_as_ref_str(x.to_owned()); + | ^^^^^^^^^^^^ help: use: `&x` + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:105:36 + | +LL | require_impl_as_ref_slice(array.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:107:40 + | +LL | require_impl_as_ref_slice(array_ref.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:109:36 + | +LL | require_impl_as_ref_slice(slice.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:112:31 + | +LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:112:49 + | +LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:115:31 + | +LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:115:53 + | +LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:118:31 + | +LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:118:49 + | +LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:121:35 + | +LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:121:49 + | +LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:124:39 + | +LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:124:53 + | +LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:127:35 + | +LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:127:49 + | +LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); + | ^^^^^^^^^^^ help: remove this + +error: the receiver implements the required traits + --> tests/ui/needless_conversion_for_trait_unnecessary_to_owned.rs:131:20 + | +LL | let _ = x.join(&x_ref.to_string()); + | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` + +error: aborting due to 43 previous errors + diff --git a/tests/ui/needless_conversion_for_trait_vec.rs b/tests/ui/needless_conversion_for_trait_vec.rs new file mode 100644 index 000000000000..ad33d4c1592d --- /dev/null +++ b/tests/ui/needless_conversion_for_trait_vec.rs @@ -0,0 +1,3 @@ +fn main() { + let _ = std::fs::write("x", vec![0]); //~ needless_conversion_for_trait +} diff --git a/tests/ui/needless_conversion_for_trait_vec.stderr b/tests/ui/needless_conversion_for_trait_vec.stderr new file mode 100644 index 000000000000..6fbe98525747 --- /dev/null +++ b/tests/ui/needless_conversion_for_trait_vec.stderr @@ -0,0 +1,12 @@ +error: the inner argument implements the required traits + --> tests/ui/needless_conversion_for_trait_vec.rs:2:33 + | +LL | let _ = std::fs::write("x", vec![0]); + | ^^^^^^^ + | + = help: use the macro arguments directly + = note: `-D clippy::needless-conversion-for-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_conversion_for_trait)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 4208efad6774..5c6b4937a60e 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -4,6 +4,7 @@ #![allow(unused_must_use, unused_variables)] #![allow( clippy::let_unit_value, + clippy::needless_conversion_for_trait, clippy::needless_question_mark, clippy::never_loop, clippy::no_effect, diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 0dcfb02b9fa0..99eea30c3b02 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> tests/ui/unit_arg.rs:63:5 + --> tests/ui/unit_arg.rs:64:5 | LL | / foo({ LL | | @@ -24,7 +24,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:67:5 + --> tests/ui/unit_arg.rs:68:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:69:5 + --> tests/ui/unit_arg.rs:70:5 | LL | / foo({ LL | | @@ -61,7 +61,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:75:5 + --> tests/ui/unit_arg.rs:76:5 | LL | / b.bar({ LL | | @@ -84,7 +84,7 @@ LL ~ b.bar(()); | error: passing unit values to a function - --> tests/ui/unit_arg.rs:79:5 + --> tests/ui/unit_arg.rs:80:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> tests/ui/unit_arg.rs:81:5 + --> tests/ui/unit_arg.rs:82:5 | LL | / taking_multiple_units(foo(0), { LL | | @@ -123,7 +123,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> tests/ui/unit_arg.rs:86:5 + --> tests/ui/unit_arg.rs:87:5 | LL | / taking_multiple_units( LL | | @@ -162,7 +162,7 @@ LL ~ ); | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:98:13 + --> tests/ui/unit_arg.rs:99:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -176,7 +176,7 @@ LL ~ }); | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:102:5 + --> tests/ui/unit_arg.rs:103:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -188,7 +188,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:140:5 + --> tests/ui/unit_arg.rs:141:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ @@ -200,21 +200,21 @@ LL + Some(()) | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:171:5 + --> tests/ui/unit_arg.rs:172:5 | LL | fn_take_unit(mac!(def)); | ^^^^^^^^^^^^^^^^^^^^^^^ | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:173:5 + --> tests/ui/unit_arg.rs:174:5 | LL | fn_take_unit(mac!(func Default::default)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:175:5 + --> tests/ui/unit_arg.rs:176:5 | LL | fn_take_unit(mac!(nonempty_block Default::default())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 316eac0b58b7..f60238037f47 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -107,91 +107,6 @@ fn main() { //~^ unnecessary_to_owned require_x(&x_ref.to_owned()); // No longer flagged because of #8759. - require_deref_c_str(c_str); - //~^ unnecessary_to_owned - require_deref_os_str(os_str); - //~^ unnecessary_to_owned - require_deref_path(path); - //~^ unnecessary_to_owned - require_deref_str(s); - //~^ unnecessary_to_owned - require_deref_slice(slice); - //~^ unnecessary_to_owned - - require_impl_deref_c_str(c_str); - //~^ unnecessary_to_owned - require_impl_deref_os_str(os_str); - //~^ unnecessary_to_owned - require_impl_deref_path(path); - //~^ unnecessary_to_owned - require_impl_deref_str(s); - //~^ unnecessary_to_owned - require_impl_deref_slice(slice); - //~^ unnecessary_to_owned - - require_deref_str_slice(s, slice); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_deref_slice_str(slice, s); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - - require_as_ref_c_str(c_str); - //~^ unnecessary_to_owned - require_as_ref_os_str(os_str); - //~^ unnecessary_to_owned - require_as_ref_path(path); - //~^ unnecessary_to_owned - require_as_ref_str(s); - //~^ unnecessary_to_owned - require_as_ref_str(&x); - //~^ unnecessary_to_owned - require_as_ref_slice(array); - //~^ unnecessary_to_owned - require_as_ref_slice(array_ref); - //~^ unnecessary_to_owned - require_as_ref_slice(slice); - //~^ unnecessary_to_owned - - require_impl_as_ref_c_str(c_str); - //~^ unnecessary_to_owned - require_impl_as_ref_os_str(os_str); - //~^ unnecessary_to_owned - require_impl_as_ref_path(path); - //~^ unnecessary_to_owned - require_impl_as_ref_str(s); - //~^ unnecessary_to_owned - require_impl_as_ref_str(&x); - //~^ unnecessary_to_owned - require_impl_as_ref_slice(array); - //~^ unnecessary_to_owned - require_impl_as_ref_slice(array_ref); - //~^ unnecessary_to_owned - require_impl_as_ref_slice(slice); - //~^ unnecessary_to_owned - - require_as_ref_str_slice(s, array); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_str_slice(s, array_ref); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_str_slice(s, slice); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_slice_str(array, s); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_slice_str(array_ref, s); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_slice_str(slice, s); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - - let _ = x.join(x_ref); - //~^ unnecessary_to_owned - let _ = slice.iter().copied(); //~^ unnecessary_to_owned let _ = slice.iter().copied(); diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index f2dbd1db3c9f..d3b724cb781c 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -107,91 +107,6 @@ fn main() { //~^ unnecessary_to_owned require_x(&x_ref.to_owned()); // No longer flagged because of #8759. - require_deref_c_str(c_str.to_owned()); - //~^ unnecessary_to_owned - require_deref_os_str(os_str.to_owned()); - //~^ unnecessary_to_owned - require_deref_path(path.to_owned()); - //~^ unnecessary_to_owned - require_deref_str(s.to_owned()); - //~^ unnecessary_to_owned - require_deref_slice(slice.to_owned()); - //~^ unnecessary_to_owned - - require_impl_deref_c_str(c_str.to_owned()); - //~^ unnecessary_to_owned - require_impl_deref_os_str(os_str.to_owned()); - //~^ unnecessary_to_owned - require_impl_deref_path(path.to_owned()); - //~^ unnecessary_to_owned - require_impl_deref_str(s.to_owned()); - //~^ unnecessary_to_owned - require_impl_deref_slice(slice.to_owned()); - //~^ unnecessary_to_owned - - require_deref_str_slice(s.to_owned(), slice.to_owned()); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_deref_slice_str(slice.to_owned(), s.to_owned()); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - - require_as_ref_c_str(c_str.to_owned()); - //~^ unnecessary_to_owned - require_as_ref_os_str(os_str.to_owned()); - //~^ unnecessary_to_owned - require_as_ref_path(path.to_owned()); - //~^ unnecessary_to_owned - require_as_ref_str(s.to_owned()); - //~^ unnecessary_to_owned - require_as_ref_str(x.to_owned()); - //~^ unnecessary_to_owned - require_as_ref_slice(array.to_owned()); - //~^ unnecessary_to_owned - require_as_ref_slice(array_ref.to_owned()); - //~^ unnecessary_to_owned - require_as_ref_slice(slice.to_owned()); - //~^ unnecessary_to_owned - - require_impl_as_ref_c_str(c_str.to_owned()); - //~^ unnecessary_to_owned - require_impl_as_ref_os_str(os_str.to_owned()); - //~^ unnecessary_to_owned - require_impl_as_ref_path(path.to_owned()); - //~^ unnecessary_to_owned - require_impl_as_ref_str(s.to_owned()); - //~^ unnecessary_to_owned - require_impl_as_ref_str(x.to_owned()); - //~^ unnecessary_to_owned - require_impl_as_ref_slice(array.to_owned()); - //~^ unnecessary_to_owned - require_impl_as_ref_slice(array_ref.to_owned()); - //~^ unnecessary_to_owned - require_impl_as_ref_slice(slice.to_owned()); - //~^ unnecessary_to_owned - - require_as_ref_str_slice(s.to_owned(), array.to_owned()); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_str_slice(s.to_owned(), slice.to_owned()); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_slice_str(array.to_owned(), s.to_owned()); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - require_as_ref_slice_str(slice.to_owned(), s.to_owned()); - //~^ unnecessary_to_owned - //~| unnecessary_to_owned - - let _ = x.join(&x_ref.to_string()); - //~^ unnecessary_to_owned - let _ = slice.to_vec().into_iter(); //~^ unnecessary_to_owned let _ = slice.to_owned().into_iter(); diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index 6c52be839301..30b3cfc83075 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:218:64 + --> tests/ui/unnecessary_to_owned.rs:133:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:218:20 + --> tests/ui/unnecessary_to_owned.rs:133:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,49 +13,49 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:220:40 + --> tests/ui/unnecessary_to_owned.rs:135:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:220:21 + --> tests/ui/unnecessary_to_owned.rs:135:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:222:48 + --> tests/ui/unnecessary_to_owned.rs:137:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:222:19 + --> tests/ui/unnecessary_to_owned.rs:137:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:224:35 + --> tests/ui/unnecessary_to_owned.rs:139:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:224:18 + --> tests/ui/unnecessary_to_owned.rs:139:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:226:39 + --> tests/ui/unnecessary_to_owned.rs:141:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:226:20 + --> tests/ui/unnecessary_to_owned.rs:141:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ @@ -171,290 +171,32 @@ error: unnecessary use of `into_owned` LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:110:25 - | -LL | require_deref_c_str(c_str.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `c_str` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:112:26 - | -LL | require_deref_os_str(os_str.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `os_str` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:114:24 - | -LL | require_deref_path(path.to_owned()); - | ^^^^^^^^^^^^^^^ help: use: `path` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:116:23 - | -LL | require_deref_str(s.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:118:25 - | -LL | require_deref_slice(slice.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `slice` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:121:30 - | -LL | require_impl_deref_c_str(c_str.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `c_str` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:123:31 - | -LL | require_impl_deref_os_str(os_str.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `os_str` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:125:29 - | -LL | require_impl_deref_path(path.to_owned()); - | ^^^^^^^^^^^^^^^ help: use: `path` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:127:28 - | -LL | require_impl_deref_str(s.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:129:30 - | -LL | require_impl_deref_slice(slice.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `slice` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:132:29 - | -LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:132:43 - | -LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `slice` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:135:29 - | -LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `slice` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:135:47 - | -LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:139:26 - | -LL | require_as_ref_c_str(c_str.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `c_str` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:141:27 - | -LL | require_as_ref_os_str(os_str.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `os_str` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:143:25 - | -LL | require_as_ref_path(path.to_owned()); - | ^^^^^^^^^^^^^^^ help: use: `path` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:145:24 - | -LL | require_as_ref_str(s.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:147:24 - | -LL | require_as_ref_str(x.to_owned()); - | ^^^^^^^^^^^^ help: use: `&x` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:149:26 - | -LL | require_as_ref_slice(array.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `array` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:151:26 - | -LL | require_as_ref_slice(array_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:153:26 - | -LL | require_as_ref_slice(slice.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `slice` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:156:31 - | -LL | require_impl_as_ref_c_str(c_str.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `c_str` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:158:32 - | -LL | require_impl_as_ref_os_str(os_str.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `os_str` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:160:30 - | -LL | require_impl_as_ref_path(path.to_owned()); - | ^^^^^^^^^^^^^^^ help: use: `path` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:162:29 - | -LL | require_impl_as_ref_str(s.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:164:29 - | -LL | require_impl_as_ref_str(x.to_owned()); - | ^^^^^^^^^^^^ help: use: `&x` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:166:31 - | -LL | require_impl_as_ref_slice(array.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `array` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:168:31 - | -LL | require_impl_as_ref_slice(array_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:170:31 - | -LL | require_impl_as_ref_slice(slice.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `slice` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:173:30 - | -LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:173:44 - | -LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `array` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:176:30 - | -LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:176:44 - | -LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:179:30 - | -LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:179:44 - | -LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `slice` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:182:30 - | -LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `array` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:182:48 - | -LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:185:30 - | -LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); - | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:185:52 - | -LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:188:30 - | -LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `slice` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:188:48 - | -LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:192:20 - | -LL | let _ = x.join(&x_ref.to_string()); - | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` - error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:195:13 + --> tests/ui/unnecessary_to_owned.rs:110:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:197:13 + --> tests/ui/unnecessary_to_owned.rs:112:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:200:13 + --> tests/ui/unnecessary_to_owned.rs:115:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:202:13 + --> tests/ui/unnecessary_to_owned.rs:117:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:230:26 + --> tests/ui/unnecessary_to_owned.rs:145:26 | LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -466,7 +208,7 @@ LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:232:26 + --> tests/ui/unnecessary_to_owned.rs:147:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -478,7 +220,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:234:26 + --> tests/ui/unnecessary_to_owned.rs:149:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +232,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:292:14 + --> tests/ui/unnecessary_to_owned.rs:207:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -503,52 +245,52 @@ LL ~ let path = match get_file_path(t) { | error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:358:24 + --> tests/ui/unnecessary_to_owned.rs:273:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:468:12 + --> tests/ui/unnecessary_to_owned.rs:383:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:612:37 + --> tests/ui/unnecessary_to_owned.rs:527:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:623:18 + --> tests/ui/unnecessary_to_owned.rs:538:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:628:14 + --> tests/ui/unnecessary_to_owned.rs:543:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:630:14 + --> tests/ui/unnecessary_to_owned.rs:545:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:636:14 + --> tests/ui/unnecessary_to_owned.rs:551:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:638:14 + --> tests/ui/unnecessary_to_owned.rs:553:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` -error: aborting due to 82 previous errors +error: aborting due to 39 previous errors diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index ad30c94f3478..eb1f44aa3688 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,5 +1,9 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps)] +#![allow( + clippy::needless_conversion_for_trait, + clippy::needless_if, + clippy::unnecessary_wraps +)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 505afb340009..ab8a2ef95900 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,5 +1,9 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps)] +#![allow( + clippy::needless_conversion_for_trait, + clippy::needless_if, + clippy::unnecessary_wraps +)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 3bfaf1411c2c..c9cd1972ca51 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:9:13 + --> tests/ui/useless_conversion.rs:13:13 | LL | let _ = T::from(val); | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` @@ -11,115 +11,115 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:11:5 + --> tests/ui/useless_conversion.rs:15:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` error: useless conversion to the same type: `i32` - --> tests/ui/useless_conversion.rs:24:22 + --> tests/ui/useless_conversion.rs:28:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:55:22 + --> tests/ui/useless_conversion.rs:59:22 | LL | if Some("ok") == lines.into_iter().next() {} | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:61:21 + --> tests/ui/useless_conversion.rs:65:21 | LL | let mut lines = text.lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:68:22 + --> tests/ui/useless_conversion.rs:72:22 | LL | if Some("ok") == text.lines().into_iter().next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:75:13 + --> tests/ui/useless_conversion.rs:79:13 | LL | let _ = NUMBERS.into_iter().next(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:81:17 + --> tests/ui/useless_conversion.rs:85:17 | LL | let mut n = NUMBERS.into_iter(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:144:21 + --> tests/ui/useless_conversion.rs:148:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:146:21 + --> tests/ui/useless_conversion.rs:150:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:148:13 + --> tests/ui/useless_conversion.rs:152:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:150:13 + --> tests/ui/useless_conversion.rs:154:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:152:13 + --> tests/ui/useless_conversion.rs:156:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> tests/ui/useless_conversion.rs:154:13 + --> tests/ui/useless_conversion.rs:158:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:156:21 + --> tests/ui/useless_conversion.rs:160:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> tests/ui/useless_conversion.rs:162:13 + --> tests/ui/useless_conversion.rs:166:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:169:23 + --> tests/ui/useless_conversion.rs:173:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:172:13 + --> tests/ui/useless_conversion.rs:176:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter>` - --> tests/ui/useless_conversion.rs:175:13 + --> tests/ui/useless_conversion.rs:179:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:208:7 + --> tests/ui/useless_conversion.rs:212:7 | LL | b(vec![1, 2].into_iter()); | ^^^^^^^^^^------------ @@ -127,13 +127,13 @@ LL | b(vec![1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:198:13 + --> tests/ui/useless_conversion.rs:202:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:210:7 + --> tests/ui/useless_conversion.rs:214:7 | LL | c(vec![1, 2].into_iter()); | ^^^^^^^^^^------------ @@ -141,13 +141,13 @@ LL | c(vec![1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:199:18 + --> tests/ui/useless_conversion.rs:203:18 | LL | fn c(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:212:7 + --> tests/ui/useless_conversion.rs:216:7 | LL | d(vec![1, 2].into_iter()); | ^^^^^^^^^^------------ @@ -155,13 +155,13 @@ LL | d(vec![1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:202:12 + --> tests/ui/useless_conversion.rs:206:12 | LL | T: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:216:7 + --> tests/ui/useless_conversion.rs:220:7 | LL | b(vec![1, 2].into_iter().into_iter()); | ^^^^^^^^^^------------------------ @@ -169,13 +169,13 @@ LL | b(vec![1, 2].into_iter().into_iter()); | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:198:13 + --> tests/ui/useless_conversion.rs:202:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:218:7 + --> tests/ui/useless_conversion.rs:222:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | ^^^^^^^^^^------------------------------------ @@ -183,13 +183,13 @@ LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:198:13 + --> tests/ui/useless_conversion.rs:202:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:265:24 + --> tests/ui/useless_conversion.rs:269:24 | LL | foo2::([1, 2, 3].into_iter()); | ^^^^^^^^^------------ @@ -197,13 +197,13 @@ LL | foo2::([1, 2, 3].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:244:12 + --> tests/ui/useless_conversion.rs:248:12 | LL | I: IntoIterator + Helper, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:274:14 + --> tests/ui/useless_conversion.rs:278:14 | LL | foo3([1, 2, 3].into_iter()); | ^^^^^^^^^------------ @@ -211,13 +211,13 @@ LL | foo3([1, 2, 3].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:253:12 + --> tests/ui/useless_conversion.rs:257:12 | LL | I: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:284:16 + --> tests/ui/useless_conversion.rs:288:16 | LL | S1.foo([1, 2].into_iter()); | ^^^^^^------------ @@ -225,13 +225,13 @@ LL | S1.foo([1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:281:27 + --> tests/ui/useless_conversion.rs:285:27 | LL | pub fn foo(&self, _: I) {} | ^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:304:44 + --> tests/ui/useless_conversion.rs:308:44 | LL | v0.into_iter().interleave_shortest(v1.into_iter()); | ^^------------ @@ -239,67 +239,67 @@ LL | v0.into_iter().interleave_shortest(v1.into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:291:20 + --> tests/ui/useless_conversion.rs:295:20 | LL | J: IntoIterator, | ^^^^^^^^^^^^ error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:332:58 + --> tests/ui/useless_conversion.rs:336:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map(Into::into); | ^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `std::io::Error` - --> tests/ui/useless_conversion.rs:335:58 + --> tests/ui/useless_conversion.rs:339:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map_err(Into::into); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:338:58 + --> tests/ui/useless_conversion.rs:342:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map(From::from); | ^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `std::io::Error` - --> tests/ui/useless_conversion.rs:341:58 + --> tests/ui/useless_conversion.rs:345:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map_err(From::from); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:345:31 + --> tests/ui/useless_conversion.rs:349:31 | LL | let _: ControlFlow<()> = c.map_break(Into::into); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:349:31 + --> tests/ui/useless_conversion.rs:353:31 | LL | let _: ControlFlow<()> = c.map_continue(Into::into); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `u32` - --> tests/ui/useless_conversion.rs:363:41 + --> tests/ui/useless_conversion.rs:367:41 | LL | let _: Vec = [1u32].into_iter().map(Into::into).collect(); | ^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:374:18 + --> tests/ui/useless_conversion.rs:378:18 | LL | x.into_iter().map(Into::into).collect() | ^^^^^^^^^^^^^^^^ help: consider removing error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:390:29 + --> tests/ui/useless_conversion.rs:394:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:383:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -310,13 +310,13 @@ LL + takes_into_iter(&self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:398:29 + --> tests/ui/useless_conversion.rs:402:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:383:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -327,13 +327,13 @@ LL + takes_into_iter(&mut self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:407:29 + --> tests/ui/useless_conversion.rs:411:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:383:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -344,13 +344,13 @@ LL + takes_into_iter(*self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:416:29 + --> tests/ui/useless_conversion.rs:420:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:383:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -361,13 +361,13 @@ LL + takes_into_iter(&*self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:425:29 + --> tests/ui/useless_conversion.rs:429:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:383:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -378,13 +378,13 @@ LL + takes_into_iter(&mut *self.my_field); | error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:440:5 + --> tests/ui/useless_conversion.rs:444:5 | LL | R.into_iter().for_each(|_x| {}); | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:442:13 + --> tests/ui/useless_conversion.rs:446:13 | LL | let _ = R.into_iter().map(|_x| 0); | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R`