Skip to content

Commit fc6dfeb

Browse files
committed
Auto merge of rust-lang#11852 - rust-lang:single-char-pattern-ascii-only, r=xFrednet
reduce `single_char_pattern` to only lint on ascii chars This should mostly fix the `single_char_pattern` lint, because with a single byte, the optimizer will usually see through the char-to-string-expansion and single loop iteration. This fixes rust-lang#11675 and rust-lang#8111. Update: As per the meeting on November 28th, 2023, we voted to also downgrade the lint to pedantic. --- changelog: downgrade [`single_char_pattern`] to `pedantic`
2 parents c642d0c + 54de78a commit fc6dfeb

8 files changed

+49
-66
lines changed

clippy_lints/src/methods/mod.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,11 +1145,16 @@ declare_clippy_lint! {
11451145
/// `str` as an argument, e.g., `_.split("x")`.
11461146
///
11471147
/// ### Why is this bad?
1148-
/// Performing these methods using a `char` is faster than
1149-
/// using a `str`.
1148+
/// While this can make a perf difference on some systems,
1149+
/// benchmarks have proven inconclusive. But at least using a
1150+
/// char literal makes it clear that we are looking at a single
1151+
/// character.
11501152
///
11511153
/// ### Known problems
1152-
/// Does not catch multi-byte unicode characters.
1154+
/// Does not catch multi-byte unicode characters. This is by
1155+
/// design, on many machines, splitting by a non-ascii char is
1156+
/// actually slower. Please do your own measurements instead of
1157+
/// relying solely on the results of this lint.
11531158
///
11541159
/// ### Example
11551160
/// ```rust,ignore
@@ -1162,7 +1167,7 @@ declare_clippy_lint! {
11621167
/// ```
11631168
#[clippy::version = "pre 1.29.0"]
11641169
pub SINGLE_CHAR_PATTERN,
1165-
perf,
1170+
pedantic,
11661171
"using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
11671172
}
11681173

clippy_lints/src/methods/single_char_insert_string.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
1010
/// lint for length-1 `str`s as argument for `insert_str`
1111
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
1212
let mut applicability = Applicability::MachineApplicable;
13-
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) {
13+
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability, false) {
1414
let base_string_snippet =
1515
snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
1616
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);

clippy_lints/src/methods/single_char_pattern.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_span::symbol::Symbol;
88

99
use super::SINGLE_CHAR_PATTERN;
1010

