mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-04 02:54:00 +00:00
Rollup merge of #79809 - Eric-Arellano:split-once, r=matklad
Dogfood `str_split_once()` Part of https://github.com/rust-lang/rust/issues/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.
This commit is contained in:
commit
17ec4b8258
@ -28,6 +28,7 @@ Rust MIR: a lowered representation of Rust.
|
|||||||
#![feature(or_patterns)]
|
#![feature(or_patterns)]
|
||||||
#![feature(once_cell)]
|
#![feature(once_cell)]
|
||||||
#![feature(control_flow_enum)]
|
#![feature(control_flow_enum)]
|
||||||
|
#![feature(str_split_once)]
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -148,40 +148,46 @@ impl DebugOptions {
|
|||||||
|
|
||||||
if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) {
|
if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) {
|
||||||
for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(',') {
|
for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(',') {
|
||||||
let mut setting = setting_str.splitn(2, '=');
|
let (option, value) = match setting_str.split_once('=') {
|
||||||
match setting.next() {
|
None => (setting_str, None),
|
||||||
Some(option) if option == "allow_unused_expressions" => {
|
Some((k, v)) => (k, Some(v)),
|
||||||
allow_unused_expressions = bool_option_val(option, setting.next());
|
};
|
||||||
|
match option {
|
||||||
|
"allow_unused_expressions" => {
|
||||||
|
allow_unused_expressions = bool_option_val(option, value);
|
||||||
debug!(
|
debug!(
|
||||||
"{} env option `allow_unused_expressions` is set to {}",
|
"{} env option `allow_unused_expressions` is set to {}",
|
||||||
RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions
|
RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(option) if option == "counter_format" => {
|
"counter_format" => {
|
||||||
if let Some(strval) = setting.next() {
|
match value {
|
||||||
counter_format = counter_format_option_val(strval);
|
None => {
|
||||||
debug!(
|
bug!(
|
||||||
"{} env option `counter_format` is set to {:?}",
|
"`{}` option in environment variable {} requires one or more \
|
||||||
RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format
|
plus-separated choices (a non-empty subset of \
|
||||||
);
|
`id+block+operation`)",
|
||||||
} else {
|
option,
|
||||||
bug!(
|
RUSTC_COVERAGE_DEBUG_OPTIONS
|
||||||
"`{}` option in environment variable {} requires one or more \
|
);
|
||||||
plus-separated choices (a non-empty subset of \
|
}
|
||||||
`id+block+operation`)",
|
Some(val) => {
|
||||||
option,
|
counter_format = counter_format_option_val(val);
|
||||||
RUSTC_COVERAGE_DEBUG_OPTIONS
|
debug!(
|
||||||
);
|
"{} env option `counter_format` is set to {:?}",
|
||||||
}
|
RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Some("") => {}
|
_ => {
|
||||||
Some(invalid) => bug!(
|
bug!(
|
||||||
"Unsupported setting `{}` in environment variable {}",
|
"Unsupported setting `{}` in environment variable {}",
|
||||||
invalid,
|
option,
|
||||||
RUSTC_COVERAGE_DEBUG_OPTIONS
|
RUSTC_COVERAGE_DEBUG_OPTIONS
|
||||||
),
|
)
|
||||||
None => {}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1296,8 +1296,10 @@ fn parse_output_types(
|
|||||||
if !debugging_opts.parse_only {
|
if !debugging_opts.parse_only {
|
||||||
for list in matches.opt_strs("emit") {
|
for list in matches.opt_strs("emit") {
|
||||||
for output_type in list.split(',') {
|
for output_type in list.split(',') {
|
||||||
let mut parts = output_type.splitn(2, '=');
|
let (shorthand, path) = match output_type.split_once('=') {
|
||||||
let shorthand = parts.next().unwrap();
|
None => (output_type, None),
|
||||||
|
Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))),
|
||||||
|
};
|
||||||
let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
|
let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
|
||||||
early_error(
|
early_error(
|
||||||
error_format,
|
error_format,
|
||||||
@ -1308,7 +1310,6 @@ fn parse_output_types(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let path = parts.next().map(PathBuf::from);
|
|
||||||
output_types.insert(output_type, path);
|
output_types.insert(output_type, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1452,11 +1453,10 @@ fn parse_opt_level(
|
|||||||
let max_c = matches
|
let max_c = matches
|
||||||
.opt_strs_pos("C")
|
.opt_strs_pos("C")
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(
|
.flat_map(|(i, s)| {
|
||||||
|(i, s)| {
|
// NB: This can match a string without `=`.
|
||||||
if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None }
|
if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None }
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.max();
|
.max();
|
||||||
if max_o > max_c {
|
if max_o > max_c {
|
||||||
OptLevel::Default
|
OptLevel::Default
|
||||||
@ -1491,11 +1491,10 @@ fn select_debuginfo(
|
|||||||
let max_c = matches
|
let max_c = matches
|
||||||
.opt_strs_pos("C")
|
.opt_strs_pos("C")
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(
|
.flat_map(|(i, s)| {
|
||||||
|(i, s)| {
|
// NB: This can match a string without `=`.
|
||||||
if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None }
|
if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None }
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.max();
|
.max();
|
||||||
if max_g > max_c {
|
if max_g > max_c {
|
||||||
DebugInfo::Full
|
DebugInfo::Full
|
||||||
@ -1528,23 +1527,26 @@ fn parse_libs(
|
|||||||
.map(|s| {
|
.map(|s| {
|
||||||
// Parse string of the form "[KIND=]lib[:new_name]",
|
// Parse string of the form "[KIND=]lib[:new_name]",
|
||||||
// where KIND is one of "dylib", "framework", "static".
|
// where KIND is one of "dylib", "framework", "static".
|
||||||
let mut parts = s.splitn(2, '=');
|
let (name, kind) = match s.split_once('=') {
|
||||||
let kind = parts.next().unwrap();
|
None => (s, NativeLibKind::Unspecified),
|
||||||
let (name, kind) = match (parts.next(), kind) {
|
Some((kind, name)) => {
|
||||||
(None, name) => (name, NativeLibKind::Unspecified),
|
let kind = match kind {
|
||||||
(Some(name), "dylib") => (name, NativeLibKind::Dylib),
|
"dylib" => NativeLibKind::Dylib,
|
||||||
(Some(name), "framework") => (name, NativeLibKind::Framework),
|
"framework" => NativeLibKind::Framework,
|
||||||
(Some(name), "static") => (name, NativeLibKind::StaticBundle),
|
"static" => NativeLibKind::StaticBundle,
|
||||||
(Some(name), "static-nobundle") => (name, NativeLibKind::StaticNoBundle),
|
"static-nobundle" => NativeLibKind::StaticNoBundle,
|
||||||
(_, s) => {
|
s => {
|
||||||
early_error(
|
early_error(
|
||||||
error_format,
|
error_format,
|
||||||
&format!(
|
&format!(
|
||||||
"unknown library kind `{}`, expected \
|
"unknown library kind `{}`, expected \
|
||||||
one of dylib, framework, or static",
|
one of dylib, framework, or static",
|
||||||
s
|
s
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(name.to_string(), kind)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if kind == NativeLibKind::StaticNoBundle
|
if kind == NativeLibKind::StaticNoBundle
|
||||||
@ -1556,10 +1558,11 @@ fn parse_libs(
|
|||||||
accepted on the nightly compiler",
|
accepted on the nightly compiler",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut name_parts = name.splitn(2, ':');
|
let (name, new_name) = match name.split_once(':') {
|
||||||
let name = name_parts.next().unwrap();
|
None => (name, None),
|
||||||
let new_name = name_parts.next();
|
Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
|
||||||
(name.to_owned(), new_name.map(|n| n.to_owned()), kind)
|
};
|
||||||
|
(name, new_name, kind)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -1580,20 +1583,13 @@ pub fn parse_externs(
|
|||||||
let is_unstable_enabled = debugging_opts.unstable_options;
|
let is_unstable_enabled = debugging_opts.unstable_options;
|
||||||
let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
|
let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
|
||||||
for arg in matches.opt_strs("extern") {
|
for arg in matches.opt_strs("extern") {
|
||||||
let mut parts = arg.splitn(2, '=');
|
let (name, path) = match arg.split_once('=') {
|
||||||
let name = parts
|
None => (arg, None),
|
||||||
.next()
|
Some((name, path)) => (name.to_string(), Some(path.to_string())),
|
||||||
.unwrap_or_else(|| early_error(error_format, "--extern value must not be empty"));
|
};
|
||||||
let path = parts.next().map(|s| s.to_string());
|
let (options, name) = match name.split_once(':') {
|
||||||
|
None => (None, name),
|
||||||
let mut name_parts = name.splitn(2, ':');
|
Some((opts, name)) => (Some(opts), name.to_string()),
|
||||||
let first_part = name_parts.next();
|
|
||||||
let second_part = name_parts.next();
|
|
||||||
let (options, name) = match (first_part, second_part) {
|
|
||||||
(Some(opts), Some(name)) => (Some(opts), name),
|
|
||||||
(Some(name), None) => (None, name),
|
|
||||||
(None, None) => early_error(error_format, "--extern name must not be empty"),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let entry = externs.entry(name.to_owned());
|
let entry = externs.entry(name.to_owned());
|
||||||
@ -1682,17 +1678,12 @@ fn parse_remap_path_prefix(
|
|||||||
matches
|
matches
|
||||||
.opt_strs("remap-path-prefix")
|
.opt_strs("remap-path-prefix")
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|remap| {
|
.map(|remap| match remap.rsplit_once('=') {
|
||||||
let mut parts = remap.rsplitn(2, '='); // reverse iterator
|
None => early_error(
|
||||||
let to = parts.next();
|
error_format,
|
||||||
let from = parts.next();
|
"--remap-path-prefix must contain '=' between FROM and TO",
|
||||||
match (from, to) {
|
),
|
||||||
(Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)),
|
Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
|
||||||
_ => early_error(
|
|
||||||
error_format,
|
|
||||||
"--remap-path-prefix must contain '=' between FROM and TO",
|
|
||||||
),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![feature(crate_visibility_modifier)]
|
#![feature(crate_visibility_modifier)]
|
||||||
#![feature(once_cell)]
|
#![feature(once_cell)]
|
||||||
#![feature(or_patterns)]
|
#![feature(or_patterns)]
|
||||||
|
#![feature(str_split_once)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
@ -179,9 +179,10 @@ macro_rules! options {
|
|||||||
{
|
{
|
||||||
let mut op = $defaultfn();
|
let mut op = $defaultfn();
|
||||||
for option in matches.opt_strs($prefix) {
|
for option in matches.opt_strs($prefix) {
|
||||||
let mut iter = option.splitn(2, '=');
|
let (key, value) = match option.split_once('=') {
|
||||||
let key = iter.next().unwrap();
|
None => (option, None),
|
||||||
let value = iter.next();
|
Some((k, v)) => (k.to_string(), Some(v)),
|
||||||
|
};
|
||||||
let option_to_lookup = key.replace("-", "_");
|
let option_to_lookup = key.replace("-", "_");
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
for &(candidate, setter, type_desc, _) in $stat {
|
for &(candidate, setter, type_desc, _) in $stat {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![feature(associated_type_bounds)]
|
#![feature(associated_type_bounds)]
|
||||||
#![feature(exhaustive_patterns)]
|
#![feature(exhaustive_patterns)]
|
||||||
|
#![feature(str_split_once)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rustc_macros;
|
extern crate rustc_macros;
|
||||||
|
@ -54,10 +54,7 @@ fn macos_deployment_target() -> (u32, u32) {
|
|||||||
let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok();
|
let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok();
|
||||||
let version = deployment_target
|
let version = deployment_target
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| {
|
.and_then(|s| s.split_once('.'))
|
||||||
let mut i = s.splitn(2, '.');
|
|
||||||
i.next().and_then(|a| i.next().map(|b| (a, b)))
|
|
||||||
})
|
|
||||||
.and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok());
|
.and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok());
|
||||||
|
|
||||||
version.unwrap_or((10, 7))
|
version.unwrap_or((10, 7))
|
||||||
|
@ -314,6 +314,7 @@
|
|||||||
#![feature(stdsimd)]
|
#![feature(stdsimd)]
|
||||||
#![feature(stmt_expr_attributes)]
|
#![feature(stmt_expr_attributes)]
|
||||||
#![feature(str_internals)]
|
#![feature(str_internals)]
|
||||||
|
#![feature(str_split_once)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
#![feature(thread_local)]
|
#![feature(thread_local)]
|
||||||
#![feature(thread_local_internals)]
|
#![feature(thread_local_internals)]
|
||||||
|
@ -177,11 +177,8 @@ impl TryFrom<&str> for LookupHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// split the string by ':' and convert the second part to u16
|
// split the string by ':' and convert the second part to u16
|
||||||
let mut parts_iter = s.rsplitn(2, ':');
|
let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address");
|
||||||
let port_str = try_opt!(parts_iter.next(), "invalid socket address");
|
|
||||||
let host = try_opt!(parts_iter.next(), "invalid socket address");
|
|
||||||
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
|
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
|
||||||
|
|
||||||
(host, port).try_into()
|
(host, port).try_into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#![feature(termination_trait_lib)]
|
#![feature(termination_trait_lib)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
#![feature(total_cmp)]
|
#![feature(total_cmp)]
|
||||||
|
#![feature(str_split_once)]
|
||||||
|
|
||||||
// Public reexports
|
// Public reexports
|
||||||
pub use self::bench::{black_box, Bencher};
|
pub use self::bench::{black_box, Bencher};
|
||||||
|
@ -105,30 +105,24 @@ impl TimeThreshold {
|
|||||||
/// value.
|
/// value.
|
||||||
pub fn from_env_var(env_var_name: &str) -> Option<Self> {
|
pub fn from_env_var(env_var_name: &str) -> Option<Self> {
|
||||||
let durations_str = env::var(env_var_name).ok()?;
|
let durations_str = env::var(env_var_name).ok()?;
|
||||||
|
let (warn_str, critical_str) = durations_str.split_once(',').unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"Duration variable {} expected to have 2 numbers separated by comma, but got {}",
|
||||||
|
env_var_name, durations_str
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// Split string into 2 substrings by comma and try to parse numbers.
|
let parse_u64 = |v| {
|
||||||
let mut durations = durations_str.splitn(2, ',').map(|v| {
|
|
||||||
u64::from_str(v).unwrap_or_else(|_| {
|
u64::from_str(v).unwrap_or_else(|_| {
|
||||||
panic!(
|
panic!(
|
||||||
"Duration value in variable {} is expected to be a number, but got {}",
|
"Duration value in variable {} is expected to be a number, but got {}",
|
||||||
env_var_name, v
|
env_var_name, v
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
});
|
|
||||||
|
|
||||||
// Callback to be called if the environment variable has unexpected structure.
|
|
||||||
let panic_on_incorrect_value = || {
|
|
||||||
panic!(
|
|
||||||
"Duration variable {} expected to have 2 numbers separated by comma, but got {}",
|
|
||||||
env_var_name, durations_str
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let (warn, critical) = (
|
let warn = parse_u64(warn_str);
|
||||||
durations.next().unwrap_or_else(panic_on_incorrect_value),
|
let critical = parse_u64(critical_str);
|
||||||
durations.next().unwrap_or_else(panic_on_incorrect_value),
|
|
||||||
);
|
|
||||||
|
|
||||||
if warn > critical {
|
if warn > critical {
|
||||||
panic!("Test execution warn time should be less or equal to the critical time");
|
panic!("Test execution warn time should be less or equal to the critical time");
|
||||||
}
|
}
|
||||||
|
@ -397,12 +397,9 @@ impl Options {
|
|||||||
matches
|
matches
|
||||||
.opt_strs("default-setting")
|
.opt_strs("default-setting")
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| {
|
.map(|s| match s.split_once('=') {
|
||||||
let mut kv = s.splitn(2, '=');
|
None => (s.clone(), "true".to_string()),
|
||||||
// never panics because `splitn` always returns at least one element
|
Some((k, v)) => (k.to_string(), v.to_string()),
|
||||||
let k = kv.next().unwrap().to_string();
|
|
||||||
let v = kv.next().unwrap_or("true").to_string();
|
|
||||||
(k, v)
|
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
];
|
];
|
||||||
@ -707,11 +704,9 @@ fn parse_extern_html_roots(
|
|||||||
) -> Result<BTreeMap<String, String>, &'static str> {
|
) -> Result<BTreeMap<String, String>, &'static str> {
|
||||||
let mut externs = BTreeMap::new();
|
let mut externs = BTreeMap::new();
|
||||||
for arg in &matches.opt_strs("extern-html-root-url") {
|
for arg in &matches.opt_strs("extern-html-root-url") {
|
||||||
let mut parts = arg.splitn(2, '=');
|
let (name, url) =
|
||||||
let name = parts.next().ok_or("--extern-html-root-url must not be empty")?;
|
arg.split_once('=').ok_or("--extern-html-root-url must be of the form name=url")?;
|
||||||
let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?;
|
|
||||||
externs.insert(name.to_string(), url.to_string());
|
externs.insert(name.to_string(), url.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(externs)
|
Ok(externs)
|
||||||
}
|
}
|
||||||
|
@ -167,10 +167,8 @@ impl Context {
|
|||||||
// `style-suffix.min.css`. Path::extension would just return `css`
|
// `style-suffix.min.css`. Path::extension would just return `css`
|
||||||
// which would result in `style.min-suffix.css` which isn't what we
|
// which would result in `style.min-suffix.css` which isn't what we
|
||||||
// want.
|
// want.
|
||||||
let mut iter = filename.splitn(2, '.');
|
let (base, ext) = filename.split_once('.').unwrap();
|
||||||
let base = iter.next().unwrap();
|
let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext);
|
||||||
let ext = iter.next().unwrap();
|
|
||||||
let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext,);
|
|
||||||
self.dst.join(&filename)
|
self.dst.join(&filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#![feature(once_cell)]
|
#![feature(once_cell)]
|
||||||
#![feature(type_ascription)]
|
#![feature(type_ascription)]
|
||||||
#![feature(split_inclusive)]
|
#![feature(split_inclusive)]
|
||||||
|
#![feature(str_split_once)]
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -435,8 +435,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
|
|
||||||
// Try looking for methods and associated items.
|
// Try looking for methods and associated items.
|
||||||
let mut split = path_str.rsplitn(2, "::");
|
let mut split = path_str.rsplitn(2, "::");
|
||||||
// this can be an `unwrap()` because we ensure the link is never empty
|
// NB: `split`'s first element is always defined, even if the delimiter was not present.
|
||||||
let (item_str, item_name) = split.next().map(|i| (i, Symbol::intern(i))).unwrap();
|
let item_str = split.next().unwrap();
|
||||||
|
assert!(!item_str.is_empty());
|
||||||
|
let item_name = Symbol::intern(item_str);
|
||||||
let path_root = split
|
let path_root = split
|
||||||
.next()
|
.next()
|
||||||
.map(|f| f.to_owned())
|
.map(|f| f.to_owned())
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
//! A few exceptions are allowed as there's known bugs in rustdoc, but this
|
//! A few exceptions are allowed as there's known bugs in rustdoc, but this
|
||||||
//! should catch the majority of "broken link" cases.
|
//! should catch the majority of "broken link" cases.
|
||||||
|
|
||||||
|
#![feature(str_split_once)]
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::env;
|
use std::env;
|
||||||
@ -232,11 +234,12 @@ fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Opti
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut parts = url.splitn(2, '#');
|
let (url, fragment) = match url.split_once('#') {
|
||||||
let url = parts.next().unwrap();
|
None => (url, None),
|
||||||
let fragment = parts.next();
|
Some((url, fragment)) => (url, Some(fragment)),
|
||||||
let mut parts = url.splitn(2, '?');
|
};
|
||||||
let url = parts.next().unwrap();
|
// NB: the `splitn` always succeeds, even if the delimiter is not present.
|
||||||
|
let url = url.splitn(2, '?').next().unwrap();
|
||||||
|
|
||||||
// Once we've plucked out the URL, parse it using our base url and
|
// Once we've plucked out the URL, parse it using our base url and
|
||||||
// then try to extract a file path.
|
// then try to extract a file path.
|
||||||
|
@ -59,11 +59,10 @@ fn verify(tomlfile: &Path, libfile: &Path, bad: &mut bool) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut parts = line.splitn(2, '=');
|
let krate = match line.split_once('=') {
|
||||||
let krate = parts.next().unwrap().trim();
|
None => continue,
|
||||||
if parts.next().is_none() {
|
Some((krate, _)) => krate.trim(),
|
||||||
continue;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// Don't worry about depending on core/std while not writing `extern crate
|
// Don't worry about depending on core/std while not writing `extern crate
|
||||||
// core/std` -- that's intentional.
|
// core/std` -- that's intentional.
|
||||||
|
@ -85,47 +85,61 @@ fn extract_error_codes(
|
|||||||
for line in f.lines() {
|
for line in f.lines() {
|
||||||
let s = line.trim();
|
let s = line.trim();
|
||||||
if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
|
if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
|
||||||
if let Some(err_code) = s.splitn(2, ':').next() {
|
let err_code = s
|
||||||
let err_code = err_code.to_owned();
|
.split_once(':')
|
||||||
if !error_codes.contains_key(&err_code) {
|
.expect(
|
||||||
error_codes.insert(err_code.clone(), false);
|
format!(
|
||||||
|
"Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} without a `:` delimiter",
|
||||||
|
s,
|
||||||
|
).as_str()
|
||||||
|
)
|
||||||
|
.0
|
||||||
|
.to_owned();
|
||||||
|
if !error_codes.contains_key(&err_code) {
|
||||||
|
error_codes.insert(err_code.clone(), false);
|
||||||
|
}
|
||||||
|
// Now we extract the tests from the markdown file!
|
||||||
|
let md_file_name = match s.split_once("include_str!(\"") {
|
||||||
|
None => continue,
|
||||||
|
Some((_, md)) => match md.split_once("\")") {
|
||||||
|
None => continue,
|
||||||
|
Some((file_name, _)) => file_name,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let path = some_or_continue!(path.parent())
|
||||||
|
.join(md_file_name)
|
||||||
|
.canonicalize()
|
||||||
|
.expect("failed to canonicalize error explanation file path");
|
||||||
|
match read_to_string(&path) {
|
||||||
|
Ok(content) => {
|
||||||
|
if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str())
|
||||||
|
&& !check_if_error_code_is_test_in_explanation(&content, &err_code)
|
||||||
|
{
|
||||||
|
errors.push(format!(
|
||||||
|
"`{}` doesn't use its own error code in compile_fail example",
|
||||||
|
path.display(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if check_error_code_explanation(&content, error_codes, err_code) {
|
||||||
|
errors.push(format!(
|
||||||
|
"`{}` uses invalid tag `compile-fail` instead of `compile_fail`",
|
||||||
|
path.display(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Now we extract the tests from the markdown file!
|
Err(e) => {
|
||||||
let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1));
|
eprintln!("Couldn't read `{}`: {}", path.display(), e);
|
||||||
let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
|
|
||||||
let path = some_or_continue!(path.parent())
|
|
||||||
.join(md_file_name)
|
|
||||||
.canonicalize()
|
|
||||||
.expect("failed to canonicalize error explanation file path");
|
|
||||||
match read_to_string(&path) {
|
|
||||||
Ok(content) => {
|
|
||||||
if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str())
|
|
||||||
&& !check_if_error_code_is_test_in_explanation(&content, &err_code)
|
|
||||||
{
|
|
||||||
errors.push(format!(
|
|
||||||
"`{}` doesn't use its own error code in compile_fail example",
|
|
||||||
path.display(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if check_error_code_explanation(&content, error_codes, err_code) {
|
|
||||||
errors.push(format!(
|
|
||||||
"`{}` uses invalid tag `compile-fail` instead of `compile_fail`",
|
|
||||||
path.display(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Couldn't read `{}`: {}", path.display(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if reached_no_explanation && s.starts_with('E') {
|
} else if reached_no_explanation && s.starts_with('E') {
|
||||||
if let Some(err_code) = s.splitn(2, ',').next() {
|
let err_code = match s.split_once(',') {
|
||||||
let err_code = err_code.to_owned();
|
None => s,
|
||||||
if !error_codes.contains_key(&err_code) {
|
Some((err_code, _)) => err_code,
|
||||||
// this check should *never* fail!
|
}
|
||||||
error_codes.insert(err_code, false);
|
.to_string();
|
||||||
}
|
if !error_codes.contains_key(&err_code) {
|
||||||
|
// this check should *never* fail!
|
||||||
|
error_codes.insert(err_code, false);
|
||||||
}
|
}
|
||||||
} else if s == ";" {
|
} else if s == ";" {
|
||||||
reached_no_explanation = true;
|
reached_no_explanation = true;
|
||||||
@ -137,12 +151,15 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, boo
|
|||||||
for line in f.lines() {
|
for line in f.lines() {
|
||||||
let s = line.trim();
|
let s = line.trim();
|
||||||
if s.starts_with("error[E") || s.starts_with("warning[E") {
|
if s.starts_with("error[E") || s.starts_with("warning[E") {
|
||||||
if let Some(err_code) = s.splitn(2, ']').next() {
|
let err_code = match s.split_once(']') {
|
||||||
if let Some(err_code) = err_code.splitn(2, '[').nth(1) {
|
None => continue,
|
||||||
let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
|
Some((err_code, _)) => match err_code.split_once('[') {
|
||||||
*nb = true;
|
None => continue,
|
||||||
}
|
Some((_, err_code)) => err_code,
|
||||||
}
|
},
|
||||||
|
};
|
||||||
|
let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
|
||||||
|
*nb = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ pub fn check(root: &Path, bad: &mut bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract source value.
|
// Extract source value.
|
||||||
let source = line.splitn(2, '=').nth(1).unwrap().trim();
|
let source = line.split_once('=').unwrap().1.trim();
|
||||||
|
|
||||||
// Ensure source is allowed.
|
// Ensure source is allowed.
|
||||||
if !ALLOWED_SOURCES.contains(&&*source) {
|
if !ALLOWED_SOURCES.contains(&&*source) {
|
||||||
|
@ -112,6 +112,7 @@ pub fn check(
|
|||||||
let gate_test_str = "gate-test-";
|
let gate_test_str = "gate-test-";
|
||||||
|
|
||||||
let feature_name = match line.find(gate_test_str) {
|
let feature_name = match line.find(gate_test_str) {
|
||||||
|
// NB: the `splitn` always succeeds, even if the delimiter is not present.
|
||||||
Some(i) => line[i + gate_test_str.len()..].splitn(2, ' ').next().unwrap(),
|
Some(i) => line[i + gate_test_str.len()..].splitn(2, ' ').next().unwrap(),
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
//! This library contains the tidy lints and exposes it
|
//! This library contains the tidy lints and exposes it
|
||||||
//! to be used by tools.
|
//! to be used by tools.
|
||||||
|
|
||||||
|
#![feature(str_split_once)]
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use walkdir::{DirEntry, WalkDir};
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
@ -19,14 +19,11 @@ pub fn check(path: &Path, bad: &mut bool) {
|
|||||||
//
|
//
|
||||||
// For now, just make sure that there is a corresponding
|
// For now, just make sure that there is a corresponding
|
||||||
// `$testname.rs` file.
|
// `$testname.rs` file.
|
||||||
let testname = file_path
|
//
|
||||||
.file_name()
|
// NB: We do not use file_stem() as some file names have multiple `.`s and we
|
||||||
.unwrap()
|
// must strip all of them.
|
||||||
.to_str()
|
let testname =
|
||||||
.unwrap()
|
file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0;
|
||||||
.splitn(2, '.')
|
|
||||||
.next()
|
|
||||||
.unwrap();
|
|
||||||
if !file_path.with_file_name(testname).with_extension("rs").exists() {
|
if !file_path.with_file_name(testname).with_extension("rs").exists() {
|
||||||
println!("Stray file with UI testing output: {:?}", file_path);
|
println!("Stray file with UI testing output: {:?}", file_path);
|
||||||
*bad = true;
|
*bad = true;
|
||||||
|
Loading…
Reference in New Issue
Block a user