From bb50ddf598ab27c69a6020eef9e5fe6f5e2a65b3 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 5 Jul 2020 13:15:21 +0000 Subject: [PATCH] Use `str::strip_{prefix,suffix}` when possible --- src/bootstrap/compile.rs | 9 +- src/bootstrap/doc.rs | 17 ++- src/bootstrap/lib.rs | 11 +- src/librustc_apfloat/ieee.rs | 19 +-- src/librustc_ast/util/comments.rs | 58 +++++---- src/librustc_codegen_llvm/intrinsic.rs | 6 +- src/librustc_codegen_ssa/back/link.rs | 7 +- src/librustc_driver/args.rs | 3 +- src/librustc_incremental/persist/fs.rs | 14 +-- src/librustc_llvm/build.rs | 35 +++--- src/librustc_metadata/locator.rs | 18 ++- .../borrow_check/diagnostics/move_errors.rs | 14 +-- .../diagnostics/mutability_errors.rs | 38 +++--- .../borrow_check/diagnostics/region_errors.rs | 4 +- src/librustc_parse/parser/expr.rs | 5 +- src/librustc_resolve/late/diagnostics.rs | 15 ++- src/librustc_session/search_paths.rs | 20 ++-- src/librustc_target/abi/mod.rs | 29 ++++- .../traits/error_reporting/suggestions.rs | 4 +- src/librustc_typeck/check/demand.rs | 38 +++--- src/librustc_typeck/check/intrinsic.rs | 48 ++++---- src/librustc_typeck/check/mod.rs | 16 ++- src/librustc_typeck/check/op.rs | 8 +- src/librustc_typeck/collect.rs | 5 +- src/librustdoc/clean/utils.rs | 14 ++- src/librustdoc/html/markdown.rs | 112 ++++++++++-------- src/librustdoc/html/render.rs | 6 +- src/librustdoc/html/sources.rs | 3 +- src/librustdoc/markdown.rs | 4 +- .../passes/collect_intra_doc_links.rs | 94 +++++++-------- src/libstd/sys/windows/fs.rs | 6 +- src/tools/clippy/clippy_dev/src/ra_setup.rs | 11 +- src/tools/clippy/clippy_lints/src/doc.rs | 79 ++++++------ src/tools/clippy/clippy_lints/src/loops.rs | 4 +- .../clippy/clippy_lints/src/misc_early.rs | 6 +- .../clippy_lints/src/redundant_clone.rs | 7 +- .../clippy_lints/src/utils/numeric_literal.rs | 6 +- src/tools/compiletest/src/header.rs | 63 +++++----- src/tools/compiletest/src/main.rs | 7 +- src/tools/compiletest/src/runtest.rs | 11 +- src/tools/tidy/src/features.rs | 4 +- 41 files changed, 457 insertions(+), 421 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 84545dcedb6d9..9b4926f28d4ed 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -963,10 +963,11 @@ pub fn run_cargo( .collect::>(); for (prefix, extension, expected_len) in toplevel { let candidates = contents.iter().filter(|&&(_, ref filename, ref meta)| { - filename.starts_with(&prefix[..]) - && filename[prefix.len()..].starts_with('-') - && filename.ends_with(&extension[..]) - && meta.len() == expected_len + meta.len() == expected_len + && filename + .strip_prefix(&prefix[..]) + .map(|s| s.starts_with('-') && s.ends_with(&extension[..])) + .unwrap_or(false) }); let max = candidates .max_by_key(|&&(_, _, ref metadata)| FileTime::from_last_modification_time(metadata)); diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index d02c19467ee68..c0abf4c5da3b0 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -461,8 +461,8 @@ impl Step for Std { builder.run(&mut cargo.into()); }; - let krates = ["alloc", "core", "std", "proc_macro", "test"]; - for krate in &krates { + static KRATES: &[&str] = &["alloc", "core", "std", "proc_macro", "test"]; + for krate in KRATES { run_cargo_rustdoc_for(krate); } builder.cp_r(&my_out, &out); @@ -470,13 +470,12 @@ impl Step for Std { // Look for src/libstd, src/libcore etc in the `x.py doc` arguments and // open the corresponding rendered docs. for path in builder.paths.iter().map(components_simplified) { - if path.get(0) == Some(&"src") - && path.get(1).map_or(false, |dir| dir.starts_with("lib")) - { - let requested_crate = &path[1][3..]; - if krates.contains(&requested_crate) { - let index = out.join(requested_crate).join("index.html"); - open(builder, &index); + if let ["src", path, ..] = path.as_slice() { + if let Some(krate) = path.strip_prefix("lib") { + if KRATES.contains(&krate) { + let index = out.join(krate).join("index.html"); + open(builder, &index); + } } } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index b973889448651..783a64c3581f9 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -436,10 +436,9 @@ impl Build { output(Command::new(&build.initial_rustc).arg("--version").arg("--verbose")); let local_release = local_version_verbose .lines() - .filter(|x| x.starts_with("release:")) + .filter_map(|x| x.strip_prefix("release:")) .next() .unwrap() - .trim_start_matches("release:") .trim(); let my_version = channel::CFG_RELEASE_NUM; if local_release.split('.').take(2).eq(my_version.split('.').take(2)) { @@ -1089,10 +1088,10 @@ impl Build { let toml_file_name = self.src.join(&format!("src/tools/{}/Cargo.toml", package)); let toml = t!(fs::read_to_string(&toml_file_name)); for line in toml.lines() { - let prefix = "version = \""; - let suffix = "\""; - if line.starts_with(prefix) && line.ends_with(suffix) { - return line[prefix.len()..line.len() - suffix.len()].to_string(); + if let Some(stripped) = + line.strip_prefix("version = \"").and_then(|s| s.strip_suffix("\"")) + { + return stripped.to_owned(); } } diff --git a/src/librustc_apfloat/ieee.rs b/src/librustc_apfloat/ieee.rs index e3d941cad7ae5..c8796b9395a2e 100644 --- a/src/librustc_apfloat/ieee.rs +++ b/src/librustc_apfloat/ieee.rs @@ -1191,21 +1191,24 @@ impl Float for IeeeFloat { } // Handle a leading minus sign. - let minus = s.starts_with('-'); - if minus || s.starts_with('+') { - s = &s[1..]; - if s.is_empty() { - return Err(ParseError("String has no digits")); + let minus = { + let m = s.strip_prefix('-'); + if let Some(remnant) = m.or_else(|| s.strip_prefix('+')) { + s = remnant; + if s.is_empty() { + return Err(ParseError("String has no digits")); + } } - } + m.is_some() + }; // Adjust the rounding mode for the absolute value below. if minus { round = -round; } - let r = if s.starts_with("0x") || s.starts_with("0X") { - s = &s[2..]; + let r = if let Some(tail) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) { + s = tail; if s.is_empty() { return Err(ParseError("Invalid string")); } diff --git a/src/librustc_ast/util/comments.rs b/src/librustc_ast/util/comments.rs index 9874754fcd2f7..d2bc918ed56b9 100644 --- a/src/librustc_ast/util/comments.rs +++ b/src/librustc_ast/util/comments.rs @@ -29,27 +29,31 @@ pub struct Comment { } pub fn is_line_doc_comment(s: &str) -> bool { - let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/') - || s.starts_with("//!"); - debug!("is {:?} a doc comment? {}", s, res); - res + let yes = match s.as_bytes() { + [b'/', b'/', b'/', c, ..] => *c != b'/', + [b'/', b'/', b'/', ..] => true, + [b'/', b'/', b'!', ..] => true, + _ => false, + }; + debug!("is {:?} a line doc comment? {}", s, yes); + yes } pub fn is_block_doc_comment(s: &str) -> bool { - // Prevent `/**/` from being parsed as a doc comment - let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*') - || s.starts_with("/*!")) - && s.len() >= 5; - debug!("is {:?} a doc comment? {}", s, res); - res + // Previously, `/**/` was incorrectly regarded as a doc comment because it + // starts with `/**` and ends with `*/`. However, this caused an ICE + // because some code assumed that the length of a doc comment is at least 5. + let yes = match s.as_bytes() { + [b'/', b'*', b'*', c, _, ..] => *c != b'*', + [b'/', b'*', b'!', _, _, ..] => true, + _ => false, + }; + debug!("is {:?} a block doc comment? {}", s, yes); + yes } -// FIXME(#64197): Try to privatize this again. pub fn is_doc_comment(s: &str) -> bool { - (s.starts_with("///") && is_line_doc_comment(s)) - || s.starts_with("//!") - || (s.starts_with("/**") && is_block_doc_comment(s)) - || s.starts_with("/*!") + is_line_doc_comment(s) || is_block_doc_comment(s) } pub fn doc_comment_style(comment: &str) -> ast::AttrStyle { @@ -127,22 +131,26 @@ pub fn strip_doc_comment_decoration(comment: &str) -> String { const ONELINERS: &[&str] = &["///!", "///", "//!", "//"]; for prefix in ONELINERS { - if comment.starts_with(*prefix) { - return (&comment[prefix.len()..]).to_string(); + if let Some(tail) = comment.strip_prefix(*prefix) { + return tail.to_owned(); } } - if comment.starts_with("/*") { - let lines = - comment[3..comment.len() - 2].lines().map(|s| s.to_string()).collect::>(); + match comment + .strip_prefix("/**") + .or_else(|| comment.strip_prefix("/*!")) + .and_then(|s| s.strip_suffix("*/")) + { + Some(doc) => { + let lines = doc.lines().map(|s| s.to_string()).collect::>(); - let lines = vertical_trim(lines); - let lines = horizontal_trim(lines); + let lines = vertical_trim(lines); + let lines = horizontal_trim(lines); - return lines.join("\n"); + lines.join("\n") + } + _ => panic!("not a doc-comment: {}", comment), } - - panic!("not a doc-comment: {}", comment); } /// Returns `None` if the first `col` chars of `s` contain a non-whitespace char. diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index de90ac0bac1d3..bc63ec4d28a86 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -1289,8 +1289,8 @@ fn generic_simd_intrinsic( )); } - if name.starts_with("simd_shuffle") { - let n: u64 = name["simd_shuffle".len()..].parse().unwrap_or_else(|_| { + if let Some(tail) = name.strip_prefix("simd_shuffle") { + let n: u64 = tail.parse().unwrap_or_else(|_| { span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?") }); @@ -1307,7 +1307,7 @@ fn generic_simd_intrinsic( require!( in_elem == ret_ty.simd_type(tcx), "expected return element type `{}` (element of input `{}`), \ - found `{}` with element type `{}`", + found `{}` with element type `{}`", in_elem, in_ty, ret_ty, diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 3adaa07db91b0..12014a5039dd0 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1901,10 +1901,9 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( // Converts a library file-stem into a cc -l argument fn unlib<'a>(config: &config::Config, stem: &'a str) -> &'a str { - if stem.starts_with("lib") && !config.target.options.is_like_windows { - &stem[3..] - } else { - stem + match stem.strip_prefix("lib") { + Some(tail) if !config.target.options.is_like_windows => tail, + _ => stem, } } diff --git a/src/librustc_driver/args.rs b/src/librustc_driver/args.rs index 5686819c61b40..4f2febf04b135 100644 --- a/src/librustc_driver/args.rs +++ b/src/librustc_driver/args.rs @@ -4,8 +4,7 @@ use std::fs; use std::io; pub fn arg_expand(arg: String) -> Result, Error> { - if arg.starts_with('@') { - let path = &arg[1..]; + if let Some(path) = arg.strip_prefix('@') { let file = match fs::read_to_string(path) { Ok(file) => file, Err(ref err) if err.kind() == io::ErrorKind::InvalidData => { diff --git a/src/librustc_incremental/persist/fs.rs b/src/librustc_incremental/persist/fs.rs index 4926f726f3593..bcbe34974618e 100644 --- a/src/librustc_incremental/persist/fs.rs +++ b/src/librustc_incremental/persist/fs.rs @@ -697,13 +697,13 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { let lock_file_to_session_dir: FxHashMap> = lock_files .into_iter() .map(|lock_file_name| { - assert!(lock_file_name.ends_with(LOCK_FILE_EXT)); - let dir_prefix_end = lock_file_name.len() - LOCK_FILE_EXT.len(); - let session_dir = { - let dir_prefix = &lock_file_name[0..dir_prefix_end]; - session_directories.iter().find(|dir_name| dir_name.starts_with(dir_prefix)) - }; - (lock_file_name, session_dir.map(String::clone)) + if let Some(dir_prefix) = lock_file_name.strip_suffix(LOCK_FILE_EXT) { + let dir = + session_directories.iter().find(|dir_name| dir_name.starts_with(dir_prefix)); + (lock_file_name, dir.map(String::clone)) + } else { + panic!("{:?} does not end with {}", lock_file_name, LOCK_FILE_EXT); + } }) .collect(); diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs index d25f8bd1b8c58..7c0b956ab40d9 100644 --- a/src/librustc_llvm/build.rs +++ b/src/librustc_llvm/build.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::env; use std::path::{Path, PathBuf}; use std::process::Command; @@ -188,10 +189,8 @@ fn main() { cmd.args(&components); for lib in output(&mut cmd).split_whitespace() { - let name = if lib.starts_with("-l") { - &lib[2..] - } else if lib.starts_with('-') { - &lib[1..] + let name = if let Some(tail) = lib.strip_prefix("-l").or_else(|| lib.strip_prefix('-')) { + tail } else if Path::new(lib).exists() { // On MSVC llvm-config will print the full name to libraries, but // we're only interested in the name part @@ -227,18 +226,14 @@ fn main() { let mut cmd = Command::new(&llvm_config); cmd.arg(llvm_link_arg).arg("--ldflags"); for lib in output(&mut cmd).split_whitespace() { - if is_crossed { - if lib.starts_with("-LIBPATH:") { - println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target)); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target)); - } - } else if lib.starts_with("-LIBPATH:") { - println!("cargo:rustc-link-search=native={}", &lib[9..]); - } else if lib.starts_with("-l") { - println!("cargo:rustc-link-lib={}", &lib[2..]); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", &lib[2..]); + if let Some(tail) = lib.strip_prefix("-LIBPATH:").or_else(|| lib.strip_prefix("-L")) { + let lib: Cow<'_, str> = match is_crossed { + true => tail.replace(&host, &target).into(), + false => tail.into(), + }; + println!("cargo:rustc-link-search=native={}", lib); + } else if let Some(tail) = lib.strip_prefix("-l") { + println!("cargo:rustc-link-lib={}", tail); } } @@ -249,10 +244,10 @@ fn main() { let llvm_linker_flags = env::var_os("LLVM_LINKER_FLAGS"); if let Some(s) = llvm_linker_flags { for lib in s.into_string().unwrap().split_whitespace() { - if lib.starts_with("-l") { - println!("cargo:rustc-link-lib={}", &lib[2..]); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", &lib[2..]); + if let Some(tail) = lib.strip_prefix("-l") { + println!("cargo:rustc-link-lib={}", tail); + } else if let Some(tail) = lib.strip_prefix("-L") { + println!("cargo:rustc-link-search=native={}", tail); } } } diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index 1bdac1039b55a..d708ba06f4ac9 100644 --- a/src/librustc_metadata/locator.rs +++ b/src/librustc_metadata/locator.rs @@ -551,12 +551,18 @@ impl<'a> CrateLocator<'a> { None => return FileDoesntMatch, Some(file) => file, }; - let (hash, found_kind) = if file.starts_with(&rlib_prefix) && file.ends_with(".rlib") { - (&file[(rlib_prefix.len())..(file.len() - ".rlib".len())], CrateFlavor::Rlib) - } else if file.starts_with(&rlib_prefix) && file.ends_with(".rmeta") { - (&file[(rlib_prefix.len())..(file.len() - ".rmeta".len())], CrateFlavor::Rmeta) - } else if file.starts_with(&dylib_prefix) && file.ends_with(&dypair.1) { - (&file[(dylib_prefix.len())..(file.len() - dypair.1.len())], CrateFlavor::Dylib) + let (hash, found_kind) = if let Some(stripped) = + file.strip_prefix(&rlib_prefix).and_then(|p| p.strip_suffix(".rlib")) + { + (stripped, CrateFlavor::Rlib) + } else if let Some(stripped) = + file.strip_prefix(&rlib_prefix).and_then(|p| p.strip_suffix(".rmeta")) + { + (stripped, CrateFlavor::Rmeta) + } else if let Some(stripped) = + file.strip_prefix(&dylib_prefix).and_then(|p| p.strip_suffix(&dypair.1)) + { + (stripped, CrateFlavor::Dylib) } else { if file.starts_with(&staticlib_prefix) && file.ends_with(&staticpair.1) { staticlibs diff --git a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs index 4883b08e42442..628d217f05540 100644 --- a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs @@ -488,14 +488,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { { if let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span) { - if pat_snippet.starts_with('&') { - let pat_snippet = pat_snippet[1..].trim_start(); - let (suggestion, to_remove) = if pat_snippet.starts_with("mut") - && pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace) - { - (pat_snippet["mut".len()..].trim_start(), "&mut") - } else { - (pat_snippet, "&") + if let Some(snippet) = pat_snippet.strip_prefix('&').map(str::trim_start) { + let (suggestion, to_remove) = match snippet.strip_prefix("mut") { + Some(tail) if tail.starts_with(rustc_lexer::is_whitespace) => { + (tail.trim_start(), "&mut") + } + _ => (snippet, "&"), }; suggestions.push((pat_span, to_remove, suggestion.to_owned())); } diff --git a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs index b4bc89e827daa..0c13cea8bde52 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs @@ -605,10 +605,11 @@ fn suggest_ampmut_self<'tcx>( // When we want to suggest a user change a local variable to be a `&mut`, there // are three potential "obvious" things to highlight: // +// ```text // let ident [: Type] [= RightHandSideExpression]; // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ // (1.) (2.) (3.) -// +// ``` // We can always fallback on highlighting the first. But chances are good that // the user experience will be better if we highlight one of the others if possible; // for example, if the RHS is present and the Type is not, then the type is going to @@ -625,15 +626,13 @@ fn suggest_ampmut<'tcx>( ) -> (Span, String) { if let Some(assignment_rhs_span) = opt_assignment_rhs_span { if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) { - if let (true, Some(ws_pos)) = - (src.starts_with("&'"), src.find(|c: char| -> bool { c.is_whitespace() })) + if let Some((tail, idx)) = + src.strip_prefix("&'").and_then(|s| s.find(char::is_whitespace).map(|idx| (s, idx))) { - let lt_name = &src[1..ws_pos]; - let ty = &src[ws_pos..]; - return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty)); - } else if src.starts_with('&') { - let borrowed_expr = &src[1..]; - return (assignment_rhs_span, format!("&mut {}", borrowed_expr)); + let (lt_name, ty) = tail.split_at(idx); + return (assignment_rhs_span, format!("&'{} mut {}", lt_name, ty)); + } else if let Some(tail) = src.strip_prefix("&") { + return (assignment_rhs_span, format!("&mut {}", tail)); } } } @@ -649,12 +648,11 @@ fn suggest_ampmut<'tcx>( }; if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) { - if let (true, Some(ws_pos)) = - (src.starts_with("&'"), src.find(|c: char| -> bool { c.is_whitespace() })) - { - let lt_name = &src[1..ws_pos]; - let ty = &src[ws_pos..]; - return (highlight_span, format!("&{} mut{}", lt_name, ty)); + if let Some(tail) = src.strip_prefix("&'") { + if let Some(idx) = tail.find(char::is_whitespace) { + let (lt_name, ty) = tail.split_at(idx); + return (highlight_span, format!("&'{} mut{}", lt_name, ty)); + } } } @@ -726,10 +724,10 @@ fn annotate_struct_field( /// If possible, suggest replacing `ref` with `ref mut`. fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option { let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?; - if hi_src.starts_with("ref") && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace) { - let replacement = format!("ref mut{}", &hi_src["ref".len()..]); - Some(replacement) - } else { - None + match hi_src.strip_prefix("ref") { + Some(tail) if tail.starts_with(rustc_lexer::is_whitespace) => { + Some(format!("ref mut{}", tail)) + } + _ => None, } } diff --git a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs index 26c2aea41d5dc..3751273360013 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs @@ -634,9 +634,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } else { "'_".to_string() }; - let suggestion = if snippet.ends_with(';') { + let suggestion = if let Some(sugg) = snippet.strip_suffix(';') { // `type X = impl Trait;` - format!("{} + {};", &snippet[..snippet.len() - 1], suggestable_fr_name) + format!("{} + {};", sugg, suggestable_fr_name) } else { format!("{} + {}", snippet, suggestable_fr_name) }; diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index abb444933536f..01bba75047abd 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -1305,7 +1305,10 @@ impl<'a> Parser<'a> { fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) { // Checks if `s` looks like i32 or u1234 etc. fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { - s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) + s.len() > 1 + && s.strip_prefix(first_chars) + .map(|s| s.chars().all(|c| c.is_ascii_digit())) + .unwrap_or(false) } let token::Lit { kind, suffix, .. } = lit; diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index e469ca80c590a..322aea65c29b2 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -1171,12 +1171,15 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { }); for param in params { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { - if snippet.starts_with('&') && !snippet.starts_with("&'") { - introduce_suggestion - .push((param.span, format!("&'a {}", &snippet[1..]))); - } else if snippet.starts_with("&'_ ") { - introduce_suggestion - .push((param.span, format!("&'a {}", &snippet[4..]))); + if let Some(snip) = snippet.strip_prefix("&") { + let sugg = match snip.strip_prefix("'") { + None => snip, + Some(tail) => match tail.strip_prefix("_ ") { + Some(tail) => tail, + None => continue, + }, + }; + introduce_suggestion.push((param.span, format!("&'a {}", sugg))); } } } diff --git a/src/librustc_session/search_paths.rs b/src/librustc_session/search_paths.rs index 4ff06acaa1fd4..c17ef45007e6f 100644 --- a/src/librustc_session/search_paths.rs +++ b/src/librustc_session/search_paths.rs @@ -56,16 +56,16 @@ impl PathKind { impl SearchPath { pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self { - let (kind, path) = if path.starts_with("native=") { - (PathKind::Native, &path["native=".len()..]) - } else if path.starts_with("crate=") { - (PathKind::Crate, &path["crate=".len()..]) - } else if path.starts_with("dependency=") { - (PathKind::Dependency, &path["dependency=".len()..]) - } else if path.starts_with("framework=") { - (PathKind::Framework, &path["framework=".len()..]) - } else if path.starts_with("all=") { - (PathKind::All, &path["all=".len()..]) + let (kind, path) = if let Some(lib) = path.strip_prefix("native=") { + (PathKind::Native, lib) + } else if let Some(lib) = path.strip_prefix("crate=") { + (PathKind::Crate, lib) + } else if let Some(lib) = path.strip_prefix("dependency=") { + (PathKind::Dependency, lib) + } else if let Some(lib) = path.strip_prefix("framework=") { + (PathKind::Framework, lib) + } else if let Some(lib) = path.strip_prefix("all=") { + (PathKind::All, lib) } else { (PathKind::All, path) }; diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index c79e9bb289008..08f0fa671c565 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -100,11 +100,18 @@ impl TargetDataLayout { for spec in target.data_layout.split('-') { let spec_parts = spec.split(':').collect::>(); + // FIXME(#51114) + let mut remnant; + match &*spec_parts { ["e"] => dl.endian = Endian::Little, ["E"] => dl.endian = Endian::Big, - [p] if p.starts_with('P') => { - dl.instruction_address_space = parse_address_space(&p[1..], "P")? + [p] if { + remnant = p.strip_prefix('P'); + remnant.is_some() + } => + { + dl.instruction_address_space = parse_address_space(remnant.unwrap(), "P")? } ["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?, ["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?, @@ -113,8 +120,13 @@ impl TargetDataLayout { dl.pointer_size = size(s, p)?; dl.pointer_align = align(a, p)?; } - [s, ref a @ ..] if s.starts_with('i') => { - let bits = match s[1..].parse::() { + [s, ref a @ ..] + if { + remnant = s.strip_prefix('i'); + remnant.is_some() + } => + { + let bits = match remnant.unwrap().parse::() { Ok(bits) => bits, Err(_) => { size(&s[1..], "i")?; // For the user error. @@ -137,8 +149,13 @@ impl TargetDataLayout { dl.i128_align = a; } } - [s, ref a @ ..] if s.starts_with('v') => { - let v_size = size(&s[1..], "v")?; + [s, ref a @ ..] + if { + remnant = s.strip_prefix('v'); + remnant.is_some() + } => + { + let v_size = size(remnant.unwrap(), "v")?; let a = align(a, s)?; if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) { v.1 = a; diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index cdfe5f9f92db0..ae3f920a0aebd 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -2039,11 +2039,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation); if self.predicate_may_hold(&try_obligation) && impls_future { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - if snippet.ends_with('?') { + if let Some(expr) = snippet.strip_suffix('?') { err.span_suggestion( span, "consider using `.await` here", - format!("{}.await?", snippet.trim_end_matches('?')), + format!("{}.await?", expr), Applicability::MaybeIncorrect, ); } diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 85c073ca30034..3da8c61ca6804 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -15,6 +15,7 @@ use rustc_span::Span; use super::method::probe; +use std::borrow::Cow; use std::fmt; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -360,15 +361,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn replace_prefix(&self, s: A, old: B, new: C) -> Option - where - A: AsRef, - B: AsRef, - C: AsRef, - { - let s = s.as_ref(); - let old = old.as_ref(); - if s.starts_with(old) { Some(new.as_ref().to_owned() + &s[old.len()..]) } else { None } + fn replace_prefix(&self, s: &str, old: &str, new: Cow<'_, str>) -> Option { + s.strip_prefix(old).map(|r| new.into_owned() + r) } /// This function is used to determine potential "simple" improvements or users' errors and @@ -416,7 +410,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind { if let Ok(src) = sm.span_to_snippet(sp) { - if let Some(src) = self.replace_prefix(src, "b\"", "\"") { + if let Some(src) = self.replace_prefix(&src, "b\"", "\"".into()) { return Some(( sp, "consider removing the leading `b`", @@ -430,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind { if let Ok(src) = sm.span_to_snippet(sp) { - if let Some(src) = self.replace_prefix(src, "\"", "b\"") { + if let Some(src) = self.replace_prefix(&src, "\"", "b\"".into()) { return Some(( sp, "consider adding a leading `b`", @@ -553,7 +547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we may want to suggest removing a `&`. if sm.is_imported(expr.span) { if let Ok(src) = sm.span_to_snippet(sp) { - if let Some(src) = self.replace_prefix(src, "&", "") { + if let Some(src) = self.replace_prefix(&src, "&", "".into()) { return Some(( sp, "consider removing the borrow", @@ -589,9 +583,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let new_prefix = "&mut ".to_owned() + derefs; match mutbl_a { hir::Mutability::Mut => { - if let Some(s) = - self.replace_prefix(src, "&mut ", new_prefix) - { + if let Some(s) = self.replace_prefix( + &src, + "&mut ", + new_prefix.into(), + ) { Some((s, Applicability::MachineApplicable)) } else { None @@ -599,7 +595,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } hir::Mutability::Not => { if let Some(s) = - self.replace_prefix(src, "&", new_prefix) + self.replace_prefix(&src, "&", new_prefix.into()) { Some((s, Applicability::Unspecified)) } else { @@ -612,9 +608,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let new_prefix = "&".to_owned() + derefs; match mutbl_a { hir::Mutability::Mut => { - if let Some(s) = - self.replace_prefix(src, "&mut ", new_prefix) - { + if let Some(s) = self.replace_prefix( + &src, + "&mut ", + new_prefix.into(), + ) { Some((s, Applicability::MachineApplicable)) } else { None @@ -622,7 +620,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } hir::Mutability::Not => { if let Some(s) = - self.replace_prefix(src, "&", new_prefix) + self.replace_prefix(&src, "&", new_prefix.into()) { Some((s, Applicability::MachineApplicable)) } else { diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 6205088adadb7..cba6687ef766b 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -98,12 +98,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { }) }; - let (n_tps, inputs, output, unsafety) = if name.starts_with("atomic_") { - let split: Vec<&str> = name.split('_').collect(); - assert!(split.len() >= 2, "Atomic intrinsic in an incorrect format"); - + let (n_tps, inputs, output, unsafety) = if let Some(tail) = name.strip_prefix("atomic_") { //We only care about the operation here - let (n_tps, inputs, output) = match split[1] { + let op = tail.split('_').next().expect("`str::split` always return self"); + let (n_tps, inputs, output) = match op { "cxchg" | "cxchgweak" => ( 1, vec![tcx.mk_mut_ptr(param(0)), param(0), param(0)], @@ -391,6 +389,8 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) let name = it.ident.as_str(); + // FIXME(#51114) + let suffix; let (n_tps, inputs, output) = match &*name { "simd_eq" | "simd_ne" | "simd_lt" | "simd_le" | "simd_gt" | "simd_ge" => { (2, vec![param(0), param(0)], param(1)) @@ -436,23 +436,29 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) | "simd_reduce_max" | "simd_reduce_min_nanless" | "simd_reduce_max_nanless" => (2, vec![param(0)], param(1)), - name if name.starts_with("simd_shuffle") => match name["simd_shuffle".len()..].parse() { - Ok(n) => { - let params = vec![param(0), param(0), tcx.mk_array(tcx.types.u32, n)]; - (2, params, param(1)) - } - Err(_) => { - struct_span_err!( - tcx.sess, - it.span, - E0439, - "invalid `simd_shuffle`, needs length: `{}`", - name - ) - .emit(); - return; + name if { + suffix = name.strip_prefix("simd_shuffle"); + suffix.is_some() + } => + { + match suffix.unwrap().parse::() { + Ok(n) => { + let params = vec![param(0), param(0), tcx.mk_array(tcx.types.u32, n)]; + (2, params, param(1)) + } + Err(_) => { + struct_span_err!( + tcx.sess, + it.span, + E0439, + "invalid `simd_shuffle`, needs length: `{}`", + name + ) + .emit(); + return; + } } - }, + } _ => { let msg = format!("unrecognized platform-specific intrinsic function: `{}`", name); tcx.sess.struct_span_err(it.span, &msg).emit(); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 58fd0f989c678..82ecdcd214985 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5068,17 +5068,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None // do not suggest code that is already there (#53348) } else { let method_call_list = [".to_vec()", ".to_string()"]; - let sugg = if receiver.ends_with(".clone()") - && method_call_list.contains(&method_call.as_str()) - { - let max_len = receiver.rfind('.').unwrap(); - format!("{}{}", &receiver[..max_len], method_call) - } else { - if expr.precedence().order() < ExprPrecedence::MethodCall.order() { + let contained = method_call_list.contains(&method_call.as_str()); + let sugg = match receiver.strip_suffix(".clone()") { + Some(rx) if contained => format!("{}{}", rx, method_call), + _ if expr.precedence().order() + < ExprPrecedence::MethodCall.order() => + { format!("({}){}", receiver, method_call) - } else { - format!("{}{}", receiver, method_call) } + _ => format!("{}{}", receiver, method_call), }; Some(if is_struct_pat_shorthand_field { format!("{}: {}", receiver, sugg) diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index a1e060b97ad28..82a3904c5ef40 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -585,10 +585,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { msg }, - if lstring.starts_with('&') { + if let Some(tail) = lstring.strip_prefix('&') { // let a = String::new(); // let _ = &a + "bar"; - lstring[1..].to_string() + tail.to_string() } else { format!("{}.to_owned()", lstring) }, @@ -613,10 +613,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_assign, ) { (Ok(l), Ok(r), IsAssign::No) => { - let to_string = if l.starts_with('&') { + let to_string = if let Some(tail) = l.strip_prefix('&') { // let a = String::new(); let b = String::new(); // let _ = &a + b; - l[1..].to_string() + tail.to_string() } else { format!("{}.to_owned()", l) }; diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 8920203e6af40..64d5d627ead28 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -2195,9 +2195,8 @@ fn from_target_feature( item.span(), format!("`{}` is not valid for this target", feature), ); - if feature.starts_with('+') { - let valid = whitelist.contains_key(&feature[1..]); - if valid { + if let Some(feat) = feature.strip_prefix('+') { + if whitelist.contains_key(feat) { err.help("consider removing the leading `+` in the feature name"); } } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 832b2420c2389..1868310abc125 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -481,12 +481,14 @@ pub fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String { _ => { let mut s = n.to_string(); // array lengths are obviously usize - if s.ends_with("_usize") { - let n = s.len() - "_usize".len(); - s.truncate(n); - if s.ends_with(": ") { - let n = s.len() - ": ".len(); - s.truncate(n); + if let Some(head) = s.strip_suffix("_usize") { + let new_len = match head.strip_suffix(": ") { + None => head.len(), + Some(hhead) => hhead.len(), + }; + // SAFETY: `new_len` should be in between char boundary + unsafe { + s.as_mut_vec().set_len(new_len); } } s diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 7a6626766d388..77c59e61c493b 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -111,6 +111,25 @@ enum Line<'a> { } impl<'a> Line<'a> { + fn from_str(s: &'a str) -> Self { + let trimmed = s.trim(); + match trimmed.strip_prefix("#") { + Some(tail) => match tail.strip_prefix("#") { + // `##text` rendered as `#text`. + Some(_) => Line::Shown(tail.into()), + None => match tail.as_bytes() { + // `#` will be hidden. + [] => Line::Hidden(""), + // `# text` will be hidden. + [b' ' | b'\t', ..] => Line::Hidden(tail), + // `#text` will be shown as it could be ``#[attr]` + _ => Line::Shown(s.into()), + }, + }, + None => Line::Shown(s.into()), + } + } + fn for_html(self) -> Option> { match self { Line::Shown(l) => Some(l), @@ -126,26 +145,6 @@ impl<'a> Line<'a> { } } -// FIXME: There is a minor inconsistency here. For lines that start with ##, we -// have no easy way of removing a potential single space after the hashes, which -// is done in the single # case. This inconsistency seems okay, if non-ideal. In -// order to fix it we'd have to iterate to find the first non-# character, and -// then reallocate to remove it; which would make us return a String. -fn map_line(s: &str) -> Line<'_> { - let trimmed = s.trim(); - if trimmed.starts_with("##") { - Line::Shown(Cow::Owned(s.replacen("##", "#", 1))) - } else if trimmed.starts_with("# ") { - // # text - Line::Hidden(&trimmed[2..]) - } else if trimmed == "#" { - // We cannot handle '#text' because it could be #[attr]. - Line::Hidden("") - } else { - Line::Shown(Cow::Borrowed(s)) - } -} - /// Convert chars from a title for an id. /// /// "Hello, world!" -> "hello-world" @@ -226,7 +225,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { _ => {} } } - let lines = origtext.lines().filter_map(|l| map_line(l).for_html()); + let lines = origtext.lines().filter_map(|l| Line::from_str(l).for_html()); let text = lines.collect::>>().join("\n"); // insert newline to clearly separate it from the // previous block so we can shorten the html output @@ -239,7 +238,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { } let test = origtext .lines() - .map(|l| map_line(l).for_code()) + .map(|l| Line::from_str(l).for_code()) .collect::>>() .join("\n"); let krate = krate.as_ref().map(|s| &**s); @@ -608,7 +607,7 @@ pub fn find_testable_code( } let text = test_s .lines() - .map(|l| map_line(l).for_code()) + .map(|l| Line::from_str(l).for_code()) .collect::>>() .join("\n"); @@ -737,6 +736,8 @@ impl LangString { data.original = string.to_owned(); let tokens = string.split(|c: char| !(c == '_' || c == '-' || c.is_alphanumeric())); + // FIXME(#51114) + let mut suffix = None; for token in tokens { match token.trim() { "" => {} @@ -752,9 +753,13 @@ impl LangString { data.ignore = Ignore::All; seen_rust_tags = !seen_other_tags; } - x if x.starts_with("ignore-") => { + x if { + suffix = x.strip_prefix("ignore-"); + suffix.is_some() + } => + { if enable_per_target_ignores { - ignores.push(x.trim_start_matches("ignore-").to_owned()); + ignores.push(suffix.unwrap().to_owned()); seen_rust_tags = !seen_other_tags; } } @@ -775,11 +780,19 @@ impl LangString { seen_rust_tags = !seen_other_tags || seen_rust_tags; data.no_run = true; } - x if x.starts_with("edition") => { - data.edition = x[7..].parse::().ok(); + x if { + suffix = x.strip_prefix("edition"); + suffix.is_some() + } => + { + data.edition = suffix.unwrap().parse::().ok(); } - x if allow_error_code_check && x.starts_with('E') && x.len() == 5 => { - if x[1..].parse::().is_ok() { + x if allow_error_code_check && x.len() == 5 && { + suffix = x.strip_prefix('E'); + suffix.is_some() + } => + { + if suffix.unwrap().parse::().is_ok() { data.error_codes.push(x.to_owned()); seen_rust_tags = !seen_other_tags || seen_rust_tags; } else { @@ -787,49 +800,44 @@ impl LangString { } } x if extra.is_some() => { + let extra = extra.unwrap(); let s = x.to_lowercase(); - match if s == "compile-fail" || s == "compile_fail" || s == "compilefail" { - Some(( + let unknown_error = |flag, help| { + extra.error_invalid_codeblock_attr( + &format!("unknown attribute `{}`. Did you mean `{}`?", x, flag), + help, + ); + }; + if s == "compile-fail" || s == "compile_fail" || s == "compilefail" { + unknown_error( "compile_fail", "the code block will either not be tested if not marked as a rust one \ or won't fail if it compiles successfully", - )) + ); } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" { - Some(( + unknown_error( "should_panic", "the code block will either not be tested if not marked as a rust one \ or won't fail if it doesn't panic when running", - )) + ); } else if s == "no-run" || s == "no_run" || s == "norun" { - Some(( + unknown_error( "no_run", "the code block will either not be tested if not marked as a rust one \ or will be run (which you might not want)", - )) + ); } else if s == "allow-fail" || s == "allow_fail" || s == "allowfail" { - Some(( + unknown_error( "allow_fail", "the code block will either not be tested if not marked as a rust one \ or will be run (which you might not want)", - )) + ); } else if s == "test-harness" || s == "test_harness" || s == "testharness" { - Some(( + unknown_error( "test_harness", "the code block will either not be tested if not marked as a rust one \ or the code will be wrapped inside a main function", - )) - } else { - None - } { - Some((flag, help)) => { - if let Some(ref extra) = extra { - extra.error_invalid_codeblock_attr( - &format!("unknown attribute `{}`. Did you mean `{}`?", x, flag), - help, - ); - } - } - None => {} + ); } seen_other_tags = true; } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 04c4685213b2e..a94e835c03e71 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -810,11 +810,9 @@ themePicker.onblur = handleThemeButtonsBlur; if line.starts_with(&format!("\"{}\"", krate)) { continue; } - if line.ends_with(",\\") { - ret.push(line[..line.len() - 2].to_string()); - } else { + if let Some(head) = line.strip_suffix(",\\").or_else(|| line.strip_suffix("\\")) { // Ends with "\\" (it's the case for the last added crate line) - ret.push(line[..line.len() - 1].to_string()); + ret.push(head.to_string()); } krates.push( line.split('"') diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index f0900c34a4ba3..abd5b761f144c 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -83,8 +83,7 @@ impl<'a> SourceCollector<'a> { }; // Remove the utf-8 BOM if any - let contents = - if contents.starts_with("\u{feff}") { &contents[3..] } else { &contents[..] }; + let contents = contents.strip_prefix("\u{feff}").unwrap_or(&contents[..]); // Create the intermediate directories let mut cur = self.dst.clone(); diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index e0753bcd70f29..89a194e2f2c40 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -18,9 +18,9 @@ fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) { let mut count = 0; for line in s.lines() { - if line.starts_with("# ") || line.starts_with('%') { + if let Some(tail) = line.strip_prefix("# ").or_else(|| line.strip_prefix('%')) { // trim the whitespace after the symbol - metadata.push(line[1..].trim_start()); + metadata.push(tail.trim_start()); count += line.len() + 1; } else { return (metadata, &s[count..]); diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 8da74f375d9ce..1bce9af880dab 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -487,7 +487,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } let cx = self.cx; - let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new); + let dox = item.collapsed_doc_value().unwrap_or_else(String::new); look_for_tests(&cx, &dox, &item, true); @@ -541,38 +541,31 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let link = ori_link.replace("`", ""); let parts = link.split('#').collect::>(); - let (link, extra_fragment) = if parts.len() > 2 { - build_diagnostic( - cx, - &item, - &link, - &dox, - link_range, - "has an issue with the link anchor.", - "only one `#` is allowed in a link", - None, - ); - continue; - } else if parts.len() == 2 { - if parts[0].trim().is_empty() { - // This is an anchor to an element of the current page, nothing to do in here! + let (link, extra_fragment) = match *parts { + [] => unreachable!("`str::split` always returns a non-empty list"), + [a] => (a.to_owned(), None), + // This is an anchor to an element of the current page, nothing to do in here! + [a, _] if a.trim().is_empty() => continue, + [a, b] => (a.to_owned(), Some(b.to_owned())), + [_, _, _, ..] => { + build_diagnostic( + cx, + &item, + &link, + &dox, + link_range, + "has an issue with the link anchor.", + "only one `#` is allowed in a link", + None, + ); continue; } - (parts[0].to_owned(), Some(parts[1].to_owned())) - } else { - (parts[0].to_owned(), None) }; let resolved_self; let mut path_str; let (res, fragment) = { - let mut kind = None; - path_str = if let Some(prefix) = ["struct@", "enum@", "type@", "trait@", "union@"] - .iter() - .find(|p| link.starts_with(**p)) - { - kind = Some(TypeNS); - link.trim_start_matches(prefix) - } else if let Some(prefix) = [ + static TYPES: &[&str] = &["struct@", "enum@", "type@", "trait@", "union@"]; + static TY_KINDS: &[&str] = &[ "const@", "static@", "value@", @@ -581,28 +574,27 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { "fn@", "module@", "method@", - ] - .iter() - .find(|p| link.starts_with(**p)) - { - kind = Some(ValueNS); - link.trim_start_matches(prefix) - } else if link.ends_with("()") { - kind = Some(ValueNS); - link.trim_end_matches("()") - } else if link.starts_with("macro@") { - kind = Some(MacroNS); - link.trim_start_matches("macro@") - } else if link.starts_with("derive@") { - kind = Some(MacroNS); - link.trim_start_matches("derive@") - } else if link.ends_with('!') { - kind = Some(MacroNS); - link.trim_end_matches('!') - } else { - &link[..] - } - .trim(); + ]; + let (kind, path) = + if let Some(tail) = TYPES.iter().filter_map(|&p| link.strip_prefix(p)).next() { + (Some(TypeNS), tail) + } else if let Some(tail) = + TY_KINDS.iter().filter_map(|&p| link.strip_prefix(p)).next() + { + (Some(ValueNS), tail) + } else if let Some(head) = link.strip_suffix("()") { + (Some(ValueNS), head) + } else if let Some(tail) = + link.strip_prefix("macro@").or_else(|| link.strip_prefix("derive@")) + { + (Some(MacroNS), tail) + } else if let Some(head) = link.strip_suffix('!') { + (Some(MacroNS), head) + } else { + (None, &link[..]) + }; + + path_str = path.trim(); if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) { continue; @@ -623,9 +615,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { if item.is_mod() && item.attrs.inner_docs { None } else { parent_node }; // replace `Self` with suitable item's parent name - if path_str.starts_with("Self::") { + if let Some(item) = path_str.strip_prefix("Self::") { if let Some(ref name) = parent_name { - resolved_self = format!("{}::{}", name, &path_str[6..]); + resolved_self = format!("{}::{}", name, item); path_str = &resolved_self; } } diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index cdbfac267b9a1..248a89d4b7d34 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -523,8 +523,10 @@ impl File { let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize); // Absolute paths start with an NT internal namespace prefix `\??\` // We should not let it leak through. - if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) { - subst = &subst[4..]; + if !relative { + if let [92u16, 63u16, 63u16, 92u16, tail @ ..] = subst { + subst = tail; + } } Ok(PathBuf::from(OsString::from_wide(subst))) } diff --git a/src/tools/clippy/clippy_dev/src/ra_setup.rs b/src/tools/clippy/clippy_dev/src/ra_setup.rs index 8617445c8a600..4032a4ef17e09 100644 --- a/src/tools/clippy/clippy_dev/src/ra_setup.rs +++ b/src/tools/clippy/clippy_dev/src/ra_setup.rs @@ -51,12 +51,11 @@ fn inject_deps_into_manifest( ) -> std::io::Result<()> { let extern_crates = lib_rs .lines() - // get the deps - .filter(|line| line.starts_with("extern crate")) - // we have something like "extern crate foo;", we only care about the "foo" - // ↓ ↓ - // extern crate rustc_middle; - .map(|s| &s[13..(s.len() - 1)]); + // Get the deps + // + // We have something like "extern crate foo;", we only care about the "foo". + // Also we don't trim the string cause we believe rustfmt did that for us. + .filter_map(|line| line.strip_prefix("extern crate ").map(|s| s.strip_suffix(';'))); let new_deps = extern_crates.map(|dep| { // format the dependencies that are going to be put inside the Cargo.toml diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index d52bb8961fae7..dfef15c6ab9a6 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -257,54 +257,57 @@ fn lint_for_missing_headers<'tcx>( /// the spans but this function is inspired from the later. #[allow(clippy::cast_possible_truncation)] #[must_use] -pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) { +fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) { // one-line comments lose their prefix - const ONELINERS: &[&str] = &["///!", "///", "//!", "//"]; + static ONELINERS: &[&str] = &["///!", "///", "//!", "//"]; for prefix in ONELINERS { - if comment.starts_with(*prefix) { - let doc = &comment[prefix.len()..]; + if let Some(doc) = comment.strip_prefix(*prefix) { let mut doc = doc.to_owned(); doc.push('\n'); + let len = doc.len(); return ( - doc.to_owned(), - vec![(doc.len(), span.with_lo(span.lo() + BytePos(prefix.len() as u32)))], + doc, + vec![(len, span.with_lo(span.lo() + BytePos(prefix.len() as u32)))], ); } } - if comment.starts_with("/*") { - let doc = &comment[3..comment.len() - 2]; - let mut sizes = vec![]; - let mut contains_initial_stars = false; - for line in doc.lines() { - let offset = line.as_ptr() as usize - comment.as_ptr() as usize; - debug_assert_eq!(offset as u32 as usize, offset); - contains_initial_stars |= line.trim_start().starts_with('*'); - // +1 for the newline - sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(offset as u32)))); - } - if !contains_initial_stars { - return (doc.to_string(), sizes); - } - // remove the initial '*'s if any - let mut no_stars = String::with_capacity(doc.len()); - for line in doc.lines() { - let mut chars = line.chars(); - while let Some(c) = chars.next() { - if c.is_whitespace() { - no_stars.push(c); - } else { - no_stars.push(if c == '*' { ' ' } else { c }); - break; - } + let doc = comment.strip_prefix("/**") + .or_else(|| comment.strip_prefix("/*!")) + .and_then(|s| s.strip_suffix("*/")); + let doc = match doc { + Some(doc) => doc, + _ => panic!("not a doc-comment: {}", comment), + }; + + let mut sizes = vec![]; + let mut contains_initial_stars = false; + for line in doc.lines() { + let offset = line.as_ptr() as usize - comment.as_ptr() as usize; + debug_assert_eq!(offset as u32 as usize, offset); + contains_initial_stars |= line.trim_start().starts_with('*'); + // +1 for the newline + sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(offset as u32)))); + } + if !contains_initial_stars { + return (doc.to_owned(), sizes); + } + // remove the initial '*'s if any + let mut no_stars = String::with_capacity(doc.len()); + for line in doc.lines() { + let mut chars = line.chars(); + while let Some(c) = chars.next() { + if c.is_whitespace() { + no_stars.push(c); + } else { + no_stars.push(if c == '*' { ' ' } else { c }); + break; } - no_stars.push_str(chars.as_str()); - no_stars.push('\n'); } - return (no_stars, sizes); + no_stars.push_str(chars.as_str()); + no_stars.push('\n'); } - - panic!("not a doc-comment: {}", comment); + (no_stars, sizes) } #[derive(Copy, Clone)] @@ -319,7 +322,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs for attr in attrs { if let AttrKind::DocComment(ref comment) = attr.kind { - let comment = comment.to_string(); + let comment: &str = &*comment.as_str(); let (comment, current_spans) = strip_doc_comment_decoration(&comment, attr.span); spans.extend_from_slice(¤t_spans); doc.push_str(&comment); @@ -480,7 +483,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { return false; } - let s = if s.ends_with('s') { &s[..s.len() - 1] } else { s }; + let s = s.strip_suffix('s').unwrap_or(s); s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs index d821b5134841e..19e6563cd5028 100644 --- a/src/tools/clippy/clippy_lints/src/loops.rs +++ b/src/tools/clippy/clippy_lints/src/loops.rs @@ -2415,8 +2415,8 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { span, NEEDLESS_COLLECT_MSG, |diag| { - let (arg, pred) = if contains_arg.starts_with('&') { - ("x", &contains_arg[1..]) + let (arg, pred) = if let Some(tail) = contains_arg.strip_prefix('&') { + ("x", tail) } else { ("&x", &*contains_arg) }; diff --git a/src/tools/clippy/clippy_lints/src/misc_early.rs b/src/tools/clippy/clippy_lints/src/misc_early.rs index ad39e59d0678a..4c0ff8a35d2ba 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early.rs @@ -428,8 +428,8 @@ impl EarlyLintPass for MiscEarlyLints { if let PatKind::Ident(_, ident, None) = arg.pat.kind { let arg_name = ident.to_string(); - if arg_name.starts_with('_') { - if let Some(correspondence) = registered_names.get(&arg_name[1..]) { + if let Some(arg) = arg_name.strip_prefix('_') { + if let Some(correspondence) = registered_names.get(arg) { span_lint( cx, DUPLICATE_UNDERSCORE_ARGUMENT, @@ -437,7 +437,7 @@ impl EarlyLintPass for MiscEarlyLints { &format!( "`{}` already exists, having another argument having almost the same \ name makes code comprehension and documentation more difficult", - arg_name[1..].to_owned() + arg ), ); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index fda7480194dce..7ff3078b12024 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -234,11 +234,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { ); let mut app = Applicability::MaybeIncorrect; - let mut call_snip = &snip[dot + 1..]; + let call_snip = &snip[dot + 1..]; // Machine applicable when `call_snip` looks like `foobar()` - if call_snip.ends_with("()") { - call_snip = call_snip[..call_snip.len()-2].trim(); - if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { + if let Some(name) = call_snip.strip_suffix("()").map(str::trim) { + if name.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { app = Applicability::MachineApplicable; } } diff --git a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs index 99413153d49bb..f087828b9816b 100644 --- a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs +++ b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs @@ -79,9 +79,11 @@ impl<'a> NumericLiteral<'a> { (Some(p), s) }; - if suffix.is_some() && sans_prefix.ends_with('_') { + if suffix.is_some() { // The '_' before the suffix isn't part of the digits - sans_prefix = &sans_prefix[..sans_prefix.len() - 1]; + if let Some(head) = sans_prefix.strip_suffix('_') { + sans_prefix = head; + } } let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float); diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 571e7a59113ad..5741df4356b48 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -666,7 +666,10 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, rdr: R, it: &mut dyn return; } - let comment = if testfile.to_string_lossy().ends_with(".rs") { "//" } else { "#" }; + let comment = match testfile.extension() { + Some(ext) if "rs" == ext => "//", + _ => "#", + }; let mut rdr = BufReader::new(rdr); let mut ln = String::new(); @@ -683,23 +686,24 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, rdr: R, it: &mut dyn let ln = ln.trim(); if ln.starts_with("fn") || ln.starts_with("mod") { return; - } else if ln.starts_with(comment) && ln[comment.len()..].trim_start().starts_with('[') { - // A comment like `//[foo]` is specific to revision `foo` - if let Some(close_brace) = ln.find(']') { - let open_brace = ln.find('[').unwrap(); - let lncfg = &ln[open_brace + 1..close_brace]; - let matches = match cfg { - Some(s) => s == &lncfg[..], - None => false, - }; - if matches { - it(ln[(close_brace + 1)..].trim_start()); + } else if let Some(tail) = ln.strip_prefix(comment).map(str::trim_start) { + if let Some(open) = tail.strip_prefix('[') { + // A comment like `//[foo]` is specific to revision `foo` + let mut v = open.split(']'); + match (v.next(), v.next()) { + (Some(lncfg), Some(after)) => { + if cfg.map(|s| s == lncfg).unwrap_or(false) { + it(after.trim_start()); + } + } + _ => panic!( + "malformed condition directive: expected `{}[foo]`, found `{}`", + comment, ln + ), } } else { - panic!("malformed condition directive: expected `{}[foo]`, found `{}`", comment, ln) + it(tail); } - } else if ln.starts_with(comment) { - it(ln[comment.len()..].trim_start()); } } return; @@ -893,27 +897,32 @@ impl Config { // returns whether this line contains this prefix or not. For prefix // "ignore", returns true if line says "ignore-x86_64", "ignore-arch", // "ignore-android" etc. - line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') + if let Some(tail) = line.strip_prefix(prefix) { + if let [b'-', ..] = tail.as_bytes() { + return true; + } + } + false } fn parse_name_directive(&self, line: &str, directive: &str) -> bool { // Ensure the directive is a whole word. Do not match "ignore-x86" when // the line says "ignore-x86_64". - line.starts_with(directive) - && match line.as_bytes().get(directive.len()) { - None | Some(&b' ') | Some(&b':') => true, - _ => false, + if let Some(tail) = line.strip_prefix(directive) { + if let [] | [b' ', ..] | [b':', ..] = tail.as_bytes() { + return true; } + } + false } pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option { - let colon = directive.len(); - if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { - let value = line[(colon + 1)..].to_owned(); - debug!("{}: {}", directive, value); - Some(expand_variables(value, self)) - } else { - None + match line.strip_prefix(directive).and_then(|s| s.strip_prefix(':')) { + Some(value) => { + debug!("{}: {}", directive, value); + Some(expand_variables(value.to_owned(), self)) + } + _ => None, } } diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 2aea4d22700f3..12189865d4bcc 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -966,11 +966,8 @@ fn extract_lldb_version(full_version_line: Option) -> (Option, b } } - if full_version_line.starts_with("lldb version ") { - let vers = full_version_line[13..] - .chars() - .take_while(|c| c.is_digit(10)) - .collect::(); + if let Some(ver) = full_version_line.strip_prefix("lldb version ") { + let vers = ver.chars().take_while(|c| c.is_digit(10)).collect::(); if !vers.is_empty() { return (Some(vers + "00"), full_version_line.contains("rust-enabled")); } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index dd0c68ecd4965..3ddcee85c3434 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1192,8 +1192,7 @@ impl<'test> TestCx<'test> { for line in reader.lines() { match line { Ok(line) => { - let line = - if line.starts_with("//") { line[2..].trim_start() } else { line.as_str() }; + let line = line.strip_prefix("//").unwrap_or(&*line).trim_start(); if line.contains("#break") { breakpoint_lines.push(counter); @@ -2528,7 +2527,7 @@ impl<'test> TestCx<'test> { // [MONO_ITEM] name [@@ (cgu)+] fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem { - let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() }; + let s = s.strip_prefix(PREFIX).unwrap_or(s).trim(); let full_string = format!("{}{}", PREFIX, s); @@ -3130,12 +3129,10 @@ impl<'test> TestCx<'test> { let _ = std::fs::remove_dir_all(&test_dir); } for l in test_file_contents.lines() { - if l.starts_with("// EMIT_MIR ") { - let test_name = l.trim_start_matches("// EMIT_MIR "); + if let Some(test_name) = l.strip_prefix("// EMIT_MIR ") { let expected_file = test_dir.join(test_name); - let dumped_string = if test_name.ends_with(".diff") { - let test_name = test_name.trim_end_matches(".diff"); + let dumped_string = if let Some(test_name) = test_name.strip_suffix(".diff") { let before = format!("{}.before.mir", test_name); let after = format!("{}.after.mir", test_name); let before = self.get_mir_dump_dir().join(before); diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 3fa637b5a696f..885496ca61be5 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -209,10 +209,10 @@ fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> { fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool { let prefix = "feature_gate_"; - if filen_underscore.starts_with(prefix) { + if let Some(gate) = filen_underscore.strip_prefix(prefix) { for (n, f) in features.iter_mut() { // Equivalent to filen_underscore == format!("feature_gate_{}", n) - if &filen_underscore[prefix.len()..] == n { + if gate == n { f.has_gate_test = true; return true; }