Skip to content

Commit 3ef1f19

Browse files
committed
Auto merge of rust-lang#7873 - xFrednet:7869-string-index-ice, r=Manishearth
Update `str` utils to prevent ICEs and FNs This PR reworks some string handling for lints regarding enum naming. I hope the refactoring will prevent future ICEs and help with new bug free implementations. It might be better to review this PR by going through the commits, as `clippy_utils::camel_case` was renamed to `clippy_utils::str_utils` and then changed further. GH sadly doesn't really make the changes that obvious 🙃 Not too much more to say. Have a nice day 🌞 --- Fixes: rust-lang/rust-clippy#7869 changelog: ICE Fix: [`enum_variant_names`] rust-lang#7869
2 parents 4f46f42 + 7517ae2 commit 3ef1f19

File tree

8 files changed

+275
-158
lines changed

8 files changed

+275
-158
lines changed

clippy_lints/src/enum_variants.rs

+20-38
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! lint on enum variants that are prefixed or suffixed by the same characters
22
3-
use clippy_utils::camel_case;
43
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
54
use clippy_utils::source::is_present_in_source;
5+
use clippy_utils::str_utils::{self, count_match_end, count_match_start};
66
use rustc_hir::{EnumDef, Item, ItemKind};
77
use rustc_lint::{LateContext, LateLintPass};
88
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -117,26 +117,6 @@ impl_lint_pass!(EnumVariantNames => [
117117
MODULE_INCEPTION
118118
]);
119119

120-
/// Returns the number of chars that match from the start
121-
#[must_use]
122-
fn partial_match(pre: &str, name: &str) -> usize {
123-
let mut name_iter = name.chars();
124-
let _ = name_iter.next_back(); // make sure the name is never fully matched
125-
pre.chars().zip(name_iter).take_while(|&(l, r)| l == r).count()
126-
}
127-
128-
/// Returns the number of chars that match from the end
129-
#[must_use]
130-
fn partial_rmatch(post: &str, name: &str) -> usize {
131-
let mut name_iter = name.chars();
132-
let _ = name_iter.next(); // make sure the name is never fully matched
133-
post.chars()
134-
.rev()
135-
.zip(name_iter.rev())
136-
.take_while(|&(l, r)| l == r)
137-
.count()
138-
}
139-
140120
fn check_variant(
141121
cx: &LateContext<'_>,
142122
threshold: u64,
@@ -150,7 +130,7 @@ fn check_variant(
150130
}
151131
for var in def.variants {
152132
let name = var.ident.name.as_str();
153-
if partial_match(item_name, &name) == item_name_chars
133+
if count_match_start(item_name, &name).char_count == item_name_chars
154134
&& name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
155135
&& name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
156136
{
@@ -161,7 +141,7 @@ fn check_variant(
161141
"variant name starts with the enum's name",
162142
);
163143
}
164-
if partial_rmatch(item_name, &name) == item_name_chars {
144+
if count_match_end(item_name, &name).char_count == item_name_chars {
165145
span_lint(
166146
cx,
167147
ENUM_VARIANT_NAMES,
@@ -171,33 +151,33 @@ fn check_variant(
171151
}
172152
}
173153
let first = &def.variants[0].ident.name.as_str();
174-
let mut pre = &first[..camel_case::until(&*first)];
175-
let mut post = &first[camel_case::from(&*first)..];
154+
let mut pre = &first[..str_utils::camel_case_until(&*first).byte_index];
155+
let mut post = &first[str_utils::camel_case_start(&*first).byte_index..];
176156
for var in def.variants {
177157
let name = var.ident.name.as_str();
178158

179-
let pre_match = partial_match(pre, &name);
159+
let pre_match = count_match_start(pre, &name).byte_count;
180160
pre = &pre[..pre_match];
181-
let pre_camel = camel_case::until(pre);
161+
let pre_camel = str_utils::camel_case_until(pre).byte_index;
182162
pre = &pre[..pre_camel];
183163
while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
184164
if next.is_numeric() {
185165
return;
186166
}
187167
if next.is_lowercase() {
188168
let last = pre.len() - last.len_utf8();
189-
let last_camel = camel_case::until(&pre[..last]);
190-
pre = &pre[..last_camel];
169+
let last_camel = str_utils::camel_case_until(&pre[..last]);
170+
pre = &pre[..last_camel.byte_index];
191171
} else {
192172
break;
193173
}
194174
}
195175

196-
let post_match = partial_rmatch(post, &name);
197-
let post_end = post.len() - post_match;
176+
let post_match = count_match_end(post, &name);
177+
let post_end = post.len() - post_match.byte_count;
198178
post = &post[post_end..];
199-
let post_camel = camel_case::from(post);
200-
post = &post[post_camel..];
179+
let post_camel = str_utils::camel_case_start(post);
180+
post = &post[post_camel.byte_index..];
201181
}
202182
let (what, value) = match (pre.is_empty(), post.is_empty()) {
203183
(true, true) => return,
@@ -266,14 +246,16 @@ impl LateLintPass<'_> for EnumVariantNames {
266246
);
267247
}
268248
}
269-
if item.vis.node.is_pub() {
270-
let matching = partial_match(mod_camel, &item_camel);
271-
let rmatching = partial_rmatch(mod_camel, &item_camel);
249+
// The `module_name_repetitions` lint should only trigger if the item has the module in its
250+
// name. Having the same name is accepted.
251+
if item.vis.node.is_pub() && item_camel.len() > mod_camel.len() {
252+
let matching = count_match_start(mod_camel, &item_camel);
253+
let rmatching = count_match_end(mod_camel, &item_camel);
272254
let nchars = mod_camel.chars().count();
273255

274256
let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric();
275257

276-
if matching == nchars {
258+
if matching.char_count == nchars {
277259
match item_camel.chars().nth(nchars) {
278260
Some(c) if is_word_beginning(c) => span_lint(
279261
cx,
@@ -284,7 +266,7 @@ impl LateLintPass<'_> for EnumVariantNames {
284266
_ => (),
285267
}
286268
}
287-
if rmatching == nchars {
269+
if rmatching.char_count == nchars {
288270
span_lint(
289271
cx,
290272
MODULE_NAME_REPETITIONS,

clippy_utils/src/camel_case.rs

-117
This file was deleted.

clippy_utils/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ pub mod sym_helper;
3737
#[allow(clippy::module_name_repetitions)]
3838
pub mod ast_utils;
3939
pub mod attrs;
40-
pub mod camel_case;
4140
pub mod comparisons;
4241
pub mod consts;
4342
pub mod diagnostics;
@@ -50,6 +49,7 @@ pub mod paths;
5049
pub mod ptr;
5150
pub mod qualify_min_const_fn;
5251
pub mod source;
52+
pub mod str_utils;
5353
pub mod sugg;
5454
pub mod ty;
5555
pub mod usage;

0 commit comments

Comments
 (0)