Skip to content

Commit b18d422

Browse files
committed
Add needless_conversion_for_trait lint; get uitest to pass
1 parent ce71795 commit b18d422

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

+2080
-580
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6271,6 +6271,7 @@ Released 2018-09-13
62716271
[`needless_character_iteration`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_character_iteration
62726272
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
62736273
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
6274+
[`needless_conversion_for_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_conversion_for_trait
62746275
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
62756276
[`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else
62766277
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
@@ -6758,4 +6759,5 @@ Released 2018-09-13
67586759
[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold
67596760
[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports
67606761
[`warn-unsafe-macro-metavars-in-private-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-unsafe-macro-metavars-in-private-macros
6762+
[`watched-functions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#watched-functions
67616763
<!-- 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
toml = "0.7.3"
4243
walkdir = "2.3"
4344
filetime = "0.2.9"

book/src/lint_configuration.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,3 +1135,13 @@ Whether to also emit warnings for unsafe blocks with metavariable expansions in
11351135
---
11361136
**Affected lints:**
11371137
* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe)
1138+
1139+
1140+
## `watched-functions`
1141+
Non-standard functions `needless_conversion_for_trait` should warn about.
1142+
1143+
**Default Value:** `[]`
1144+
1145+
---
1146+
**Affected lints:**
1147+
* [`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
@@ -199,7 +199,7 @@ macro_rules! deserialize {
199199
let mut conf_paths = Vec::new();
200200
for raw_value in array {
201201
let value_span = raw_value.span();
202-
let mut conf_path = match ConfPath::<$replaceable>::deserialize(raw_value.into_inner()) {
202+
let mut conf_path = match ConfPath::<String, $replaceable>::deserialize(raw_value.into_inner()) {
203203
Err(e) => {
204204
$errors.push(ConfError::spanned(
205205
$file,
@@ -877,6 +877,9 @@ define_Conf! {
877877
/// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
878878
#[lints(macro_metavars_in_unsafe)]
879879
warn_unsafe_macro_metavars_in_private_macros: bool = false,
880+
/// Non-standard functions `needless_conversion_for_trait` should warn about.
881+
#[lints(needless_conversion_for_trait)]
882+
watched_functions: Vec<ConfPathWithoutReplacement> = Vec::new(),
880883
}
881884

882885
/// 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
@@ -544,6 +544,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
544544
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
545545
crate::needless_borrows_for_generic_args::NEEDLESS_BORROWS_FOR_GENERIC_ARGS_INFO,
546546
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
547+
crate::needless_conversion_for_trait::NEEDLESS_CONVERSION_FOR_TRAIT_INFO,
547548
crate::needless_else::NEEDLESS_ELSE_INFO,
548549
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
549550
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
@@ -259,6 +259,7 @@ mod needless_bool;
259259
mod needless_borrowed_ref;
260260
mod needless_borrows_for_generic_args;
261261
mod needless_continue;
262+
mod needless_conversion_for_trait;
262263
mod needless_else;
263264
mod needless_for_each;
264265
mod needless_if;
@@ -828,5 +829,10 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
828829
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
829830
store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
830831
store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
832+
store.register_late_pass(move |tcx| {
833+
Box::new(needless_conversion_for_trait::NeedlessConversionForTrait::new(
834+
tcx, conf,
835+
))
836+
});
831837
// add lints here, do not remove this comment, it's used in `new_lint`
832838
}

clippy_lints/src/methods/unnecessary_to_owned.rs

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

0 commit comments

Comments
 (0)