Skip to content

Commit 2d6c238

Browse files
committed
Auto merge of #12091 - samueltardieu:issue-12068, r=Alexendoo
Add .as_ref() to suggestion to remove .to_string() The case of `.to_owned().split(…)` is treated specially in the `unnecessary_to_owned` lint. Test cases check that it works both for slices and for strings, but they missed a corner case: `x.to_string().split(…)` when `x` implements `AsRef<str>` but not `Deref<Target = str>`. In this case, it is wrong to suggest to remove `.to_string()` without adding `.as_ref()` instead. Fix #12068 changelog: [`unnecessary_to_owned`]: suggest replacing `.to_string()` by `.as_ref()`
2 parents ee8bfb7 + e758413 commit 2d6c238

File tree

4 files changed

+65
-14
lines changed

4 files changed

+65
-14
lines changed

clippy_lints/src/methods/unnecessary_to_owned.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
33
use clippy_config::msrvs::{self, Msrv};
44
use clippy_utils::diagnostics::span_lint_and_sugg;
55
use clippy_utils::source::snippet_opt;
6-
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
6+
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_mid_ty_refs};
77
use clippy_utils::visitors::find_all_ret_expressions;
88
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
99
use rustc_errors::Applicability;
1010
use rustc_hir::def::{DefKind, Res};
1111
use rustc_hir::def_id::DefId;
12-
use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, Node};
12+
use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node};
1313
use rustc_hir_typeck::{FnCtxt, Inherited};
1414
use rustc_infer::infer::TyCtxtInferExt;
1515
use rustc_lint::LateContext;
@@ -246,6 +246,19 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb
246246
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
247247
&& let Some(arg_snippet) = snippet_opt(cx, argument_expr.span)
248248
{
249+
// We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x`
250+
// implements `AsRef<str>` but does not implement `Deref<Target = str>`. In this case, we have to
251+
// add `.as_ref()` to the suggestion.
252+
let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String)
253+
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
254+
&& cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, "Target")
255+
!= Some(cx.tcx.types.str_)
256+
{
257+
".as_ref()"
258+
} else {
259+
""
260+
};
261+
249262
// The next suggestion may be incorrect because the removal of the `to_owned`-like
250263
// function could cause the iterator to hold a reference to a resource that is used
251264
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
@@ -255,7 +268,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb
255268
parent.span,
256269
&format!("unnecessary use of `{method_name}`"),
257270
"use",
258-
format!("{receiver_snippet}.split({arg_snippet})"),
271+
format!("{receiver_snippet}{as_ref}.split({arg_snippet})"),
259272
Applicability::MaybeIncorrect,
260273
);
261274
return true;

tests/ui/unnecessary_to_owned_on_split.fixed

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
#[allow(clippy::single_char_pattern)]
1+
#![allow(clippy::single_char_pattern)]
2+
3+
struct Issue12068;
4+
5+
impl AsRef<str> for Issue12068 {
6+
fn as_ref(&self) -> &str {
7+
""
8+
}
9+
}
10+
11+
impl ToString for Issue12068 {
12+
fn to_string(&self) -> String {
13+
String::new()
14+
}
15+
}
216

