Skip to content

Commit 17ec4b8

Browse files
authoredDec 11, 2020
Rollup merge of #79809 - Eric-Arellano:split-once, r=matklad
Dogfood `str_split_once()` Part of #74773. Beyond increased clarity, this fixes some instances of a common confusion with how `splitn(2)` behaves: the first element will always be `Some()`, regardless of the delimiter, and even if the value is empty. Given this code: ```rust fn main() { let val = "..."; let mut iter = val.splitn(2, '='); println!("Input: {:?}, first: {:?}, second: {:?}", val, iter.next(), iter.next()); } ``` We get: ``` Input: "no_delimiter", first: Some("no_delimiter"), second: None Input: "k=v", first: Some("k"), second: Some("v") Input: "=", first: Some(""), second: Some("") ``` Using `str_split_once()` makes more clear what happens when the delimiter is not found.
2 parents 8b9a59c + 989edf4 commit 17ec4b8

File tree

22 files changed

+196
-190
lines changed

22 files changed

+196
-190
lines changed
 

‎compiler/rustc_mir/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Rust MIR: a lowered representation of Rust.
2828
#![feature(or_patterns)]
2929
#![feature(once_cell)]
3030
#![feature(control_flow_enum)]
31+
#![feature(str_split_once)]
3132
#![recursion_limit = "256"]
3233

3334
#[macro_use]

