Skip to content

Commit 3b1f8b1

Browse files
committed
Add needless_conversion_for_trait lint; get uitest to pass
1 parent b89fd5e commit 3b1f8b1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2077
-580
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6421,6 +6421,7 @@ Released 2018-09-13
64216421
[`needless_character_iteration`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_character_iteration
64226422
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
64236423
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
6424+
[`needless_conversion_for_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_conversion_for_trait
64246425
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
64256426
[`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else
64266427
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
@@ -6912,4 +6913,5 @@ Released 2018-09-13
69126913
[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold
69136914
[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports
69146915
[`warn-unsafe-macro-metavars-in-private-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-unsafe-macro-metavars-in-private-macros
6916+
[`watched-functions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#watched-functions
69156917
<!-- end autogenerated links to configuration documentation -->

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ ui_test = "0.30.2"
3838
regex = "1.5.5"
3939
serde = { version = "1.0.145", features = ["derive"] }
4040
serde_json = "1.0.122"
41+
tempfile = "3.20"
4142
walkdir = "2.3"
4243
filetime = "0.2.9"
4344
itertools = "0.12"

book/src/lint_configuration.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,3 +1146,13 @@ Whether to also emit warnings for unsafe blocks with metavariable expansions in
11461146
---
11471147
**Affected lints:**
11481148
* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe)
1149+
1150+
1151+
## `watched-functions`
1152+
Non-standard functions `needless_conversion_for_trait` should warn about.
1153+
1154+
**Default Value:** `[]`
1155+
1156+
---
1157+
**Affected lints:**
1158+
* [`needless_conversion_for_trait`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_conversion_for_trait)

clippy_config/src/conf.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ macro_rules! deserialize {
200200
let mut conf_paths = Vec::new();
201201
for raw_value in array {
202202
let value_span = raw_value.span();
203-
let mut conf_path = match ConfPath::<$replaceable>::deserialize(raw_value.into_inner()) {
203+
let mut conf_path = match ConfPath::<String, $replaceable>::deserialize(raw_value.into_inner()) {
204204
Err(e) => {
205205
$errors.push(ConfError::spanned(
206206
$file,
@@ -882,6 +882,9 @@ define_Conf! {
882882
/// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
883883
#[lints(macro_metavars_in_unsafe)]
884884
warn_unsafe_macro_metavars_in_private_macros: bool = false,
885+
/// Non-standard functions `needless_conversion_for_trait` should warn about.
886+
#[lints(needless_conversion_for_trait)]
887+
watched_functions: Vec<ConfPathWithoutReplacement> = Vec::new(),
885888
}
886889

887890
/// Search for the configuration file.

clippy_config/src/types.rs

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
use clippy_utils::paths::{PathNS, find_crates, lookup_path};
2+
use itertools::Itertools;
23
use rustc_data_structures::fx::FxHashMap;
34
use rustc_errors::{Applicability, Diag};
45
use rustc_hir::PrimTy;
56
use rustc_hir::def::DefKind;
67
use rustc_hir::def_id::DefIdMap;
78
use rustc_middle::ty::TyCtxt;
8-
use rustc_span::{Span, Symbol};
9+
use rustc_span::{DUMMY_SP, Span, Symbol};
910
use serde::de::{self, Deserializer, Visitor};
1011
use serde::{Deserialize, Serialize, ser};
1112
use std::collections::HashMap;
1213
use std::fmt;
14+
use std::ops::Deref;
1315

1416
#[derive(Debug, Deserialize)]
1517
#[serde(deny_unknown_fields)]
@@ -18,11 +20,11 @@ pub struct Rename {
1820
pub rename: String,
1921
}
2022

21-
pub type ConfPathWithoutReplacement = ConfPath<false>;
23+
pub type ConfPathWithoutReplacement = ConfPath<String, false>;
2224

2325
#[derive(Debug, Serialize)]
24-
pub struct ConfPath<const REPLACEABLE: bool = true> {
25-
path: String,
26+
pub struct ConfPath<T = String, const REPLACEABLE: bool = true> {
27+
path: T,
2628
reason: Option<String>,
2729
replacement: Option<String>,
2830
/// Setting `allow_invalid` to true suppresses a warning if `path` does not refer to an existing
@@ -38,7 +40,7 @@ pub struct ConfPath<const REPLACEABLE: bool = true> {
3840
span: Span,
3941
}
4042

41-
impl<'de, const REPLACEABLE: bool> Deserialize<'de> for ConfPath<REPLACEABLE> {
43+
impl<'de, const REPLACEABLE: bool> Deserialize<'de> for ConfPath<String, REPLACEABLE> {
4244
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4345
where
4446
D: Deserializer<'de>,
@@ -72,8 +74,8 @@ enum ConfPathEnum {
7274
},
7375
}
7476

75-
impl<const REPLACEABLE: bool> ConfPath<REPLACEABLE> {
76-
pub fn path(&self) -> &str {
77+
impl<T, const REPLACEABLE: bool> ConfPath<T, REPLACEABLE> {
78+
pub fn path(&self) -> &T {
7779
&self.path
7880
}
7981

@@ -101,6 +103,16 @@ impl<const REPLACEABLE: bool> ConfPath<REPLACEABLE> {
101103
}
102104
}
103105

106+
impl<T, const REPLACEABLE: bool> ConfPath<T, REPLACEABLE>
107+
where
108+
T: Deref,
109+
<T as Deref>::Target: ToSymPath,
110+
{
111+
pub fn to_sym_path(&self) -> Vec<Symbol> {
112+
self.path().to_sym_path()
113+
}
114+
}
115+
104116
impl ConfPathEnum {
105117
pub fn path(&self) -> &str {
106118
let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
@@ -130,24 +142,71 @@ impl ConfPathEnum {
130142
}
131143
}
132144

145+
pub trait ToSymPath {
146+
fn to_sym_path(&self) -> Vec<Symbol>;
147+
}
148+
149+
impl ToSymPath for str {
150+
fn to_sym_path(&self) -> Vec<Symbol> {
151+
self.split("::").map(Symbol::intern).collect()
152+
}
153+
}
154+
155+
#[derive(Clone, Copy, Debug)]
156+
pub struct SymPath<'a>(pub &'a [Symbol]);
157+
158+
impl Deref for SymPath<'_> {
159+
type Target = [Symbol];
160+
fn deref(&self) -> &Self::Target {
161+
self.0
162+
}
163+
}
164+
165+
impl fmt::Display for SymPath<'_> {
166+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167+
f.write_str(&self.0.iter().map(Symbol::to_string).join("::"))
168+
}
169+
}
170+
171+
impl ToSymPath for [Symbol] {
172+
fn to_sym_path(&self) -> Vec<Symbol> {
173+
self.to_owned()
174+
}
175+
}
176+
177+
pub const fn conf_path_from_sym_path(sym_path: &'static [Symbol]) -> ConfPath<SymPath<'static>, false> {
178+
ConfPath {
179+
path: SymPath(sym_path),
180+
reason: None,
181+
replacement: None,
182+
allow_invalid: false,
183+
span: DUMMY_SP,
184+
}
185+
}
186+
133187
/// Creates a map of disallowed items to the reason they were disallowed.
134188
#[allow(clippy::type_complexity)]
135-
pub fn create_conf_path_map<const REPLACEABLE: bool>(
189+
pub fn create_conf_path_map<T, const REPLACEABLE: bool>(
136190
tcx: TyCtxt<'_>,
137-
conf_paths: &'static [ConfPath<REPLACEABLE>],
191+
conf_paths: &'static [ConfPath<T, REPLACEABLE>],
138192
ns: PathNS,
139193
def_kind_predicate: impl Fn(DefKind) -> bool,
140194
predicate_description: &str,
141195
allow_prim_tys: bool,
142196
) -> (
143-
DefIdMap<(&'static str, &'static ConfPath<REPLACEABLE>)>,
144-
FxHashMap<PrimTy, (&'static str, &'static ConfPath<REPLACEABLE>)>,
145-
) {
146-
let mut def_ids: DefIdMap<(&'static str, &ConfPath<REPLACEABLE>)> = DefIdMap::default();
147-
let mut prim_tys: FxHashMap<PrimTy, (&'static str, &ConfPath<REPLACEABLE>)> = FxHashMap::default();
197+
DefIdMap<(&'static <T as Deref>::Target, &'static ConfPath<T, REPLACEABLE>)>,
198+
FxHashMap<PrimTy, (&'static <T as Deref>::Target, &'static ConfPath<T, REPLACEABLE>)>,
199+
)
200+
where
201+
T: Deref + fmt::Display,
202+
<T as Deref>::Target: ToSymPath,
203+
{
204+
let mut def_ids: DefIdMap<(&'static <T as Deref>::Target, &ConfPath<T, REPLACEABLE>)> = DefIdMap::default();
205+
let mut prim_tys: FxHashMap<PrimTy, (&'static <T as Deref>::Target, &ConfPath<T, REPLACEABLE>)> =
206+
FxHashMap::default();
148207
for conf_path in conf_paths {
149208
let path = conf_path.path();
150-
let sym_path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
209+
let sym_path = path.to_sym_path();
151210
let mut resolutions = lookup_path(tcx, ns, &sym_path);
152211
resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id)));
153212

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
542542
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
543543
crate::needless_borrows_for_generic_args::NEEDLESS_BORROWS_FOR_GENERIC_ARGS_INFO,
544544
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
545+
crate::needless_conversion_for_trait::NEEDLESS_CONVERSION_FOR_TRAIT_INFO,
545546
crate::needless_else::NEEDLESS_ELSE_INFO,
546547
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
547548
crate::needless_if::NEEDLESS_IF_INFO,

clippy_lints/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ mod needless_bool;
258258
mod needless_borrowed_ref;
259259
mod needless_borrows_for_generic_args;
260260
mod needless_continue;
261+
mod needless_conversion_for_trait;
261262
mod needless_else;
262263
mod needless_for_each;
263264
mod needless_if;
@@ -830,5 +831,10 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
830831
store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
831832
store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
832833
store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg));
834+
store.register_late_pass(move |tcx| {
835+
Box::new(needless_conversion_for_trait::NeedlessConversionForTrait::new(
836+
tcx, conf,
837+
))
838+
});
833839
// add lints here, do not remove this comment, it's used in `new_lint`
834840
}

clippy_lints/src/methods/unnecessary_to_owned.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,11 @@ fn check_other_call_arg<'tcx>(
400400
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
401401
&& (trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id)
402402
&& let receiver_ty = cx.typeck_results().expr_ty(receiver)
403+
// Verify the output type contains the input type to be replaced.
404+
// `needless_conversion_for_trait` cannot change the output type (see
405+
// `clippy_utils::ty::replace_types`). Hence, this check ensures that `unnecessary_to_owned`
406+
// and `needless_conversion_for_trait` do not flag the same code.
407+
&& fn_sig.output().contains(input)
403408
// We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
404409
// `Target = T`.
405410
&& let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) {

0 commit comments

Comments
 (0)