Skip to content

Commit c1bccb6

Browse files
committed
Merge from rustc
2 parents 431140b + 8c8cc42 commit c1bccb6

File tree

4 files changed

+305
-0
lines changed

4 files changed

+305
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
3+
use rustc_ast::BorrowKind;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{self as hir, ExprKind};
6+
use rustc_lint::LateContext;
7+
8+
use super::SINGLE_CHAR_ADD_STR;
9+
10+
/// lint for length-1 `str`s as argument for `insert_str`
11+
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
12+
let mut applicability = Applicability::MachineApplicable;
13+
if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) {
14+
let base_string_snippet =
15+
snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
16+
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
17+
let sugg = format!("{base_string_snippet}.insert({pos_arg}, {extension_string})");
18+
span_lint_and_sugg(
19+
cx,
20+
SINGLE_CHAR_ADD_STR,
21+
expr.span,
22+
"calling `insert_str()` using a single-character string literal",
23+
"consider using `insert` with a character literal",
24+
sugg,
25+
applicability,
26+
);
27+
}
28+
29+
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind
30+
&& let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind
31+
&& path_segment.ident.name == rustc_span::sym::to_string
32+
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
33+
{
34+
let base_string_snippet =
35+
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
36+
let extension_string =
37+
snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
38+
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
39+
let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
40+
41+
let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})");
42+
span_lint_and_sugg(
43+
cx,
44+
SINGLE_CHAR_ADD_STR,
45+
expr.span,
46+
"calling `insert_str()` using a single-character converted to string",
47+
"consider using `insert` without `to_string()`",
48+
sugg,
49+
applicability,
50+
);
51+
}
52+
}
53+
54+
fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
55+
if cx.typeck_results().expr_ty(expr).is_ref()
56+
&& let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
57+
&& ty.is_char()
58+
{
59+
return true;
60+
}
61+
62+
false
63+
}
64+
65+
fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
66+
cx.typeck_results().expr_ty(expr).is_char()
67+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
3+
use rustc_ast::BorrowKind;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{self as hir, ExprKind};
6+
use rustc_lint::LateContext;
7+
8+
use super::SINGLE_CHAR_ADD_STR;
9+
10+
/// lint for length-1 `str`s as argument for `push_str`
11+
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
12+
let mut applicability = Applicability::MachineApplicable;
13+
if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) {
14+
let base_string_snippet =
15+
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
16+
let sugg = format!("{base_string_snippet}.push({extension_string})");
17+
span_lint_and_sugg(
18+
cx,
19+
SINGLE_CHAR_ADD_STR,
20+
expr.span,
21+
"calling `push_str()` using a single-character string literal",
22+
"consider using `push` with a character literal",
23+
sugg,
24+
applicability,
25+
);
26+
}
27+
28+
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind
29+
&& let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind
30+
&& path_segment.ident.name == rustc_span::sym::to_string
31+
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
32+
{
33+
let base_string_snippet =
34+
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
35+
let extension_string =
36+
snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
37+
let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
38+
39+
let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})");
40+
span_lint_and_sugg(
41+
cx,
42+
SINGLE_CHAR_ADD_STR,
43+
expr.span,
44+
"calling `push_str()` using a single-character converted to string",
45+
"consider using `push` without `to_string()`",
46+
sugg,
47+
applicability,
48+
);
49+
}
50+
}
51+
52+
fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
53+
if cx.typeck_results().expr_ty(expr).is_ref()
54+
&& let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
55+
&& ty.is_char()
56+
{
57+
return true;
58+
}
59+
60+
false
61+
}
62+
63+
fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
64+
cx.typeck_results().expr_ty(expr).is_char()
65+
}