‎compiler/rustc_mir/src/transform/coverage/debug.rs

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -148,40 +148,46 @@ impl DebugOptions {
148148

149149
if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) {
150150
for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(',') {
151-
let mut setting = setting_str.splitn(2, '=');
152-
match setting.next() {
153-
Some(option) if option == "allow_unused_expressions" => {
154-
allow_unused_expressions = bool_option_val(option, setting.next());
151+
let (option, value) = match setting_str.split_once('=') {
152+
None => (setting_str, None),
153+
Some((k, v)) => (k, Some(v)),
154+
};
155+
match option {
156+
"allow_unused_expressions" => {
157+
allow_unused_expressions = bool_option_val(option, value);
155158
debug!(
156159
"{} env option `allow_unused_expressions` is set to {}",
157160
RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions
158161
);
159162
}
160-
Some(option) if option == "counter_format" => {
161-
if let Some(strval) = setting.next() {
162-
counter_format = counter_format_option_val(strval);
163-
debug!(
164-
"{} env option `counter_format` is set to {:?}",
165-
RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format
166-
);
167-
} else {
168-
bug!(
169-
"`{}` option in environment variable {} requires one or more \
170-
plus-separated choices (a non-empty subset of \
171-
`id+block+operation`)",
172-
option,
173-
RUSTC_COVERAGE_DEBUG_OPTIONS
174-
);
175-
}
163+
"counter_format" => {
164+
match value {
165+
None => {
166+
bug!(
167+
"`{}` option in environment variable {} requires one or more \
168+
plus-separated choices (a non-empty subset of \
169+
`id+block+operation`)",
170+
option,
171+
RUSTC_COVERAGE_DEBUG_OPTIONS
172+
);
173+
}
174+
Some(val) => {
175+
counter_format = counter_format_option_val(val);
176+
debug!(
177+
"{} env option `counter_format` is set to {:?}",
178+
RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format
179+
);
180+
}
181+
};
176182
}
177-
Some("") => {}
178-
Some(invalid) => bug!(
179-
"Unsupported setting `{}` in environment variable {}",
180-
invalid,
181-
RUSTC_COVERAGE_DEBUG_OPTIONS
182-
),
183-
None => {}
184-
}
183+
_ => {
184+
bug!(
185+
"Unsupported setting `{}` in environment variable {}",
186+
option,
187+
RUSTC_COVERAGE_DEBUG_OPTIONS
188+
)
189+
}
190+
};
185191
}
186192
}
187193

‎compiler/rustc_session/src/config.rs

Lines changed: 50 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,8 +1296,10 @@ fn parse_output_types(
12961296
if !debugging_opts.parse_only {
12971297
for list in matches.opt_strs("emit") {
12981298
for output_type in list.split(',') {
1299-
let mut parts = output_type.splitn(2, '=');
1300-
let shorthand = parts.next().unwrap();
1299+
let (shorthand, path) = match output_type.split_once('=') {
1300+
None => (output_type, None),
1301+
Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))),
1302+
};
13011303
let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
13021304
early_error(
13031305
error_format,
@@ -1308,7 +1310,6 @@ fn parse_output_types(
13081310
),
13091311
)
13101312
});
1311-
let path = parts.next().map(PathBuf::from);
13121313
output_types.insert(output_type, path);
13131314
}
13141315
}
@@ -1452,11 +1453,10 @@ fn parse_opt_level(
14521453
let max_c = matches
14531454
.opt_strs_pos("C")
14541455
.into_iter()
1455-
.flat_map(
1456-
|(i, s)| {
1457-
if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None }
1458-
},
1459-
)
1456+
.flat_map(|(i, s)| {
1457+
// NB: This can match a string without `=`.
1458+
if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None }
1459+
})
14601460
.max();
14611461
if max_o > max_c {
14621462
OptLevel::Default
@@ -1491,11 +1491,10 @@ fn select_debuginfo(
14911491
let max_c = matches
14921492
.opt_strs_pos("C")
14931493
.into_iter()
1494-
.flat_map(
1495-
|(i, s)| {
1496-
if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None }
1497-
},
1498-
)
1494+
.flat_map(|(i, s)| {
1495+
// NB: This can match a string without `=`.
1496+
if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None }
1497+
})
14991498
.max();
15001499
if max_g > max_c {
15011500
DebugInfo::Full
@@ -1528,23 +1527,26 @@ fn parse_libs(
15281527
.map(|s| {
15291528
// Parse string of the form "[KIND=]lib[:new_name]",
15301529
// where KIND is one of "dylib", "framework", "static".
1531-
let mut parts = s.splitn(2, '=');
1532-
let kind = parts.next().unwrap();
1533-
let (name, kind) = match (parts.next(), kind) {
1534-
(None, name) => (name, NativeLibKind::Unspecified),
1535-
(Some(name), "dylib") => (name, NativeLibKind::Dylib),
1536-
(Some(name), "framework") => (name, NativeLibKind::Framework),
1537-
(Some(name), "static") => (name, NativeLibKind::StaticBundle),
1538-
(Some(name), "static-nobundle") => (name, NativeLibKind::StaticNoBundle),
1539-
(_, s) => {
1540-
early_error(
1541-
error_format,
1542-
&format!(
1543-
"unknown library kind `{}`, expected \
1544-
one of dylib, framework, or static",
1545-
s
1546-
),
1547-
);
1530+
let (name, kind) = match s.split_once('=') {
1531+
None => (s, NativeLibKind::Unspecified),
1532+
Some((kind, name)) => {
1533+
let kind = match kind {
1534+
"dylib" => NativeLibKind::Dylib,
1535+
"framework" => NativeLibKind::Framework,
1536+
"static" => NativeLibKind::StaticBundle,
1537+
"static-nobundle" => NativeLibKind::StaticNoBundle,
1538+
s => {
1539+
early_error(
1540+
error_format,
1541+
&format!(
1542+
"unknown library kind `{}`, expected \
1543+
one of dylib, framework, or static",
1544+
s
1545+
),
1546+
);
1547+
}
1548+
};
1549+
(name.to_string(), kind)
15481550
}
15491551
};
15501552
if kind == NativeLibKind::StaticNoBundle
@@ -1556,10 +1558,11 @@ fn parse_libs(
15561558
accepted on the nightly compiler",
15571559
);
15581560
}
1559-
let mut name_parts = name.splitn(2, ':');
1560-
let name = name_parts.next().unwrap();
1561-
let new_name = name_parts.next();
1562-
(name.to_owned(), new_name.map(|n| n.to_owned()), kind)
1561+
let (name, new_name) = match name.split_once(':') {
1562+
None => (name, None),
1563+
Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
1564+
};
1565+
(name, new_name, kind)
15631566
})
15641567
.collect()
15651568
}
@@ -1580,20 +1583,13 @@ pub fn parse_externs(
15801583
let is_unstable_enabled = debugging_opts.unstable_options;
15811584
let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
15821585
for arg in matches.opt_strs("extern") {
1583-
let mut parts = arg.splitn(2, '=');
1584-
let name = parts
1585-
.next()
1586-
.unwrap_or_else(|| early_error(error_format, "--extern value must not be empty"));
1587-
let path = parts.next().map(|s| s.to_string());
1588-
1589-
let mut name_parts = name.splitn(2, ':');
1590-
let first_part = name_parts.next();
1591-
let second_part = name_parts.next();
1592-
let (options, name) = match (first_part, second_part) {
1593-
(Some(opts), Some(name)) => (Some(opts), name),
1594-
(Some(name), None) => (None, name),
1595-
(None, None) => early_error(error_format, "--extern name must not be empty"),
1596-
_ => unreachable!(),
1586+
let (name, path) = match arg.split_once('=') {
1587+
None => (arg, None),
1588+
Some((name, path)) => (name.to_string(), Some(path.to_string())),
1589+
};
1590+
let (options, name) = match name.split_once(':') {
1591+
None => (None, name),
1592+
Some((opts, name)) => (Some(opts), name.to_string()),
15971593
};
15981594

15991595
let entry = externs.entry(name.to_owned());
@@ -1682,17 +1678,12 @@ fn parse_remap_path_prefix(
16821678
matches
16831679
.opt_strs("remap-path-prefix")
16841680
.into_iter()
1685-
.map(|remap| {
1686-
let mut parts = remap.rsplitn(2, '='); // reverse iterator
1687-
let to = parts.next();
1688-
let from = parts.next();
1689-
match (from, to) {
1690-
(Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)),
1691-
_ => early_error(
1692-
error_format,
1693-
"--remap-path-prefix must contain '=' between FROM and TO",
1694-
),
1695-
}
1681+
.map(|remap| match remap.rsplit_once('=') {
1682+
None => early_error(
1683+
error_format,
1684+
"--remap-path-prefix must contain '=' between FROM and TO",
1685+
),
1686+
Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
16961687
})
16971688
.collect()
16981689
}

‎compiler/rustc_session/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![feature(crate_visibility_modifier)]
22
#![feature(once_cell)]
33
#![feature(or_patterns)]
4+
#![feature(str_split_once)]
45

56
#[macro_use]
67
extern crate bitflags;

‎compiler/rustc_session/src/options.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,10 @@ macro_rules! options {
179179
{
180180
let mut op = $defaultfn();
181181
for option in matches.opt_strs($prefix) {
182-
let mut iter = option.splitn(2, '=');
183-
let key = iter.next().unwrap();
184-
let value = iter.next();
182+
let (key, value) = match option.split_once('=') {
183+
None => (option, None),
184+
Some((k, v)) => (k.to_string(), Some(v)),
185+
};
185186
let option_to_lookup = key.replace("-", "_");
186187
let mut found = false;
187188
for &(candidate, setter, type_desc, _) in $stat {

‎compiler/rustc_target/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#![feature(never_type)]
1616
#![feature(associated_type_bounds)]
1717
#![feature(exhaustive_patterns)]
18+
#![feature(str_split_once)]
1819

1920
#[macro_use]
2021
extern crate rustc_macros;

‎compiler/rustc_target/src/spec/apple_base.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,7 @@ fn macos_deployment_target() -> (u32, u32) {
5454
let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok();
5555
let version = deployment_target
5656
.as_ref()
57-
.and_then(|s| {
58-
let mut i = s.splitn(2, '.');
59-
i.next().and_then(|a| i.next().map(|b| (a, b)))
60-
})
57+
.and_then(|s| s.split_once('.'))
6158
.and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok());
6259

6360
version.unwrap_or((10, 7))

‎library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@
314314
#![feature(stdsimd)]
315315
#![feature(stmt_expr_attributes)]
316316
#![feature(str_internals)]
317+
#![feature(str_split_once)]
317318
#![feature(test)]
318319
#![feature(thread_local)]
319320
#![feature(thread_local_internals)]

‎library/std/src/sys_common/net.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,8 @@ impl TryFrom<&str> for LookupHost {
177177
}
178178

179179
// split the string by ':' and convert the second part to u16
180-
let mut parts_iter = s.rsplitn(2, ':');
181-
let port_str = try_opt!(parts_iter.next(), "invalid socket address");
182-
let host = try_opt!(parts_iter.next(), "invalid socket address");
180+
let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address");
183181
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
184-
185182
(host, port).try_into()
186183
}
187184
}

‎library/test/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#![feature(termination_trait_lib)]
3131
#![feature(test)]
3232
#![feature(total_cmp)]
33+
#![feature(str_split_once)]
3334

3435
// Public reexports
3536
pub use self::bench::{black_box, Bencher};

‎library/test/src/time.rs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -105,30 +105,24 @@ impl TimeThreshold {
105105
/// value.
106106
pub fn from_env_var(env_var_name: &str) -> Option<Self> {
107107
let durations_str = env::var(env_var_name).ok()?;
108+
let (warn_str, critical_str) = durations_str.split_once(',').unwrap_or_else(|| {
109+
panic!(
110+
"Duration variable {} expected to have 2 numbers separated by comma, but got {}",
111+
env_var_name, durations_str
112+
)
113+
});
108114

109-
// Split string into 2 substrings by comma and try to parse numbers.
110-
let mut durations = durations_str.splitn(2, ',').map(|v| {
115+
let parse_u64 = |v| {
111116
u64::from_str(v).unwrap_or_else(|_| {
112117
panic!(
113118
"Duration value in variable {} is expected to be a number, but got {}",
114119
env_var_name, v
115120
)
116121
})
117-
});
118-
119-
// Callback to be called if the environment variable has unexpected structure.
120-
let panic_on_incorrect_value = || {
121-
panic!(
122-
"Duration variable {} expected to have 2 numbers separated by comma, but got {}",
123-
env_var_name, durations_str
124-
);
125122
};
126123

127-
let (warn, critical) = (
128-
durations.next().unwrap_or_else(panic_on_incorrect_value),
129-
durations.next().unwrap_or_else(panic_on_incorrect_value),
130-
);
131-
124+
let warn = parse_u64(warn_str);
125+
let critical = parse_u64(critical_str);
132126
if warn > critical {
133127
panic!("Test execution warn time should be less or equal to the critical time");
134128
}

‎src/librustdoc/config.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -397,12 +397,9 @@ impl Options {
397397
matches
398398
.opt_strs("default-setting")
399399
.iter()
400-
.map(|s| {
401-
let mut kv = s.splitn(2, '=');
402-
// never panics because `splitn` always returns at least one element
403-
let k = kv.next().unwrap().to_string();
404-
let v = kv.next().unwrap_or("true").to_string();
405-
(k, v)
400+
.map(|s| match s.split_once('=') {
401+
None => (s.clone(), "true".to_string()),
402+
Some((k, v)) => (k.to_string(), v.to_string()),
406403
})
407404
.collect(),
408405
];
@@ -707,11 +704,9 @@ fn parse_extern_html_roots(
707704
) -> Result<BTreeMap<String, String>, &'static str> {
708705
let mut externs = BTreeMap::new();
709706
for arg in &matches.opt_strs("extern-html-root-url") {
710-
let mut parts = arg.splitn(2, '=');
711-
let name = parts.next().ok_or("--extern-html-root-url must not be empty")?;
712-
let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?;
707+
let (name, url) =
708+
arg.split_once('=').ok_or("--extern-html-root-url must be of the form name=url")?;
713709
externs.insert(name.to_string(), url.to_string());
714710
}
715-
716711
Ok(externs)
717712
}

‎src/librustdoc/html/render/mod.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,8 @@ impl Context {
167167
// `style-suffix.min.css`. Path::extension would just return `css`
168168
// which would result in `style.min-suffix.css` which isn't what we
169169
// want.
170-
let mut iter = filename.splitn(2, '.');
171-
let base = iter.next().unwrap();
172-
let ext = iter.next().unwrap();
173-
let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext,);
170+
let (base, ext) = filename.split_once('.').unwrap();
171+
let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext);
174172
self.dst.join(&filename)
175173
}
176174
}

‎src/librustdoc/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#![feature(once_cell)]
1717
#![feature(type_ascription)]
1818
#![feature(split_inclusive)]
19+
#![feature(str_split_once)]
1920
#![recursion_limit = "256"]
2021

2122
#[macro_use]

‎src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,8 +435,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
435435

436436
// Try looking for methods and associated items.
437437
let mut split = path_str.rsplitn(2, "::");
438-
// this can be an `unwrap()` because we ensure the link is never empty
439-
let (item_str, item_name) = split.next().map(|i| (i, Symbol::intern(i))).unwrap();
438+
// NB: `split`'s first element is always defined, even if the delimiter was not present.
439+
let item_str = split.next().unwrap();
440+
assert!(!item_str.is_empty());
441+
let item_name = Symbol::intern(item_str);
440442
let path_root = split
441443
.next()
442444
.map(|f| f.to_owned())

‎src/tools/linkchecker/main.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
//! A few exceptions are allowed as there's known bugs in rustdoc, but this
1515
//! should catch the majority of "broken link" cases.
1616
17+
#![feature(str_split_once)]
18+
1719
use std::collections::hash_map::Entry;
1820
use std::collections::{HashMap, HashSet};
1921
use std::env;
@@ -232,11 +234,12 @@ fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Opti
232234
{
233235
return;
234236
}
235-
let mut parts = url.splitn(2, '#');
236-
let url = parts.next().unwrap();
237-
let fragment = parts.next();
238-
let mut parts = url.splitn(2, '?');
239-
let url = parts.next().unwrap();
237+
let (url, fragment) = match url.split_once('#') {
238+
None => (url, None),
239+
Some((url, fragment)) => (url, Some(fragment)),
240+
};
241+
// NB: the `splitn` always succeeds, even if the delimiter is not present.
242+
let url = url.splitn(2, '?').next().unwrap();
240243

241244
// Once we've plucked out the URL, parse it using our base url and
242245
// then try to extract a file path.

‎src/tools/tidy/src/cargo.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,10 @@ fn verify(tomlfile: &Path, libfile: &Path, bad: &mut bool) {
5959
break;
6060
}
6161

62-
let mut parts = line.splitn(2, '=');
63-
let krate = parts.next().unwrap().trim();
64-
if parts.next().is_none() {
65-
continue;
66-
}
62+
let krate = match line.split_once('=') {
63+
None => continue,
64+
Some((krate, _)) => krate.trim(),
65+
};
6766

6867
// Don't worry about depending on core/std while not writing `extern crate
6968
// core/std` -- that's intentional.

‎src/tools/tidy/src/error_codes_check.rs

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -85,47 +85,61 @@ fn extract_error_codes(
8585
for line in f.lines() {
8686
let s = line.trim();
8787
if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
88-
if let Some(err_code) = s.splitn(2, ':').next() {
89-
let err_code = err_code.to_owned();
90-
if !error_codes.contains_key(&err_code) {
91-
error_codes.insert(err_code.clone(), false);
92-
}
93-
// Now we extract the tests from the markdown file!
94-
let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1));
95-
let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
96-
let path = some_or_continue!(path.parent())
97-
.join(md_file_name)
98-
.canonicalize()
99-
.expect("failed to canonicalize error explanation file path");
100-
match read_to_string(&path) {
101-
Ok(content) => {
102-
if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str())
103-
&& !check_if_error_code_is_test_in_explanation(&content, &err_code)
104-
{
105-
errors.push(format!(
106-
"`{}` doesn't use its own error code in compile_fail example",
107-
path.display(),
108-
));
109-
}
110-
if check_error_code_explanation(&content, error_codes, err_code) {
111-
errors.push(format!(
112-
"`{}` uses invalid tag `compile-fail` instead of `compile_fail`",
113-
path.display(),
114-
));
115-
}
88+
let err_code = s
89+
.split_once(':')
90+
.expect(
91+
format!(
92+
"Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} without a `:` delimiter",
93+
s,
94+
).as_str()
95+
)
96+
.0
97+
.to_owned();
98+
if !error_codes.contains_key(&err_code) {
99+
error_codes.insert(err_code.clone(), false);
100+
}
101+
// Now we extract the tests from the markdown file!
102+
let md_file_name = match s.split_once("include_str!(\"") {
103+
None => continue,
104+
Some((_, md)) => match md.split_once("\")") {
105+
None => continue,
106+
Some((file_name, _)) => file_name,
107+
},
108+
};
109+
let path = some_or_continue!(path.parent())
110+
.join(md_file_name)
111+
.canonicalize()
112+
.expect("failed to canonicalize error explanation file path");
113+
match read_to_string(&path) {
114+
Ok(content) => {
115+
if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str())
116+
&& !check_if_error_code_is_test_in_explanation(&content, &err_code)
117+
{
118+
errors.push(format!(
119+
"`{}` doesn't use its own error code in compile_fail example",
120+
path.display(),
121+
));
116122
}
117-
Err(e) => {
118-
eprintln!("Couldn't read `{}`: {}", path.display(), e);
123+
if check_error_code_explanation(&content, error_codes, err_code) {
124+
errors.push(format!(
125+
"`{}` uses invalid tag `compile-fail` instead of `compile_fail`",
126+
path.display(),
127+
));
119128
}
120129
}
130+
Err(e) => {
131+
eprintln!("Couldn't read `{}`: {}", path.display(), e);
132+
}
121133
}
122134
} else if reached_no_explanation && s.starts_with('E') {
123-
if let Some(err_code) = s.splitn(2, ',').next() {
124-
let err_code = err_code.to_owned();
125-
if !error_codes.contains_key(&err_code) {
126-
// this check should *never* fail!
127-
error_codes.insert(err_code, false);
128-
}
135+
let err_code = match s.split_once(',') {
136+
None => s,
137+
Some((err_code, _)) => err_code,
138+
}
139+
.to_string();
140+
if !error_codes.contains_key(&err_code) {
141+
// this check should *never* fail!
142+
error_codes.insert(err_code, false);
129143
}
130144
} else if s == ";" {
131145
reached_no_explanation = true;
@@ -137,12 +151,15 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, boo
137151
for line in f.lines() {
138152
let s = line.trim();
139153
if s.starts_with("error[E") || s.starts_with("warning[E") {
140-
if let Some(err_code) = s.splitn(2, ']').next() {
141-
if let Some(err_code) = err_code.splitn(2, '[').nth(1) {
142-
let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
143-
*nb = true;
144-
}
145-
}
154+
let err_code = match s.split_once(']') {
155+
None => continue,
156+
Some((err_code, _)) => match err_code.split_once('[') {
157+
None => continue,
158+
Some((_, err_code)) => err_code,
159+
},
160+
};
161+
let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
162+
*nb = true;
146163
}
147164
}
148165
}

‎src/tools/tidy/src/extdeps.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn check(root: &Path, bad: &mut bool) {
2323
}
2424

2525
// Extract source value.
26-
let source = line.splitn(2, '=').nth(1).unwrap().trim();
26+
let source = line.split_once('=').unwrap().1.trim();
2727

2828
// Ensure source is allowed.
2929
if !ALLOWED_SOURCES.contains(&&*source) {

‎src/tools/tidy/src/features.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pub fn check(
112112
let gate_test_str = "gate-test-";
113113

114114
let feature_name = match line.find(gate_test_str) {
115+
// NB: the `splitn` always succeeds, even if the delimiter is not present.
115116
Some(i) => line[i + gate_test_str.len()..].splitn(2, ' ').next().unwrap(),
116117
None => continue,
117118
};

‎src/tools/tidy/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
//! This library contains the tidy lints and exposes it
44
//! to be used by tools.
55
6+
#![feature(str_split_once)]
7+
68
use std::fs::File;
79
use std::io::Read;
810
use walkdir::{DirEntry, WalkDir};

‎src/tools/tidy/src/ui_tests.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,11 @@ pub fn check(path: &Path, bad: &mut bool) {
1919
//
2020
// For now, just make sure that there is a corresponding
2121
// `$testname.rs` file.
22-
let testname = file_path
23-
.file_name()
24-
.unwrap()
25-
.to_str()
26-
.unwrap()
27-
.splitn(2, '.')
28-
.next()
29-
.unwrap();
22+
//
23+
// NB: We do not use file_stem() as some file names have multiple `.`s and we
24+
// must strip all of them.
25+
let testname =
26+
file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0;
3027
if !file_path.with_file_name(testname).with_extension("rs").exists() {
3128
println!("Stray file with UI testing output: {:?}", file_path);
3229
*bad = true;

0 commit comments

Comments
 (0)
Please sign in to comment.