317
fn main() {
418
let _ = "a".split('a').next().unwrap();
@@ -9,6 +23,8 @@ fn main() {
923
//~^ ERROR: unnecessary use of `to_owned`
1024
let _ = "a".split("a").next().unwrap();
1125
//~^ ERROR: unnecessary use of `to_owned`
26+
let _ = Issue12068.as_ref().split('a').next().unwrap();
27+
//~^ ERROR: unnecessary use of `to_string`
1228

1329
let _ = [1].split(|x| *x == 2).next().unwrap();
1430
//~^ ERROR: unnecessary use of `to_vec`

tests/ui/unnecessary_to_owned_on_split.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
#[allow(clippy::single_char_pattern)]
1+
#![allow(clippy::single_char_pattern)]
2+
3+
struct Issue12068;
4+
5+
impl AsRef<str> for Issue12068 {
6+
fn as_ref(&self) -> &str {
7+
""
8+
}
9+
}
10+
11+
impl ToString for Issue12068 {
12+
fn to_string(&self) -> String {
13+
String::new()
14+
}
15+
}
216

317
fn main() {
418
let _ = "a".to_string().split('a').next().unwrap();
@@ -9,6 +23,8 @@ fn main() {
923
//~^ ERROR: unnecessary use of `to_owned`
1024
let _ = "a".to_owned().split("a").next().unwrap();
1125
//~^ ERROR: unnecessary use of `to_owned`
26+
let _ = Issue12068.to_string().split('a').next().unwrap();
27+
//~^ ERROR: unnecessary use of `to_string`
1228

1329
let _ = [1].to_vec().split(|x| *x == 2).next().unwrap();
1430
//~^ ERROR: unnecessary use of `to_vec`
Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: unnecessary use of `to_string`
2-
--> $DIR/unnecessary_to_owned_on_split.rs:4:13
2+
--> $DIR/unnecessary_to_owned_on_split.rs:18:13
33
|
44
LL | let _ = "a".to_string().split('a').next().unwrap();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split('a')`
@@ -8,46 +8,52 @@ LL | let _ = "a".to_string().split('a').next().unwrap();
88
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]`
99

1010
error: unnecessary use of `to_string`
11-
--> $DIR/unnecessary_to_owned_on_split.rs:6:13
11+
--> $DIR/unnecessary_to_owned_on_split.rs:20:13
1212
|
1313
LL | let _ = "a".to_string().split("a").next().unwrap();
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split("a")`
1515

1616
error: unnecessary use of `to_owned`
17-
--> $DIR/unnecessary_to_owned_on_split.rs:8:13
17+
--> $DIR/unnecessary_to_owned_on_split.rs:22:13
1818
|
1919
LL | let _ = "a".to_owned().split('a').next().unwrap();
2020
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split('a')`
2121

2222
error: unnecessary use of `to_owned`
23-
--> $DIR/unnecessary_to_owned_on_split.rs:10:13
23+
--> $DIR/unnecessary_to_owned_on_split.rs:24:13
2424
|
2525
LL | let _ = "a".to_owned().split("a").next().unwrap();
2626
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split("a")`
2727

28+
error: unnecessary use of `to_string`
29+
--> $DIR/unnecessary_to_owned_on_split.rs:26:13
30+
|
31+
LL | let _ = Issue12068.to_string().split('a').next().unwrap();
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Issue12068.as_ref().split('a')`
33+
2834
error: unnecessary use of `to_vec`
29-
--> $DIR/unnecessary_to_owned_on_split.rs:13:13
35+
--> $DIR/unnecessary_to_owned_on_split.rs:29:13
3036
|
3137
LL | let _ = [1].to_vec().split(|x| *x == 2).next().unwrap();
3238
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)`
3339

3440
error: unnecessary use of `to_vec`
35-
--> $DIR/unnecessary_to_owned_on_split.rs:15:13
41+
--> $DIR/unnecessary_to_owned_on_split.rs:31:13
3642
|
3743
LL | let _ = [1].to_vec().split(|x| *x == 2).next().unwrap();
3844
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)`
3945

4046
error: unnecessary use of `to_owned`
41-
--> $DIR/unnecessary_to_owned_on_split.rs:17:13
47+
--> $DIR/unnecessary_to_owned_on_split.rs:33:13
4248
|
4349
LL | let _ = [1].to_owned().split(|x| *x == 2).next().unwrap();
4450
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)`
4551

4652
error: unnecessary use of `to_owned`
47-
--> $DIR/unnecessary_to_owned_on_split.rs:19:13
53+
--> $DIR/unnecessary_to_owned_on_split.rs:35:13
4854
|
4955
LL | let _ = [1].to_owned().split(|x| *x == 2).next().unwrap();
5056
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)`
5157

52-
error: aborting due to 8 previous errors
58+
error: aborting due to 9 previous errors
5359

0 commit comments

Comments
 (0)