2023-09-15 06:07:34 -05:00
|
|
|
#![warn(clippy::needless_borrows_for_generic_args)]
|
|
|
|
#![allow(
|
|
|
|
clippy::unnecessary_to_owned,
|
|
|
|
clippy::unnecessary_literal_unwrap,
|
|
|
|
clippy::needless_borrow
|
|
|
|
)]
|
|
|
|
|
|
|
|
use core::ops::Deref;
|
|
|
|
use std::any::Any;
|
|
|
|
use std::ffi::OsStr;
|
|
|
|
use std::fmt::{Debug, Display};
|
|
|
|
use std::path::Path;
|
|
|
|
use std::process::Command;
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap();
|
|
|
|
let _ = Path::new(".").join(".");
|
|
|
|
let _ = Any::type_id(&""); // Don't lint. `Any` is only bound
|
|
|
|
let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
|
|
|
|
let _ = Some("").unwrap_or(&"");
|
|
|
|
let _ = std::fs::write("x", "".to_string());
|
|
|
|
|
|
|
|
{
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
struct X;
|
|
|
|
|
|
|
|
impl Deref for X {
|
|
|
|
type Target = X;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn deref_target_is_x<T: Deref<Target = X>>(_: T) {}
|
|
|
|
|
|
|
|
deref_target_is_x(X);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
fn multiple_constraints<T, U, V, X, Y>(_: T)
|
|
|
|
where
|
|
|
|
T: IntoIterator<Item = U> + IntoIterator<Item = X>,
|
|
|
|
U: IntoIterator<Item = V>,
|
|
|
|
V: AsRef<str>,
|
|
|
|
X: IntoIterator<Item = Y>,
|
|
|
|
Y: AsRef<OsStr>,
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
multiple_constraints([[""]]);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
struct X;
|
|
|
|
|
|
|
|
impl Deref for X {
|
|
|
|
type Target = X;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
|
|
|
|
where
|
|
|
|
T: Deref<Target = U>,
|
|
|
|
U: Deref<Target = V>,
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
multiple_constraints_normalizes_to_same(X, X);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
fn only_sized<T>(_: T) {}
|
|
|
|
only_sized(&""); // Don't lint. `Sized` is only bound
|
|
|
|
}
|
|
|
|
{
|
|
|
|
fn ref_as_ref_path<T: 'static>(_: &'static T)
|
|
|
|
where
|
|
|
|
&'static T: AsRef<Path>,
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
|
|
|
|
}
|
|
|
|
{
|
|
|
|
trait RefsOnly {
|
|
|
|
type Referent;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> RefsOnly for &T {
|
|
|
|
type Referent = T;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn refs_only<T, U>(_: T)
|
|
|
|
where
|
|
|
|
T: RefsOnly<Referent = U>,
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
|
|
|
|
}
|
|
|
|
{
|
|
|
|
fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
|
|
|
|
where
|
|
|
|
T: IntoIterator<Item = U>,
|
|
|
|
U: IntoIterator<Item = V>,
|
|
|
|
V: AsRef<str>,
|
|
|
|
{
|
|
|
|
}
|
|
|
|
multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
|
|
|
|
}
|
|
|
|
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
|
|
|
{
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
struct Iter;
|
|
|
|
impl Iterator for Iter {
|
|
|
|
type Item = ();
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn takes_iter(_: impl Iterator) {}
|
|
|
|
fn dont_warn(mut x: Iter) {
|
|
|
|
takes_iter(&mut x);
|
|
|
|
}
|
|
|
|
#[allow(unused_mut)]
|
|
|
|
fn warn(mut x: &mut Iter) {
|
|
|
|
takes_iter(x)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[clippy::msrv = "1.52.0"]
|
|
|
|
{
|
|
|
|
let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
|
|
|
};
|
|
|
|
#[clippy::msrv = "1.53.0"]
|
|
|
|
{
|
|
|
|
let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap();
|
|
|
|
};
|
|
|
|
{
|
|
|
|
let env = "env".to_owned();
|
|
|
|
let arg = "arg".to_owned();
|
|
|
|
let f = |arg| {
|
|
|
|
let loc = "loc".to_owned();
|
|
|
|
let _ = std::fs::write("x", &env); // Don't lint. In environment
|
2024-04-23 08:32:35 -05:00
|
|
|
let _ = std::fs::write("x", &arg);
|
|
|
|
let _ = std::fs::write("x", &loc);
|
2023-09-15 06:07:34 -05:00
|
|
|
};
|
|
|
|
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
|
|
|
|
f(arg);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct X;
|
|
|
|
|
|
|
|
impl Drop for X {
|
|
|
|
fn drop(&mut self) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn f(_: impl Debug) {}
|
|
|
|
|
|
|
|
let x = X;
|
2024-04-23 08:32:35 -05:00
|
|
|
f(&x); // Don't lint, not copy, passed by a reference to a variable
|
2023-09-15 06:07:34 -05:00
|
|
|
}
|
|
|
|
{
|
|
|
|
fn f(_: impl AsRef<str>) {}
|
|
|
|
|
|
|
|
let x = String::new();
|
2024-04-23 08:32:35 -05:00
|
|
|
f(&x);
|
2023-09-15 06:07:34 -05:00
|
|
|
}
|
|
|
|
{
|
|
|
|
fn f(_: impl AsRef<str>) {}
|
|
|
|
fn f2(_: impl AsRef<str>) {}
|
|
|
|
|
|
|
|
let x = String::new();
|
|
|
|
f(&x);
|
|
|
|
f2(&x);
|
|
|
|
}
|
|
|
|
// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
|
|
|
|
// issue 9111
|
|
|
|
{
|
|
|
|
struct A;
|
|
|
|
|
|
|
|
impl Extend<u8> for A {
|
|
|
|
fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Extend<&'a u8> for A {
|
|
|
|
fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut a = A;
|
|
|
|
a.extend(&[]); // vs a.extend([]);
|
|
|
|
}
|
|
|
|
// issue 9710
|
|
|
|
{
|
|
|
|
fn f(_: impl AsRef<str>) {}
|
|
|
|
|
|
|
|
let x = String::new();
|
|
|
|
for _ in 0..10 {
|
|
|
|
f(&x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// issue 9739
|
|
|
|
{
|
|
|
|
fn foo<D: Display>(_it: impl IntoIterator<Item = D>) {}
|
|
|
|
foo(if std::env::var_os("HI").is_some() {
|
|
|
|
&[0]
|
|
|
|
} else {
|
|
|
|
&[] as &[u32]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
{
|
|
|
|
struct S;
|
|
|
|
|
|
|
|
impl S {
|
|
|
|
fn foo<D: Display>(&self, _it: impl IntoIterator<Item = D>) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
S.foo(if std::env::var_os("HI").is_some() {
|
|
|
|
&[0]
|
|
|
|
} else {
|
|
|
|
&[] as &[u32]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// issue 9782
|
|
|
|
{
|
|
|
|
fn foo<T: AsRef<[u8]>>(t: T) {
|
|
|
|
println!("{}", std::mem::size_of::<T>());
|
|
|
|
let _t: &[u8] = t.as_ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
let a: [u8; 100] = [0u8; 100];
|
|
|
|
|
|
|
|
// 100
|
|
|
|
foo::<[u8; 100]>(a);
|
|
|
|
foo(a);
|
|
|
|
|
|
|
|
// 16
|
|
|
|
foo::<&[u8]>(&a);
|
|
|
|
foo(a.as_slice());
|
|
|
|
|
|
|
|
// 8
|
|
|
|
foo::<&[u8; 100]>(&a);
|
|
|
|
foo(a);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
struct S;
|
|
|
|
|
|
|
|
impl S {
|
|
|
|
fn foo<T: AsRef<[u8]>>(t: T) {
|
|
|
|
println!("{}", std::mem::size_of::<T>());
|
|
|
|
let _t: &[u8] = t.as_ref();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let a: [u8; 100] = [0u8; 100];
|
|
|
|
S::foo::<&[u8; 100]>(&a);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
struct S;
|
|
|
|
|
|
|
|
impl S {
|
|
|
|
fn foo<T: AsRef<[u8]>>(&self, t: T) {
|
|
|
|
println!("{}", std::mem::size_of::<T>());
|
|
|
|
let _t: &[u8] = t.as_ref();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let a: [u8; 100] = [0u8; 100];
|
|
|
|
S.foo::<&[u8; 100]>(&a);
|
|
|
|
}
|
|
|
|
// issue 10535
|
|
|
|
{
|
|
|
|
static SOME_STATIC: String = String::new();
|
|
|
|
|
|
|
|
static UNIT: () = compute(&SOME_STATIC);
|
|
|
|
|
|
|
|
pub const fn compute<T>(_: T)
|
|
|
|
where
|
|
|
|
T: Copy,
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
2023-12-01 01:15:55 -06:00
|
|
|
// address of field when operand impl Drop
|
|
|
|
{
|
|
|
|
struct CustomDrop(String);
|
|
|
|
|
|
|
|
impl Drop for CustomDrop {
|
|
|
|
fn drop(&mut self) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_str<P: AsRef<str>>(_to: P) {}
|
|
|
|
|
|
|
|
fn test() {
|
|
|
|
let owner = CustomDrop(String::default());
|
|
|
|
check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop
|
|
|
|
}
|
|
|
|
}
|
2024-04-23 08:32:35 -05:00
|
|
|
{
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct X(Vec<u8>);
|
|
|
|
|
|
|
|
fn f(_: impl Debug) {}
|
|
|
|
|
|
|
|
let x = X(vec![]);
|
|
|
|
f(&x); // Don't lint, makes x unavailable later
|
|
|
|
}
|
|
|
|
{
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct X;
|
|
|
|
|
|
|
|
impl Drop for X {
|
|
|
|
fn drop(&mut self) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn f(_: impl Debug) {}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Y(X);
|
|
|
|
|
|
|
|
let y = Y(X);
|
|
|
|
f(&y); // Don't lint. Not copy, passed by a reference to value
|
|
|
|
}
|
|
|
|
{
|
|
|
|
fn f(_: impl AsRef<str>) {}
|
|
|
|
let x = String::new();
|
|
|
|
f(&x); // Don't lint, not a copy, makes it unavailable later
|
|
|
|
f(String::new()); // Lint, makes no difference
|
|
|
|
let y = "".to_owned();
|
|
|
|
f(&y); // Don't lint
|
|
|
|
f("".to_owned()); // Lint
|
|
|
|
}
|
needless_borrows_for_generic_args: Fix for &mut
This commit fixes a bug introduced in #12706, where the behavior of the
lint has been changed, to avoid suggestions that introduce a move. The
motivation in the commit message is quite poor (if the detection for
significant drops is not sufficient because it's not transitive, the
proper fix would be to make it transitive). However, #12454, the linked
issue, provides a good reason for the change — if the value being
borrowed is bound to a variable, then moving it will only introduce
friction into future refactorings.
Thus #12706 changes the logic so that the lint triggers if the value
being borrowed is Copy, or is the result of a function call, simplifying
the logic to the point where analysing "is this the only use of this
value" isn't necessary.
However, said PR also introduces an undocumented carveout, where
referents that themselves are mutable references are treated as Copy,
to catch some cases that we do want to lint against. However, that is
not sound — it's possible to consume a mutable reference by moving it.
To avoid emitting false suggestions, this PR reintroduces the
referent_used_exactly_once logic and runs that check for referents that
are themselves mutable references.
Thinking about the code shape of &mut x, where x: &mut T, raises the
point that while removing the &mut outright won't work, the extra
indirection is still undesirable, and perhaps instead we should suggest
reborrowing: &mut *x. That, however, is left as possible future work.
Fixes #12856
2024-06-05 16:29:37 -05:00
|
|
|
{
|
|
|
|
fn takes_writer<T: std::io::Write>(_: T) {}
|
|
|
|
|
2024-07-24 15:24:12 -05:00
|
|
|
fn issue_12856(mut buffer: &mut Vec<u8>) {
|
needless_borrows_for_generic_args: Fix for &mut
This commit fixes a bug introduced in #12706, where the behavior of the
lint has been changed, to avoid suggestions that introduce a move. The
motivation in the commit message is quite poor (if the detection for
significant drops is not sufficient because it's not transitive, the
proper fix would be to make it transitive). However, #12454, the linked
issue, provides a good reason for the change — if the value being
borrowed is bound to a variable, then moving it will only introduce
friction into future refactorings.
Thus #12706 changes the logic so that the lint triggers if the value
being borrowed is Copy, or is the result of a function call, simplifying
the logic to the point where analysing "is this the only use of this
value" isn't necessary.
However, said PR also introduces an undocumented carveout, where
referents that themselves are mutable references are treated as Copy,
to catch some cases that we do want to lint against. However, that is
not sound — it's possible to consume a mutable reference by moving it.
To avoid emitting false suggestions, this PR reintroduces the
referent_used_exactly_once logic and runs that check for referents that
are themselves mutable references.
Thinking about the code shape of &mut x, where x: &mut T, raises the
point that while removing the &mut outright won't work, the extra
indirection is still undesirable, and perhaps instead we should suggest
reborrowing: &mut *x. That, however, is left as possible future work.
Fixes #12856
2024-06-05 16:29:37 -05:00
|
|
|
takes_writer(&mut buffer); // Don't lint, would make buffer unavailable later
|
|
|
|
buffer.extend(b"\n");
|
|
|
|
}
|
|
|
|
}
|
2023-09-15 06:07:34 -05:00
|
|
|
}
|