tests/ui/unnecessary_clone.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// does not test any rustfixable lints
2+
#![warn(clippy::clone_on_ref_ptr)]
3+
#![allow(unused)]
4+
#![allow(clippy::redundant_clone, clippy::uninlined_format_args, clippy::unnecessary_wraps)]
5+
//@no-rustfix
6+
use std::cell::RefCell;
7+
use std::rc::{self, Rc};
8+
use std::sync::{self, Arc};
9+
10+
trait SomeTrait {}
11+
struct SomeImpl;
12+
impl SomeTrait for SomeImpl {}
13+
14+
fn main() {}
15+
16+
fn clone_on_ref_ptr() {
17+
let rc = Rc::new(true);
18+
let arc = Arc::new(true);
19+
20+
let rcweak = Rc::downgrade(&rc);
21+
let arc_weak = Arc::downgrade(&arc);
22+
23+
rc.clone();
24+
//~^ clone_on_ref_ptr
25+
26+
Rc::clone(&rc);
27+
28+
arc.clone();
29+
//~^ clone_on_ref_ptr
30+
31+
Arc::clone(&arc);
32+
33+
rcweak.clone();
34+
//~^ clone_on_ref_ptr
35+
36+
rc::Weak::clone(&rcweak);
37+
38+
arc_weak.clone();
39+
//~^ clone_on_ref_ptr
40+
41+
sync::Weak::clone(&arc_weak);
42+
43+
let x = Arc::new(SomeImpl);
44+
let _: Arc<dyn SomeTrait> = x.clone();
45+
//~^ clone_on_ref_ptr
46+
}
47+
48+
fn clone_on_copy_generic<T: Copy>(t: T) {
49+
t.clone();
50+
//~^ clone_on_copy
51+
52+
Some(t).clone();
53+
//~^ clone_on_copy
54+
}
55+
56+
mod many_derefs {
57+
struct A;
58+
struct B;
59+
struct C;
60+
struct D;
61+
#[derive(Copy, Clone)]
62+
struct E;
63+
64+
macro_rules! impl_deref {
65+
($src:ident, $dst:ident) => {
66+
impl std::ops::Deref for $src {
67+
type Target = $dst;
68+
fn deref(&self) -> &Self::Target {
69+
&$dst
70+
}
71+
}
72+
};
73+
}
74+
75+
impl_deref!(A, B);
76+
impl_deref!(B, C);
77+
impl_deref!(C, D);
78+
impl std::ops::Deref for D {
79+
type Target = &'static E;
80+
fn deref(&self) -> &Self::Target {
81+
&&E
82+
}
83+
}
84+
85+
fn go1() {
86+
let a = A;
87+
let _: E = a.clone();
88+
//~^ clone_on_copy
89+
90+
let _: E = *****a;
91+
}
92+
}
93+
94+
mod issue2076 {
95+
use std::rc::Rc;
96+
97+
macro_rules! try_opt {
98+
($expr: expr) => {
99+
match $expr {
100+
Some(value) => value,
101+
None => return None,
102+
}
103+
};
104+
}
105+
106+
fn func() -> Option<Rc<u8>> {
107+
let rc = Rc::new(42);
108+
Some(try_opt!(Some(rc)).clone())
109+
//~^ clone_on_ref_ptr
110+
}
111+
}

tests/ui/unnecessary_clone.stderr

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
error: using `.clone()` on a ref-counted pointer
2+
--> tests/ui/unnecessary_clone.rs:23:5
3+
|
4+
LL | rc.clone();
5+
| ^^^^^^^^^^ help: try: `std::rc::Rc::<bool>::clone(&rc)`
6+
|
7+
= note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]`
9+
10+
error: using `.clone()` on a ref-counted pointer
11+
--> tests/ui/unnecessary_clone.rs:28:5
12+
|
13+
LL | arc.clone();
14+
| ^^^^^^^^^^^ help: try: `std::sync::Arc::<bool>::clone(&arc)`
15+
16+
error: using `.clone()` on a ref-counted pointer
17+
--> tests/ui/unnecessary_clone.rs:33:5
18+
|
19+
LL | rcweak.clone();
20+
| ^^^^^^^^^^^^^^ help: try: `std::rc::Weak::<bool>::clone(&rcweak)`
21+
22+
error: using `.clone()` on a ref-counted pointer
23+
--> tests/ui/unnecessary_clone.rs:38:5
24+
|
25+
LL | arc_weak.clone();
26+
| ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::<bool>::clone(&arc_weak)`
27+
28+
error: using `.clone()` on a ref-counted pointer
29+
--> tests/ui/unnecessary_clone.rs:44:33
30+
|
31+
LL | let _: Arc<dyn SomeTrait> = x.clone();
32+
| ^^^^^^^^^ help: try: `std::sync::Arc::<SomeImpl>::clone(&x)`
33+
34+
error: using `clone` on type `T` which implements the `Copy` trait
35+
--> tests/ui/unnecessary_clone.rs:49:5
36+
|
37+
LL | t.clone();
38+
| ^^^^^^^^^ help: try removing the `clone` call: `t`
39+
|
40+
= note: `-D clippy::clone-on-copy` implied by `-D warnings`
41+
= help: to override `-D warnings` add `#[allow(clippy::clone_on_copy)]`
42+
43+
error: using `clone` on type `Option<T>` which implements the `Copy` trait
44+
--> tests/ui/unnecessary_clone.rs:52:5
45+
|
46+
LL | Some(t).clone();
47+
| ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)`
48+
49+
error: using `clone` on type `E` which implements the `Copy` trait
50+
--> tests/ui/unnecessary_clone.rs:87:20
51+
|
52+
LL | let _: E = a.clone();
53+
| ^^^^^^^^^ help: try dereferencing it: `*****a`
54+
55+
error: using `.clone()` on a ref-counted pointer
56+
--> tests/ui/unnecessary_clone.rs:108:14
57+
|
58+
LL | Some(try_opt!(Some(rc)).clone())
59+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::<u8>::clone(&try_opt!(Some(rc)))`
60+
61+
error: aborting due to 9 previous errors
62+

0 commit comments

Comments
 (0)