11-
const PATTERN_METHODS: [(&str, usize); 24] = [
11+
const PATTERN_METHODS: [(&str, usize); 22] = [
1212
("contains", 0),
1313
("starts_with", 0),
1414
("ends_with", 0),
@@ -27,8 +27,6 @@ const PATTERN_METHODS: [(&str, usize); 24] = [
2727
("rmatches", 0),
2828
("match_indices", 0),
2929
("rmatch_indices", 0),
30-
("strip_prefix", 0),
31-
("strip_suffix", 0),
3230
("trim_start_matches", 0),
3331
("trim_end_matches", 0),
3432
("replace", 0),
@@ -50,7 +48,7 @@ pub(super) fn check(
5048
&& args.len() > pos
5149
&& let arg = &args[pos]
5250
&& let mut applicability = Applicability::MachineApplicable
53-
&& let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability)
51+
&& let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true)
5452
{
5553
span_lint_and_sugg(
5654
cx,

clippy_lints/src/methods/single_char_push_string.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
1010
/// lint for length-1 `str`s as argument for `push_str`
1111
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
1212
let mut applicability = Applicability::MachineApplicable;
13-
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability) {
13+
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) {
1414
let base_string_snippet =
1515
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
1616
let sugg = format!("{base_string_snippet}.push({extension_string})");

clippy_lints/src/methods/utils.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,17 @@ pub(super) fn get_hint_if_single_char_arg(
5252
cx: &LateContext<'_>,
5353
arg: &Expr<'_>,
5454
applicability: &mut Applicability,
55+
ascii_only: bool,
5556
) -> Option<String> {
5657
if let ExprKind::Lit(lit) = &arg.kind
5758
&& let ast::LitKind::Str(r, style) = lit.node
5859
&& let string = r.as_str()
59-
&& string.chars().count() == 1
60+
&& let len = if ascii_only {
61+
string.len()
62+
} else {
63+
string.chars().count()
64+
}
65+
&& len == 1
6066
{
6167
let snip = snippet_with_applicability(cx, arg.span, string, applicability);
6268
let ch = if let ast::StrStyle::Raw(nhash) = style {

tests/ui/single_char_pattern.fixed

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![allow(clippy::needless_raw_strings, clippy::needless_raw_string_hashes, unused_must_use)]
2-
2+
#![warn(clippy::single_char_pattern)]
33
use std::collections::HashSet;
44

55
fn main() {
@@ -10,9 +10,9 @@ fn main() {
1010

1111
let y = "x";
1212
x.split(y);
13-
x.split('ß');
14-
x.split('ℝ');
15-
x.split('💣');
13+
x.split("ß");
14+
x.split("ℝ");
15+
x.split("💣");
1616
// Can't use this lint for unicode code points which don't fit in a char
1717
x.split("❤️");
1818
x.split_inclusive('x');
@@ -34,8 +34,6 @@ fn main() {
3434
x.rmatch_indices('x');
3535
x.trim_start_matches('x');
3636
x.trim_end_matches('x');
37-
x.strip_prefix('x');
38-
x.strip_suffix('x');
3937
x.replace('x', "y");
4038
x.replacen('x', "y", 3);
4139
// Make sure we escape characters correctly.
@@ -64,4 +62,8 @@ fn main() {
6462
// Must escape backslash in raw strings when converting to char #8060
6563
x.split('\\');
6664
x.split('\\');
65+
66+
// should not warn, the char versions are actually slower in some cases
67+
x.strip_prefix("x");
68+
x.strip_suffix("x");
6769
}

tests/ui/single_char_pattern.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![allow(clippy::needless_raw_strings, clippy::needless_raw_string_hashes, unused_must_use)]
2-
2+
#![warn(clippy::single_char_pattern)]
33
use std::collections::HashSet;
44

55
fn main() {
@@ -34,8 +34,6 @@ fn main() {
3434
x.rmatch_indices("x");
3535
x.trim_start_matches("x");
3636
x.trim_end_matches("x");
37-
x.strip_prefix("x");
38-
x.strip_suffix("x");
3937
x.replace("x", "y");
4038
x.replacen("x", "y", 3);
4139
// Make sure we escape characters correctly.
@@ -64,4 +62,8 @@ fn main() {
6462
// Must escape backslash in raw strings when converting to char #8060
6563
x.split(r#"\"#);
6664
x.split(r"\");
65+
66+
// should not warn, the char versions are actually slower in some cases
67+
x.strip_prefix("x");
68+
x.strip_suffix("x");
6769
}

tests/ui/single_char_pattern.stderr

Lines changed: 16 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,6 @@ LL | x.split("x");
77
= note: `-D clippy::single-char-pattern` implied by `-D warnings`
88
= help: to override `-D warnings` add `#[allow(clippy::single_char_pattern)]`
99

10-
error: single-character string constant used as pattern
11-
--> tests/ui/single_char_pattern.rs:13:13
12-
|
13-
LL | x.split("ß");
14-
| ^^^ help: consider using a `char`: `'ß'`
15-
16-
error: single-character string constant used as pattern
17-
--> tests/ui/single_char_pattern.rs:14:13
18-
|
19-
LL | x.split("ℝ");
20-
| ^^^ help: consider using a `char`: `'ℝ'`
21-
22-
error: single-character string constant used as pattern
23-
--> tests/ui/single_char_pattern.rs:15:13
24-
|
25-
LL | x.split("💣");
26-
| ^^^^ help: consider using a `char`: `'💣'`
27-
2810
error: single-character string constant used as pattern
2911
--> tests/ui/single_char_pattern.rs:18:23
3012
|
@@ -140,106 +122,94 @@ LL | x.trim_end_matches("x");
140122
| ^^^ help: consider using a `char`: `'x'`
141123

142124
error: single-character string constant used as pattern
143-
--> tests/ui/single_char_pattern.rs:37:20
144-
|
145-
LL | x.strip_prefix("x");
146-
| ^^^ help: consider using a `char`: `'x'`
147-
148-
error: single-character string constant used as pattern
149-
--> tests/ui/single_char_pattern.rs:38:20
150-
|
151-
LL | x.strip_suffix("x");
152-
| ^^^ help: consider using a `char`: `'x'`
153-
154-
error: single-character string constant used as pattern
155-
--> tests/ui/single_char_pattern.rs:39:15
125+
--> tests/ui/single_char_pattern.rs:37:15
156126
|
157127
LL | x.replace("x", "y");
158128
| ^^^ help: consider using a `char`: `'x'`
159129

160130
error: single-character string constant used as pattern
161-
--> tests/ui/single_char_pattern.rs:40:16
131+
--> tests/ui/single_char_pattern.rs:38:16
162132
|
163133
LL | x.replacen("x", "y", 3);
164134
| ^^^ help: consider using a `char`: `'x'`
165135

166136
error: single-character string constant used as pattern
167-
--> tests/ui/single_char_pattern.rs:42:13
137+
--> tests/ui/single_char_pattern.rs:40:13
168138
|
169139
LL | x.split("\n");
170140
| ^^^^ help: consider using a `char`: `'\n'`
171141

172142
error: single-character string constant used as pattern
173-
--> tests/ui/single_char_pattern.rs:43:13
143+
--> tests/ui/single_char_pattern.rs:41:13
174144
|
175145
LL | x.split("'");
176146
| ^^^ help: consider using a `char`: `'\''`
177147

178148
error: single-character string constant used as pattern
179-
--> tests/ui/single_char_pattern.rs:44:13
149+
--> tests/ui/single_char_pattern.rs:42:13
180150
|
181151
LL | x.split("\'");
182152
| ^^^^ help: consider using a `char`: `'\''`
183153

184154
error: single-character string constant used as pattern
185-
--> tests/ui/single_char_pattern.rs:46:13
155+
--> tests/ui/single_char_pattern.rs:44:13
186156
|
187157
LL | x.split("\"");
188158
| ^^^^ help: consider using a `char`: `'"'`
189159

190160
error: single-character string constant used as pattern
191-
--> tests/ui/single_char_pattern.rs:51:31
161+
--> tests/ui/single_char_pattern.rs:49:31
192162
|
193163
LL | x.replace(';', ",").split(","); // issue #2978
194164
| ^^^ help: consider using a `char`: `','`
195165

196166
error: single-character string constant used as pattern
197-
--> tests/ui/single_char_pattern.rs:52:19
167+
--> tests/ui/single_char_pattern.rs:50:19
198168
|
199169
LL | x.starts_with("\x03"); // issue #2996
200170
| ^^^^^^ help: consider using a `char`: `'\x03'`
201171

202172
error: single-character string constant used as pattern
203-
--> tests/ui/single_char_pattern.rs:59:13
173+
--> tests/ui/single_char_pattern.rs:57:13
204174
|
205175
LL | x.split(r"a");
206176
| ^^^^ help: consider using a `char`: `'a'`
207177

208178
error: single-character string constant used as pattern
209-
--> tests/ui/single_char_pattern.rs:60:13
179+
--> tests/ui/single_char_pattern.rs:58:13
210180
|
211181
LL | x.split(r#"a"#);
212182
| ^^^^^^ help: consider using a `char`: `'a'`
213183

214184
error: single-character string constant used as pattern
215-
--> tests/ui/single_char_pattern.rs:61:13
185+
--> tests/ui/single_char_pattern.rs:59:13
216186
|
217187
LL | x.split(r###"a"###);
218188
| ^^^^^^^^^^ help: consider using a `char`: `'a'`
219189

220190
error: single-character string constant used as pattern
221-
--> tests/ui/single_char_pattern.rs:62:13
191+
--> tests/ui/single_char_pattern.rs:60:13
222192
|
223193
LL | x.split(r###"'"###);
224194
| ^^^^^^^^^^ help: consider using a `char`: `'\''`
225195

226196
error: single-character string constant used as pattern
227-
--> tests/ui/single_char_pattern.rs:63:13
197+
--> tests/ui/single_char_pattern.rs:61:13
228198
|
229199
LL | x.split(r###"#"###);
230200
| ^^^^^^^^^^ help: consider using a `char`: `'#'`
231201

232202
error: single-character string constant used as pattern
233-
--> tests/ui/single_char_pattern.rs:65:13
203+
--> tests/ui/single_char_pattern.rs:63:13
234204
|
235205
LL | x.split(r#"\"#);
236206
| ^^^^^^ help: consider using a `char`: `'\\'`
237207

238208
error: single-character string constant used as pattern
239-
--> tests/ui/single_char_pattern.rs:66:13
209+
--> tests/ui/single_char_pattern.rs:64:13
240210
|
241211
LL | x.split(r"\");
242212
| ^^^^ help: consider using a `char`: `'\\'`
243213

244-
error: aborting due to 40 previous errors
214+
error: aborting due to 35 previous errors
245215

0 commit comments

Comments
 (0)