Skip to content

Commit 2f39b36

Browse files
committed
add Deref support (still produces a false positive)
Signed-off-by: Max Baumann <[email protected]>
1 parent 28092f9 commit 2f39b36

File tree

4 files changed

+92
-10
lines changed

4 files changed

+92
-10
lines changed

clippy_lints/src/strings.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
22
use clippy_utils::source::{snippet, snippet_with_applicability};
3-
use clippy_utils::ty::is_type_diagnostic_item;
3+
use clippy_utils::ty::{get_associated_type, implements_trait, is_type_diagnostic_item};
44
use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
55
use clippy_utils::{peel_blocks, SpanlessEq};
66
use if_chain::if_chain;
77
use rustc_errors::Applicability;
88
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
99
use rustc_lint::{LateContext, LateLintPass, LintContext};
1010
use rustc_middle::lint::in_external_macro;
11-
use rustc_middle::ty;
11+
use rustc_middle::ty::{self, Ty};
1212
use rustc_session::{declare_lint_pass, declare_tool_lint};
1313
use rustc_span::source_map::Spanned;
1414
use rustc_span::sym;
@@ -482,7 +482,7 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
482482
if let ExprKind::MethodCall(path, [recv], trim_span) = recv.kind;
483483
if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str();
484484
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
485-
if recv_ty.is_str() || is_string(cx, recv);
485+
if recv_ty.is_str() || implements_deref_str(cx, recv_ty);
486486
then {
487487
span_lint_and_sugg(
488488
cx,
@@ -497,3 +497,19 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
497497
}
498498
}
499499
}
500+
501+
/// does ty implement Deref<Target=str>?
502+
fn implements_deref_str<'t>(cx: &LateContext<'t>, ty: Ty<'t>) -> bool {
503+
if_chain! {
504+
if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
505+
if implements_trait(cx, ty, deref_trait_id, &[]);
506+
if let Some(target) = get_associated_type(cx, ty, deref_trait_id, "Target");
507+
if target.is_str();
508+
then {
509+
true
510+
}
511+
else {
512+
false
513+
}
514+
}
515+
}

tests/ui/trim_split_whitespace.fixed

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ impl Custom {
99
fn split_whitespace(self) {}
1010
}
1111

12+
struct DerefStr(&'static str);
13+
impl std::ops::Deref for DerefStr {
14+
type Target = str;
15+
fn deref(&self) -> &Self::Target {
16+
self.0
17+
}
18+
}
19+
20+
struct DerefStrAndCustom(&'static str);
21+
impl std::ops::Deref for DerefStrAndCustom {
22+
type Target = str;
23+
fn deref(&self) -> &Self::Target {
24+
self.0
25+
}
26+
}
27+
impl DerefStrAndCustom {
28+
fn trim(self) -> Self {
29+
self
30+
}
31+
fn split_whitespace(self) {}
32+
}
33+
1234
fn main() {
1335
// &str
1436
let _ = " A B C ".split_whitespace(); // should trigger lint
@@ -22,4 +44,12 @@ fn main() {
2244

2345
// Custom
2446
let _ = Custom().trim().split_whitespace(); // should not trigger lint
47+
48+
// Deref<Target=str>
49+
let s = DerefStr(" A B C ");
50+
let _ = s.split_whitespace(); // should trigger lint
51+
52+
// Deref<Target=str> + custom impl
53+
let s = DerefStrAndCustom(" A B C ");
54+
let _ = s.trim().split_whitespace(); // should not trigger lint
2555
}

tests/ui/trim_split_whitespace.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ impl Custom {
99
fn split_whitespace(self) {}
1010
}
1111

12+
struct DerefStr(&'static str);
13+
impl std::ops::Deref for DerefStr {
14+
type Target = str;
15+
fn deref(&self) -> &Self::Target {
16+
self.0
17+
}
18+
}
19+
20+
struct DerefStrAndCustom(&'static str);
21+
impl std::ops::Deref for DerefStrAndCustom {
22+
type Target = str;
23+
fn deref(&self) -> &Self::Target {
24+
self.0
25+
}
26+
}
27+
impl DerefStrAndCustom {
28+
fn trim(self) -> Self {
29+
self
30+
}
31+
fn split_whitespace(self) {}
32+
}
33+
1234
fn main() {
1335
// &str
1436
let _ = " A B C ".trim().split_whitespace(); // should trigger lint
@@ -22,4 +44,12 @@ fn main() {
2244

2345
// Custom
2446
let _ = Custom().trim().split_whitespace(); // should not trigger lint
47+
48+
// Deref<Target=str>
49+
let s = DerefStr(" A B C ");
50+
let _ = s.trim().split_whitespace(); // should trigger lint
51+
52+
// Deref<Target=str> + custom impl
53+
let s = DerefStrAndCustom(" A B C ");
54+
let _ = s.trim().split_whitespace(); // should not trigger lint
2555
}

tests/ui/trim_split_whitespace.stderr

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
error: found call to `str::trim` before `str::split_whitespace`
2-
--> $DIR/trim_split_whitespace.rs:14:23
2+
--> $DIR/trim_split_whitespace.rs:36:23
33
|
44
LL | let _ = " A B C ".trim().split_whitespace(); // should trigger lint
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `trim()`: `split_whitespace()`
66
|
77
= note: `-D clippy::trim-split-whitespace` implied by `-D warnings`
88

99
error: found call to `str::trim_start` before `str::split_whitespace`
10-
--> $DIR/trim_split_whitespace.rs:15:23
10+
--> $DIR/trim_split_whitespace.rs:37:23
1111
|
1212
LL | let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `trim_start()`: `split_whitespace()`
1414

1515
error: found call to `str::trim_end` before `str::split_whitespace`
16-
--> $DIR/trim_split_whitespace.rs:16:23
16+
--> $DIR/trim_split_whitespace.rs:38:23
1717
|
1818
LL | let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `trim_end()`: `split_whitespace()`
2020

2121
error: found call to `str::trim` before `str::split_whitespace`
22-
--> $DIR/trim_split_whitespace.rs:19:37
22+
--> $DIR/trim_split_whitespace.rs:41:37
2323
|
2424
LL | let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `trim()`: `split_whitespace()`
2626

2727
error: found call to `str::trim_start` before `str::split_whitespace`
28-
--> $DIR/trim_split_whitespace.rs:20:37
28+
--> $DIR/trim_split_whitespace.rs:42:37
2929
|
3030
LL | let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `trim_start()`: `split_whitespace()`
3232

3333
error: found call to `str::trim_end` before `str::split_whitespace`
34-
--> $DIR/trim_split_whitespace.rs:21:37
34+
--> $DIR/trim_split_whitespace.rs:43:37
3535
|
3636
LL | let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint
3737
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `trim_end()`: `split_whitespace()`
3838

39-
error: aborting due to 6 previous errors
39+
error: found call to `str::trim` before `str::split_whitespace`
40+
--> $DIR/trim_split_whitespace.rs:50:15
41+
|
42+
LL | let _ = s.trim().split_whitespace(); // should trigger lint
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `trim()`: `split_whitespace()`
44+
45+
error: aborting due to 7 previous errors
4046

0 commit comments

Comments
 (0)