diff --git a/Cargo.lock b/Cargo.lock index 99cb71cd0ac87..57157b32b9782 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -582,13 +582,11 @@ dependencies = [ name = "clippy_dev" version = "0.0.1" dependencies = [ - "aho-corasick", "chrono", "clap", "indoc", "itertools", "opener", - "shell-escape", "walkdir", ] @@ -4888,12 +4886,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shell-escape" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" - [[package]] name = "shlex" version = "1.3.0" diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 28147dfbea3e0..3a98217f625a0 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6440,6 +6440,7 @@ Released 2018-09-13 [`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute +[`useless_concat`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_concat [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 45ba2f078be78..72ab08792d37e 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -51,7 +51,7 @@ Clippy team directly by mentioning them in the issue or over on [Zulip]. All currently active team members can be found [here](https://github.com/rust-lang/rust-clippy/blob/master/triagebot.toml#L18) -Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy +Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues. You can use `@rustbot claim` to assign the issue to yourself. There are also some abandoned PRs, marked with [`S-inactive-closed`]. @@ -70,7 +70,7 @@ To figure out how this syntax structure is encoded in the AST, it is recommended Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. But we can make it nest-less by using [let chains], [like this][nest-less]. -[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`] +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`] first. Sometimes they are only somewhat involved code wise, but not difficult per-se. Note that [`E-medium`] issues may require some knowledge of Clippy internals or some debugging to find the actual problem behind the issue. @@ -79,7 +79,7 @@ debugging to find the actual problem behind the issue. lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of an AST expression). -[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue +[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed [`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST [`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index c7c06afb6129e..f69e5bee4bb2b 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy" -# begin autogenerated version version = "0.1.89" -# end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md index cdbbe76bdb08e..fc405249bcfe8 100644 --- a/src/tools/clippy/book/src/development/basics.md +++ b/src/tools/clippy/book/src/development/basics.md @@ -151,7 +151,7 @@ toolchain called `clippy` by default, see `cargo dev setup toolchain --help` for other options. ```terminal -cargo dev setup toolcahin +cargo dev setup toolchain ``` Now you may run `cargo +clippy clippy` in any project using the new toolchain. diff --git a/src/tools/clippy/book/src/development/the_team.md b/src/tools/clippy/book/src/development/the_team.md index da5d084ed97f3..d22123231868e 100644 --- a/src/tools/clippy/book/src/development/the_team.md +++ b/src/tools/clippy/book/src/development/the_team.md @@ -33,7 +33,7 @@ this group to help with triaging, which can include: 1. **Labeling issues** - For the `good-first-issue` label, it can still be good to use `@rustbot` to + For the `good first issue` label, it can still be good to use `@rustbot` to subscribe to the issue and help interested parties, if they post questions in the comments. diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 0db4182dbcdbd..9809e32de8a44 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -836,6 +836,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n) * [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) * [`manual_slice_fill`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_fill) +* [`manual_slice_size_calculation`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation) * [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) * [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) * [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 1134b0e97af22..0606245f990c1 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_config" -# begin autogenerated version version = "0.1.89" -# end autogenerated version edition = "2024" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index ad0aea39d41a2..4ce8d001c2f00 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -739,6 +739,7 @@ define_Conf! { manual_repeat_n, manual_retain, manual_slice_fill, + manual_slice_size_calculation, manual_split_once, manual_str_repeat, manual_strip, diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 47b7b3758613c..10c08dba50b90 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -5,13 +5,11 @@ version = "0.0.1" edition = "2024" [dependencies] -aho-corasick = "1.0" chrono = { version = "0.4.38", default-features = false, features = ["clock"] } clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" opener = "0.7" -shell-escape = "0.1" walkdir = "2.3" [package.metadata.rust-analyzer] diff --git a/src/tools/clippy/clippy_dev/src/deprecate_lint.rs b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs index bf0e777104698..3bdc5b2772327 100644 --- a/src/tools/clippy/clippy_dev/src/deprecate_lint.rs +++ b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs @@ -1,6 +1,4 @@ -use crate::update_lints::{ - DeprecatedLint, DeprecatedLints, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints, -}; +use crate::update_lints::{DeprecatedLint, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints}; use crate::utils::{UpdateMode, Version}; use std::ffi::OsStr; use std::path::{Path, PathBuf}; @@ -16,28 +14,34 @@ use std::{fs, io}; /// /// If a file path could not read from or written to pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { - let prefixed_name = if name.starts_with("clippy::") { - name.to_owned() - } else { - format!("clippy::{name}") - }; - let stripped_name = &prefixed_name[8..]; + if let Some((prefix, _)) = name.split_once("::") { + panic!("`{name}` should not contain the `{prefix}` prefix"); + } let mut lints = find_lint_decls(); - let DeprecatedLints { - renamed: renamed_lints, - deprecated: mut deprecated_lints, - file: mut deprecated_file, - contents: mut deprecated_contents, - deprecated_end, - .. - } = read_deprecated_lints(); - - let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { + let (mut deprecated_lints, renamed_lints) = read_deprecated_lints(); + + let Some(lint) = lints.iter().find(|l| l.name == name) else { eprintln!("error: failed to find lint `{name}`"); return; }; + let prefixed_name = String::from_iter(["clippy::", name]); + match deprecated_lints.binary_search_by(|x| x.name.cmp(&prefixed_name)) { + Ok(_) => { + println!("`{name}` is already deprecated"); + return; + }, + Err(idx) => deprecated_lints.insert( + idx, + DeprecatedLint { + name: prefixed_name, + reason: reason.into(), + version: clippy_version.rust_display().to_string(), + }, + ), + } + let mod_path = { let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); if mod_path.is_dir() { @@ -48,24 +52,7 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { mod_path }; - if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { - deprecated_contents.insert_str( - deprecated_end as usize, - &format!( - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - clippy_version.rust_display(), - prefixed_name, - reason, - ), - ); - deprecated_file.replace_contents(deprecated_contents.as_bytes()); - drop(deprecated_file); - - deprecated_lints.push(DeprecatedLint { - name: prefixed_name, - reason: reason.into(), - }); - + if remove_lint_declaration(name, &mod_path, &mut lints).unwrap_or(false) { generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); println!("info: `{name}` has successfully been deprecated"); println!("note: you must run `cargo uitest` to update the test results"); diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index b4c13213f552a..13d6b1285dcdb 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,19 +1,18 @@ +use crate::utils::{ + ClippyInfo, ErrAction, FileUpdater, UpdateMode, UpdateStatus, panic_action, run_with_args_split, run_with_output, +}; use itertools::Itertools; use rustc_lexer::{TokenKind, tokenize}; -use shell_escape::escape; -use std::ffi::{OsStr, OsString}; +use std::fmt::Write; +use std::fs; +use std::io::{self, Read}; use std::ops::ControlFlow; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::{self, Command, Stdio}; -use std::{fs, io}; use walkdir::WalkDir; pub enum Error { - CommandFailed(String, String), Io(io::Error), - RustfmtNotInstalled, - WalkDir(walkdir::Error), - IntellijSetupActive, Parse(PathBuf, usize, String), CheckFailed, } @@ -24,37 +23,15 @@ impl From for Error { } } -impl From for Error { - fn from(error: walkdir::Error) -> Self { - Self::WalkDir(error) - } -} - impl Error { fn display(&self) { match self { Self::CheckFailed => { eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); }, - Self::CommandFailed(command, stderr) => { - eprintln!("error: command `{command}` failed!\nstderr: {stderr}"); - }, Self::Io(err) => { eprintln!("error: {err}"); }, - Self::RustfmtNotInstalled => { - eprintln!("error: rustfmt nightly is not installed."); - }, - Self::WalkDir(err) => { - eprintln!("error: {err}"); - }, - Self::IntellijSetupActive => { - eprintln!( - "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ - Not formatting because that would format the local repo as well!\n\ - Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." - ); - }, Self::Parse(path, line, msg) => { eprintln!("error parsing `{}:{line}`: {msg}", path.display()); }, @@ -62,12 +39,6 @@ impl Error { } } -struct FmtContext { - check: bool, - verbose: bool, - rustfmt_path: String, -} - struct ClippyConf<'a> { name: &'a str, attrs: &'a str, @@ -257,155 +228,153 @@ fn fmt_conf(check: bool) -> Result<(), Error> { Ok(()) } -fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { - // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to - // format because rustfmt would also format the entire rustc repo as it is a local - // dependency - if fs::read_to_string("Cargo.toml") - .expect("Failed to read clippy Cargo.toml") - .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") - { - return Err(Error::IntellijSetupActive); - } - - check_for_rustfmt(context)?; - - cargo_fmt(context, ".".as_ref())?; - cargo_fmt(context, "clippy_dev".as_ref())?; - cargo_fmt(context, "rustc_tools_util".as_ref())?; - cargo_fmt(context, "lintcheck".as_ref())?; - - let chunks = WalkDir::new("tests") - .into_iter() - .filter_map(|entry| { - let entry = entry.expect("failed to find tests"); - let path = entry.path(); - if path.extension() != Some("rs".as_ref()) - || path - .components() - .nth_back(1) - .is_some_and(|c| c.as_os_str() == "syntax-error-recovery") - || entry.file_name() == "ice-3891.rs" - { - None +/// Format the symbols list +fn fmt_syms(update_mode: UpdateMode) { + FileUpdater::default().update_file_checked( + "cargo dev fmt", + update_mode, + "clippy_utils/src/sym.rs", + &mut |_, text: &str, new_text: &mut String| { + let (pre, conf) = text.split_once("generate! {\n").expect("can't find generate! call"); + let (conf, post) = conf.split_once("\n}\n").expect("can't find end of generate! call"); + let mut lines = conf + .lines() + .map(|line| { + let line = line.trim(); + line.strip_suffix(',').unwrap_or(line).trim_end() + }) + .collect::>(); + lines.sort_unstable(); + write!( + new_text, + "{pre}generate! {{\n {},\n}}\n{post}", + lines.join(",\n "), + ) + .unwrap(); + if text == new_text { + UpdateStatus::Unchanged } else { - Some(entry.into_path().into_os_string()) + UpdateStatus::Changed } - }) - .chunks(250); - - for chunk in &chunks { - rustfmt(context, chunk)?; - } - Ok(()) + }, + ); } -// the "main" function of cargo dev fmt -pub fn run(check: bool, verbose: bool) { - let output = Command::new("rustup") - .args(["which", "rustfmt"]) - .stderr(Stdio::inherit()) - .output() - .expect("error running `rustup which rustfmt`"); - if !output.status.success() { - eprintln!("`rustup which rustfmt` did not execute successfully"); - process::exit(1); - } - let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path"); +fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) { + let mut rustfmt_path = String::from_utf8(run_with_output( + "rustup which rustfmt", + Command::new("rustup").args(["which", "rustfmt"]), + )) + .expect("invalid rustfmt path"); rustfmt_path.truncate(rustfmt_path.trim_end().len()); - let context = FmtContext { - check, - verbose, - rustfmt_path, - }; - if let Err(e) = run_rustfmt(&context).and_then(|()| fmt_conf(check)) { - e.display(); - process::exit(1); - } -} - -fn format_command(program: impl AsRef, dir: impl AsRef, args: &[impl AsRef]) -> String { - let arg_display: Vec<_> = args.iter().map(|a| escape(a.as_ref().to_string_lossy())).collect(); - - format!( - "cd {} && {} {}", - escape(dir.as_ref().to_string_lossy()), - escape(program.as_ref().to_string_lossy()), - arg_display.join(" ") - ) -} - -fn exec_fmt_command( - context: &FmtContext, - program: impl AsRef, - dir: impl AsRef, - args: &[impl AsRef], -) -> Result<(), Error> { - if context.verbose { - println!("{}", format_command(&program, &dir, args)); + let mut cargo_path = String::from_utf8(run_with_output( + "rustup which cargo", + Command::new("rustup").args(["which", "cargo"]), + )) + .expect("invalid cargo path"); + cargo_path.truncate(cargo_path.trim_end().len()); + + // Start all format jobs first before waiting on the results. + let mut children = Vec::with_capacity(16); + for &path in &[ + ".", + "clippy_config", + "clippy_dev", + "clippy_lints", + "clippy_lints_internal", + "clippy_utils", + "rustc_tools_util", + "lintcheck", + ] { + let mut cmd = Command::new(&cargo_path); + cmd.current_dir(clippy.path.join(path)) + .args(["fmt"]) + .env("RUSTFMT", &rustfmt_path) + .stdout(Stdio::null()) + .stdin(Stdio::null()) + .stderr(Stdio::piped()); + if update_mode.is_check() { + cmd.arg("--check"); + } + match cmd.spawn() { + Ok(x) => children.push(("cargo fmt", x)), + Err(ref e) => panic_action(&e, ErrAction::Run, "cargo fmt".as_ref()), + } } - let output = Command::new(&program) - .env("RUSTFMT", &context.rustfmt_path) - .current_dir(&dir) - .args(args.iter()) - .output() - .unwrap(); - let success = output.status.success(); - - match (context.check, success) { - (_, true) => Ok(()), - (true, false) => Err(Error::CheckFailed), - (false, false) => { - let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); - Err(Error::CommandFailed( - format_command(&program, &dir, args), - String::from(stderr), - )) + run_with_args_split( + || { + let mut cmd = Command::new(&rustfmt_path); + if update_mode.is_check() { + cmd.arg("--check"); + } + cmd.stdout(Stdio::null()) + .stdin(Stdio::null()) + .stderr(Stdio::piped()) + .args(["--config", "show_parse_errors=false"]); + cmd }, - } -} + |cmd| match cmd.spawn() { + Ok(x) => children.push(("rustfmt", x)), + Err(ref e) => panic_action(&e, ErrAction::Run, "rustfmt".as_ref()), + }, + WalkDir::new("tests") + .into_iter() + .filter_entry(|p| p.path().file_name().is_none_or(|x| x != "skip_rustfmt")) + .filter_map(|e| { + let e = e.expect("error reading `tests`"); + e.path() + .as_os_str() + .as_encoded_bytes() + .ends_with(b".rs") + .then(|| e.into_path().into_os_string()) + }), + ); -fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<(), Error> { - let mut args = vec!["fmt", "--all"]; - if context.check { - args.push("--check"); + for (name, child) in &mut children { + match child.wait() { + Ok(status) => match (update_mode, status.exit_ok()) { + (UpdateMode::Check | UpdateMode::Change, Ok(())) => {}, + (UpdateMode::Check, Err(_)) => { + let mut s = String::new(); + if let Some(mut stderr) = child.stderr.take() + && stderr.read_to_string(&mut s).is_ok() + { + eprintln!("{s}"); + } + eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); + process::exit(1); + }, + (UpdateMode::Change, Err(e)) => { + let mut s = String::new(); + if let Some(mut stderr) = child.stderr.take() + && stderr.read_to_string(&mut s).is_ok() + { + eprintln!("{s}"); + } + panic_action(&e, ErrAction::Run, name.as_ref()); + }, + }, + Err(ref e) => panic_action(e, ErrAction::Run, name.as_ref()), + } } - exec_fmt_command(context, "cargo", path, &args) } -fn check_for_rustfmt(context: &FmtContext) -> Result<(), Error> { - let program = "rustfmt"; - let dir = std::env::current_dir()?; - let args = &["--version"]; - - if context.verbose { - println!("{}", format_command(program, &dir, args)); - } - - let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?; - - if output.status.success() { - Ok(()) - } else if std::str::from_utf8(&output.stderr) - .unwrap_or("") - .starts_with("error: 'rustfmt' is not installed") - { - Err(Error::RustfmtNotInstalled) - } else { - Err(Error::CommandFailed( - format_command(program, &dir, args), - std::str::from_utf8(&output.stderr).unwrap_or("").to_string(), - )) +// the "main" function of cargo dev fmt +pub fn run(clippy: &ClippyInfo, update_mode: UpdateMode) { + if clippy.has_intellij_hook { + eprintln!( + "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ + Not formatting because that would format the local repo as well!\n\ + Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." + ); + return; } -} - -fn rustfmt(context: &FmtContext, paths: impl Iterator) -> Result<(), Error> { - let mut args = Vec::new(); - if context.check { - args.push(OsString::from("--check")); + run_rustfmt(clippy, update_mode); + fmt_syms(update_mode); + if let Err(e) = fmt_conf(update_mode.is_check()) { + e.display(); + process::exit(1); } - args.extend(paths); - exec_fmt_command(context, &context.rustfmt_path, std::env::current_dir()?, &args) } diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index e237a05b2530e..3361443196ab5 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,4 +1,12 @@ -#![feature(rustc_private, if_let_guard, let_chains)] +#![feature( + rustc_private, + exit_status_error, + if_let_guard, + let_chains, + os_str_slice, + os_string_truncate, + slice_split_once +)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 5dce0be742b26..ebcd8611d78cd 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -26,7 +26,7 @@ fn main() { allow_staged, allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), - DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), + DevCommand::Fmt { check } => fmt::run(&clippy, utils::UpdateMode::from_check(check)), DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)), DevCommand::NewLint { pass, @@ -125,9 +125,6 @@ enum DevCommand { #[arg(long)] /// Use the rustfmt --check option check: bool, - #[arg(short, long)] - /// Echo commands run - verbose: bool, }, #[command(name = "update_lints")] /// Updates lint registration and information from the source code diff --git a/src/tools/clippy/clippy_dev/src/release.rs b/src/tools/clippy/clippy_dev/src/release.rs index d3b1a7ff3201f..62c1bee81850e 100644 --- a/src/tools/clippy/clippy_dev/src/release.rs +++ b/src/tools/clippy/clippy_dev/src/release.rs @@ -1,4 +1,4 @@ -use crate::utils::{FileUpdater, Version, update_text_region_fn}; +use crate::utils::{FileUpdater, UpdateStatus, Version, parse_cargo_package}; use std::fmt::Write; static CARGO_TOML_FILES: &[&str] = &[ @@ -13,15 +13,17 @@ pub fn bump_version(mut version: Version) { let mut updater = FileUpdater::default(); for file in CARGO_TOML_FILES { - updater.update_file( - file, - &mut update_text_region_fn( - "# begin autogenerated version\n", - "# end autogenerated version", - |dst| { - writeln!(dst, "version = \"{}\"", version.toml_display()).unwrap(); - }, - ), - ); + updater.update_file(file, &mut |_, src, dst| { + let package = parse_cargo_package(src); + if package.version_range.is_empty() { + dst.push_str(src); + UpdateStatus::Unchanged + } else { + dst.push_str(&src[..package.version_range.start]); + write!(dst, "\"{}\"", version.toml_display()).unwrap(); + dst.push_str(&src[package.version_range.end..]); + UpdateStatus::from_changed(src.get(package.version_range.clone()) != dst.get(package.version_range)) + } + }); } } diff --git a/src/tools/clippy/clippy_dev/src/rename_lint.rs b/src/tools/clippy/clippy_dev/src/rename_lint.rs index 9e7e5d97f0219..be8b27c7a9e9f 100644 --- a/src/tools/clippy/clippy_dev/src/rename_lint.rs +++ b/src/tools/clippy/clippy_dev/src/rename_lint.rs @@ -1,9 +1,11 @@ -use crate::update_lints::{ - DeprecatedLints, RenamedLint, find_lint_decls, gen_renamed_lints_test_fn, generate_lint_files, - read_deprecated_lints, +use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::utils::{ + FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, + try_rename_dir, try_rename_file, }; -use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, try_rename_file}; -use std::ffi::OsStr; +use rustc_lexer::TokenKind; +use std::ffi::OsString; +use std::fs; use std::path::Path; use walkdir::WalkDir; @@ -22,7 +24,7 @@ use walkdir::WalkDir; /// * If either lint name has a prefix /// * If `old_name` doesn't name an existing lint. /// * If `old_name` names a deprecated or renamed lint. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { if let Some((prefix, _)) = old_name.split_once("::") { panic!("`{old_name}` should not contain the `{prefix}` prefix"); @@ -33,162 +35,369 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b let mut updater = FileUpdater::default(); let mut lints = find_lint_decls(); - let DeprecatedLints { - renamed: mut renamed_lints, - deprecated: deprecated_lints, - file: mut deprecated_file, - contents: mut deprecated_contents, - renamed_end, - .. - } = read_deprecated_lints(); - - let mut old_lint_index = None; - let mut found_new_name = false; - for (i, lint) in lints.iter().enumerate() { - if lint.name == old_name { - old_lint_index = Some(i); - } else if lint.name == new_name { - found_new_name = true; + let (deprecated_lints, mut renamed_lints) = read_deprecated_lints(); + + let Ok(lint_idx) = lints.binary_search_by(|x| x.name.as_str().cmp(old_name)) else { + panic!("could not find lint `{old_name}`"); + }; + let lint = &lints[lint_idx]; + + let old_name_prefixed = String::from_iter(["clippy::", old_name]); + let new_name_prefixed = if uplift { + new_name.to_owned() + } else { + String::from_iter(["clippy::", new_name]) + }; + + for lint in &mut renamed_lints { + if lint.new_name == old_name_prefixed { + lint.new_name.clone_from(&new_name_prefixed); } } - let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); + match renamed_lints.binary_search_by(|x| x.old_name.cmp(&old_name_prefixed)) { + Ok(_) => { + println!("`{old_name}` already has a rename registered"); + return; + }, + Err(idx) => { + renamed_lints.insert( + idx, + RenamedLint { + old_name: old_name_prefixed, + new_name: if uplift { + new_name.to_owned() + } else { + String::from_iter(["clippy::", new_name]) + }, + version: clippy_version.rust_display().to_string(), + }, + ); + }, + } - let lint = RenamedLint { - old_name: format!("clippy::{old_name}"), - new_name: if uplift { - new_name.into() + // Some tests are named `lint_name_suffix` which should also be renamed, + // but we can't do that if the renamed lint's name overlaps with another + // lint. e.g. renaming 'foo' to 'bar' when a lint 'foo_bar' also exists. + let change_prefixed_tests = lints.get(lint_idx + 1).is_none_or(|l| !l.name.starts_with(old_name)); + + let mut mod_edit = ModEdit::None; + if uplift { + let is_unique_mod = lints[..lint_idx].iter().any(|l| l.module == lint.module) + || lints[lint_idx + 1..].iter().any(|l| l.module == lint.module); + if is_unique_mod { + if delete_file_if_exists(lint.path.as_ref()) { + mod_edit = ModEdit::Delete; + } } else { - format!("clippy::{new_name}") - }, - }; + updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { + let mut start = &src[..lint.declaration_range.start]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; + } + let mut end = &src[lint.declaration_range.end..]; + if end.starts_with("\n\n") { + end = &end[1..]; + } + dst.push_str(start); + dst.push_str(end); + UpdateStatus::Changed + }); + } + delete_test_files(old_name, change_prefixed_tests); + lints.remove(lint_idx); + } else if lints.binary_search_by(|x| x.name.as_str().cmp(new_name)).is_err() { + let lint = &mut lints[lint_idx]; + if lint.module.ends_with(old_name) + && lint + .path + .file_stem() + .is_some_and(|x| x.as_encoded_bytes() == old_name.as_bytes()) + { + let mut new_path = lint.path.with_file_name(new_name).into_os_string(); + new_path.push(".rs"); + if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { + mod_edit = ModEdit::Rename; + } - // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in - // case. - assert!( - !renamed_lints.iter().any(|l| lint.old_name == l.old_name), - "`{old_name}` has already been renamed" - ); - assert!( - !deprecated_lints.iter().any(|l| lint.old_name == l.name), - "`{old_name}` has already been deprecated" - ); - - // Update all lint level attributes. (`clippy::lint_name`) - let replacements = &[(&*lint.old_name, &*lint.new_name)]; - let replacer = StringReplacer::new(replacements); - for file in WalkDir::new(".").into_iter().map(Result::unwrap).filter(|f| { - let name = f.path().file_name(); - let ext = f.path().extension(); - (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) - && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("deprecated_lints.rs")) - }) { - updater.update_file(file.path(), &mut replacer.replace_ident_fn()); + let mod_len = lint.module.len(); + lint.module.truncate(mod_len - old_name.len()); + lint.module.push_str(new_name); + } + rename_test_files(old_name, new_name, change_prefixed_tests); + new_name.clone_into(&mut lints[lint_idx].name); + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + } else { + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Since `{new_name}` already exists the existing code has not been changed"); + return; } - deprecated_contents.insert_str( - renamed_end as usize, - &format!( - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - clippy_version.rust_display(), - lint.old_name, - lint.new_name, - ), - ); - deprecated_file.replace_contents(deprecated_contents.as_bytes()); - drop(deprecated_file); - - renamed_lints.push(lint); - renamed_lints.sort_by(|lhs, rhs| { - lhs.new_name - .starts_with("clippy::") - .cmp(&rhs.new_name.starts_with("clippy::")) - .reverse() - .then_with(|| lhs.old_name.cmp(&rhs.old_name)) - }); + let mut update_fn = file_update_fn(old_name, new_name, mod_edit); + for file in WalkDir::new(".").into_iter().filter_entry(|e| { + // Skip traversing some of the larger directories. + e.path() + .as_os_str() + .as_encoded_bytes() + .get(2..) + .is_none_or(|x| x != "target".as_bytes() && x != ".git".as_bytes()) + }) { + let file = file.expect("error reading clippy directory"); + if file.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { + updater.update_file(file.path(), &mut update_fn); + } + } + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); if uplift { - updater.update_file("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(&renamed_lints)); - println!( - "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." - ); - } else if found_new_name { - updater.update_file("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(&renamed_lints)); - println!( - "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." - ); - } else { - // Rename the lint struct and source files sharing a name with the lint. - let lint = &mut lints[old_lint_index]; - let old_name_upper = old_name.to_uppercase(); - let new_name_upper = new_name.to_uppercase(); - lint.name = new_name.into(); - - // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. - if try_rename_file( - Path::new(&format!("tests/ui/{old_name}.rs")), - Path::new(&format!("tests/ui/{new_name}.rs")), - ) { - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.stderr")), - Path::new(&format!("tests/ui/{new_name}.stderr")), - ); - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.fixed")), - Path::new(&format!("tests/ui/{new_name}.fixed")), - ); + println!("Uplifted `clippy::{old_name}` as `{new_name}`"); + if matches!(mod_edit, ModEdit::None) { + println!("Only the rename has been registered, the code will need to be edited manually"); + } else { + println!("All the lint's code has been deleted"); + println!("Make sure to inspect the results as some things may have been missed"); } + } else { + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("All code referencing the old name has been updated"); + println!("Make sure to inspect the results as some things may have been missed"); + } + println!("note: `cargo uibless` still needs to be run to update the test results"); +} + +#[derive(Clone, Copy)] +enum ModEdit { + None, + Delete, + Rename, +} - // Try to rename the file containing the lint if the file name matches the lint's name. - let replacements; - let replacements = if lint.module == old_name - && try_rename_file( - Path::new(&format!("clippy_lints/src/{old_name}.rs")), - Path::new(&format!("clippy_lints/src/{new_name}.rs")), - ) { - // Edit the module name in the lint list. Note there could be multiple lints. - for lint in lints.iter_mut().filter(|l| l.module == old_name) { - lint.module = new_name.into(); +fn collect_ui_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { + let e = e.expect("error reading `tests/ui`"); + let name = e.file_name(); + if let Some((name_only, _)) = name.as_encoded_bytes().split_once(|&x| x == b'.') { + if name_only.starts_with(lint.as_bytes()) && (rename_prefixed || name_only.len() == lint.len()) { + dst.push((name, true)); } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else if !lint.module.contains("::") - // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` - && try_rename_file( - Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), - Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), - ) + } else if name.as_encoded_bytes().starts_with(lint.as_bytes()) && (rename_prefixed || name.len() == lint.len()) { - // Edit the module name in the lint list. Note there could be multiple lints, or none. - let renamed_mod = format!("{}::{old_name}", lint.module); - for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { - lint.module = format!("{}::{new_name}", lint.module); + dst.push((name, false)); + } + } +} + +fn collect_ui_toml_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + if rename_prefixed { + for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") { + let e = e.expect("error reading `tests/ui-toml`"); + let name = e.file_name(); + if name.as_encoded_bytes().starts_with(lint.as_bytes()) && e.file_type().is_ok_and(|ty| ty.is_dir()) { + dst.push((name, false)); } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() + } + } else { + dst.push((lint.into(), false)); + } +} + +/// Renames all test files for the given lint. +/// +/// If `rename_prefixed` is `true` this will also rename tests which have the lint name as a prefix. +fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut old_buf = OsString::from("tests/ui/"); + let mut new_buf = OsString::from("tests/ui/"); + collect_ui_test_names(old_name, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + if is_file { + try_rename_file(old_buf.as_ref(), new_buf.as_ref()); } else { - replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; - &replacements[0..1] - }; - - // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being - // renamed. - let replacer = StringReplacer::new(replacements); - for file in WalkDir::new("clippy_lints/src") { - let file = file.expect("error reading `clippy_lints/src`"); - if file - .path() - .as_os_str() - .to_str() - .is_some_and(|x| x.ends_with("*.rs") && x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") - { - updater.update_file(file.path(), &mut replacer.replace_ident_fn()); - } + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + } + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); + } + + tests.clear(); + old_buf.truncate("tests/ui".len()); + new_buf.truncate("tests/ui".len()); + old_buf.push("-toml/"); + new_buf.push("-toml/"); + collect_ui_toml_test_names(old_name, rename_prefixed, &mut tests); + for (name, _) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); + } +} + +fn delete_test_files(lint: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut buf = OsString::from("tests/ui/"); + collect_ui_test_names(lint, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + buf.push(name); + if is_file { + delete_file_if_exists(buf.as_ref()); + } else { + delete_dir_if_exists(buf.as_ref()); } + buf.truncate("tests/ui/".len()); + } - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("{old_name} has been successfully renamed"); + buf.truncate("tests/ui".len()); + buf.push("-toml/"); + + tests.clear(); + collect_ui_toml_test_names(lint, rename_prefixed, &mut tests); + for (name, _) in &tests { + buf.push(name); + delete_dir_if_exists(buf.as_ref()); + buf.truncate("tests/ui/".len()); } +} - println!("note: `cargo uitest` still needs to be run to update the test results"); +fn snake_to_pascal(s: &str) -> String { + let mut dst = Vec::with_capacity(s.len()); + let mut iter = s.bytes(); + || -> Option<()> { + dst.push(iter.next()?.to_ascii_uppercase()); + while let Some(c) = iter.next() { + if c == b'_' { + dst.push(iter.next()?.to_ascii_uppercase()); + } else { + dst.push(c); + } + } + Some(()) + }(); + String::from_utf8(dst).unwrap() +} + +#[expect(clippy::too_many_lines)] +fn file_update_fn<'a, 'b>( + old_name: &'a str, + new_name: &'b str, + mod_edit: ModEdit, +) -> impl use<'a, 'b> + FnMut(&Path, &str, &mut String) -> UpdateStatus { + let old_name_pascal = snake_to_pascal(old_name); + let new_name_pascal = snake_to_pascal(new_name); + let old_name_upper = old_name.to_ascii_uppercase(); + let new_name_upper = new_name.to_ascii_uppercase(); + move |_, src, dst| { + let mut copy_pos = 0u32; + let mut changed = false; + let mut searcher = RustSearcher::new(src); + let mut capture = ""; + loop { + match searcher.peek() { + TokenKind::Eof => break, + TokenKind::Ident => { + let match_start = searcher.pos(); + let text = searcher.peek_text(); + searcher.step(); + match text { + // clippy::line_name or clippy::lint-name + "clippy" => { + if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + } + }, + // mod lint_name + "mod" => { + if !matches!(mod_edit, ModEdit::None) + && searcher.match_tokens(&[Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name + { + match mod_edit { + ModEdit::Rename => { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + }, + ModEdit::Delete if searcher.match_tokens(&[Token::Semi], &mut []) => { + let mut start = &src[copy_pos as usize..match_start as usize]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; + } + dst.push_str(start); + copy_pos = searcher.pos(); + if src[copy_pos as usize..].starts_with("\n\n") { + copy_pos += 1; + } + changed = true; + }, + ModEdit::Delete | ModEdit::None => {}, + } + } + }, + // lint_name:: + name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { + let name_end = searcher.pos(); + if searcher.match_tokens(&[Token::DoubleColon], &mut []) { + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(new_name); + copy_pos = name_end; + changed = true; + } + }, + // LINT_NAME or LintName + name => { + let replacement = if name == old_name_upper { + &new_name_upper + } else if name == old_name_pascal { + &new_name_pascal + } else { + continue; + }; + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(replacement); + copy_pos = searcher.pos(); + changed = true; + }, + } + }, + // //~ lint_name + TokenKind::LineComment { doc_style: None } => { + let text = searcher.peek_text(); + if text.starts_with("//~") + && let Some(text) = text.strip_suffix(old_name) + && !text.ends_with(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')) + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize + text.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos() + searcher.peek_len(); + changed = true; + } + searcher.step(); + }, + // ::lint_name + TokenKind::Colon + if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name => + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + }, + _ => searcher.step(), + } + } + + dst.push_str(&src[copy_pos as usize..]); + UpdateStatus::from_changed(changed) + } } diff --git a/src/tools/clippy/clippy_dev/src/sync.rs b/src/tools/clippy/clippy_dev/src/sync.rs index c699b0d7b959c..98fd72fc0bd1a 100644 --- a/src/tools/clippy/clippy_dev/src/sync.rs +++ b/src/tools/clippy/clippy_dev/src/sync.rs @@ -4,15 +4,22 @@ use std::fmt::Write; pub fn update_nightly() { let date = Utc::now().format("%Y-%m-%d").to_string(); - let update = &mut update_text_region_fn( + let toolchain_update = &mut update_text_region_fn( "# begin autogenerated nightly\n", "# end autogenerated nightly", |dst| { writeln!(dst, "channel = \"nightly-{date}\"").unwrap(); }, ); + let readme_update = &mut update_text_region_fn( + "\n", + "", + |dst| { + writeln!(dst, "```\nnightly-{date}\n```").unwrap(); + }, + ); let mut updater = FileUpdater::default(); - updater.update_file("rust-toolchain.toml", update); - updater.update_file("clippy_utils/README.md", update); + updater.update_file("rust-toolchain.toml", toolchain_update); + updater.update_file("clippy_utils/README.md", readme_update); } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 0c861b7293562..25ba2c7204904 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,12 +1,11 @@ use crate::utils::{ - File, FileAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_file, update_text_region_fn, + ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_action, update_text_region_fn, }; use itertools::Itertools; use std::collections::HashSet; use std::fmt::Write; -use std::fs::OpenOptions; use std::ops::Range; -use std::path::Path; +use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ @@ -26,12 +25,11 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// Panics if a file path could not read from or then written to pub fn update(update_mode: UpdateMode) { let lints = find_lint_decls(); - let DeprecatedLints { - renamed, deprecated, .. - } = read_deprecated_lints(); + let (deprecated, renamed) = read_deprecated_lints(); generate_lint_files(update_mode, &lints, &deprecated, &renamed); } +#[expect(clippy::too_many_lines)] pub fn generate_lint_files( update_mode: UpdateMode, lints: &[Lint], @@ -93,6 +91,40 @@ pub fn generate_lint_files( dst.push_str("];\n"); UpdateStatus::from_changed(src != dst) }), + ("clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| { + let mut searcher = RustSearcher::new(src); + assert!( + searcher.find_token(Token::Ident("declare_with_version")) + && searcher.find_token(Token::Ident("declare_with_version")), + "error reading deprecated lints" + ); + dst.push_str(&src[..searcher.pos() as usize]); + dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); + for lint in deprecated { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.name, lint.reason, + ) + .unwrap(); + } + dst.push_str( + "]}\n\n\ + #[rustfmt::skip]\n\ + declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ + ", + ); + for lint in renamed { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.old_name, lint.new_name, + ) + .unwrap(); + } + dst.push_str("]}\n"); + UpdateStatus::from_changed(src != dst) + }), ("tests/ui/deprecated.rs", &mut |_, src, dst| { dst.push_str(GENERATED_FILE_COMMENT); for lint in deprecated { @@ -101,7 +133,24 @@ pub fn generate_lint_files( dst.push_str("\nfn main() {}\n"); UpdateStatus::from_changed(src != dst) }), - ("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(renamed)), + ("tests/ui/rename.rs", &mut move |_, src, dst| { + let mut seen_lints = HashSet::new(); + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); + for lint in renamed { + if seen_lints.insert(&lint.new_name) { + writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); + } + } + seen_lints.clear(); + for lint in renamed { + if seen_lints.insert(&lint.old_name) { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + } + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }), ], ); } @@ -111,44 +160,25 @@ fn round_to_fifty(count: usize) -> usize { } /// Lint data parsed from the Clippy source code. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug)] pub struct Lint { pub name: String, pub group: String, pub module: String, + pub path: PathBuf, pub declaration_range: Range, } -#[derive(Clone, PartialEq, Eq, Debug)] pub struct DeprecatedLint { pub name: String, pub reason: String, + pub version: String, } pub struct RenamedLint { pub old_name: String, pub new_name: String, -} - -pub fn gen_renamed_lints_test_fn(lints: &[RenamedLint]) -> impl Fn(&Path, &str, &mut String) -> UpdateStatus { - move |_, src, dst| { - let mut seen_lints = HashSet::new(); - dst.push_str(GENERATED_FILE_COMMENT); - dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in lints { - if seen_lints.insert(&lint.new_name) { - writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); - } - } - seen_lints.clear(); - for lint in lints { - if seen_lints.insert(&lint.old_name) { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); - } - } - dst.push_str("\nfn main() {}\n"); - UpdateStatus::from_changed(src != dst) - } + pub version: String, } /// Finds all lint declarations (`declare_clippy_lint!`) @@ -158,6 +188,7 @@ pub fn find_lint_decls() -> Vec { let mut contents = String::new(); for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { parse_clippy_lint_decls( + file.path(), File::open_read_to_cleared_string(file.path(), &mut contents), &module, &mut lints, @@ -172,7 +203,7 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator e, - Err(ref e) => panic_file(e, FileAction::Read, src_root), + Err(ref e) => panic_action(e, ErrAction::Read, src_root), }; let path = e.path().as_os_str().as_encoded_bytes(); if let Some(path) = path.strip_suffix(b".rs") @@ -202,17 +233,17 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator) { +fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mut Vec) { #[allow(clippy::enum_glob_use)] use Token::*; #[rustfmt::skip] - static DECL_TOKENS: &[Token] = &[ + static DECL_TOKENS: &[Token<'_>] = &[ // !{ /// docs - Bang, OpenBrace, AnyDoc, + Bang, OpenBrace, AnyComment, // #[clippy::version = "version"] Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, // pub NAME, GROUP, - Ident("pub"), CaptureIdent, Comma, CaptureIdent, Comma, + Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, ]; let mut searcher = RustSearcher::new(contents); @@ -224,55 +255,42 @@ fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec) name: name.to_lowercase(), group: group.into(), module: module.into(), + path: path.into(), declaration_range: start..searcher.pos() as usize, }); } } } -pub struct DeprecatedLints { - pub file: File<'static>, - pub contents: String, - pub deprecated: Vec, - pub renamed: Vec, - pub deprecated_end: u32, - pub renamed_end: u32, -} - #[must_use] -pub fn read_deprecated_lints() -> DeprecatedLints { +pub fn read_deprecated_lints() -> (Vec, Vec) { #[allow(clippy::enum_glob_use)] use Token::*; #[rustfmt::skip] - static DECL_TOKENS: &[Token] = &[ + static DECL_TOKENS: &[Token<'_>] = &[ // #[clippy::version = "version"] - Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket, // ("first", "second"), OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, ]; #[rustfmt::skip] - static DEPRECATED_TOKENS: &[Token] = &[ + static DEPRECATED_TOKENS: &[Token<'_>] = &[ // !{ DEPRECATED(DEPRECATED_VERSION) = [ Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, ]; #[rustfmt::skip] - static RENAMED_TOKENS: &[Token] = &[ + static RENAMED_TOKENS: &[Token<'_>] = &[ // !{ RENAMED(RENAMED_VERSION) = [ Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, ]; let path = "clippy_lints/src/deprecated_lints.rs"; - let mut res = DeprecatedLints { - file: File::open(path, OpenOptions::new().read(true).write(true)), - contents: String::new(), - deprecated: Vec::with_capacity(30), - renamed: Vec::with_capacity(80), - deprecated_end: 0, - renamed_end: 0, - }; + let mut deprecated = Vec::with_capacity(30); + let mut renamed = Vec::with_capacity(80); + let mut contents = String::new(); + File::open_read_to_cleared_string(path, &mut contents); - res.file.read_append_to_string(&mut res.contents); - let mut searcher = RustSearcher::new(&res.contents); + let mut searcher = RustSearcher::new(&contents); // First instance is the macro definition. assert!( @@ -281,36 +299,38 @@ pub fn read_deprecated_lints() -> DeprecatedLints { ); if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { + let mut version = ""; let mut name = ""; let mut reason = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut reason]) { - res.deprecated.push(DeprecatedLint { + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { + deprecated.push(DeprecatedLint { name: parse_str_single_line(path.as_ref(), name), reason: parse_str_single_line(path.as_ref(), reason), + version: parse_str_single_line(path.as_ref(), version), }); } } else { panic!("error reading deprecated lints"); } - // position of the closing `]}` of `declare_with_version` - res.deprecated_end = searcher.pos(); if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { + let mut version = ""; let mut old_name = ""; let mut new_name = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut old_name, &mut new_name]) { - res.renamed.push(RenamedLint { + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { + renamed.push(RenamedLint { old_name: parse_str_single_line(path.as_ref(), old_name), new_name: parse_str_single_line(path.as_ref(), new_name), + version: parse_str_single_line(path.as_ref(), version), }); } } else { panic!("error reading renamed lints"); } - // position of the closing `]}` of `declare_with_version` - res.renamed_end = searcher.pos(); - res + deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); + (deprecated, renamed) } /// Removes the line splices and surrounding quotes from a string literal @@ -366,7 +386,7 @@ mod tests { } "#; let mut result = Vec::new(); - parse_clippy_lint_decls(CONTENTS, "module_name", &mut result); + parse_clippy_lint_decls("".as_ref(), CONTENTS, "module_name", &mut result); for r in &mut result { r.declaration_range = Range::default(); } @@ -376,12 +396,14 @@ mod tests { name: "ptr_arg".into(), group: "style".into(), module: "module_name".into(), + path: PathBuf::new(), declaration_range: Range::default(), }, Lint { name: "doc_markdown".into(), group: "pedantic".into(), module: "module_name".into(), + path: PathBuf::new(), declaration_range: Range::default(), }, ]; diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs index ae2eabc45dd00..255e36afe69c2 100644 --- a/src/tools/clippy/clippy_dev/src/utils.rs +++ b/src/tools/clippy/clippy_dev/src/utils.rs @@ -1,13 +1,14 @@ -use aho_corasick::{AhoCorasick, AhoCorasickBuilder}; use core::fmt::{self, Display}; +use core::ops::Range; use core::slice; use core::str::FromStr; use rustc_lexer::{self as lexer, FrontmatterAllowed}; use std::env; +use std::ffi::OsStr; use std::fs::{self, OpenOptions}; use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; use std::path::{Path, PathBuf}; -use std::process::{self, ExitStatus}; +use std::process::{self, Command, ExitStatus, Stdio}; #[cfg(not(windows))] static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; @@ -15,14 +16,16 @@ static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; #[derive(Clone, Copy)] -pub enum FileAction { +pub enum ErrAction { Open, Read, Write, Create, Rename, + Delete, + Run, } -impl FileAction { +impl ErrAction { fn as_str(self) -> &'static str { match self { Self::Open => "opening", @@ -30,13 +33,15 @@ impl FileAction { Self::Write => "writing", Self::Create => "creating", Self::Rename => "renaming", + Self::Delete => "deleting", + Self::Run => "running", } } } #[cold] #[track_caller] -pub fn panic_file(err: &impl Display, action: FileAction, path: &Path) -> ! { +pub fn panic_action(err: &impl Display, action: ErrAction, path: &Path) -> ! { panic!("error {} `{}`: {}", action.as_str(), path.display(), *err) } @@ -52,7 +57,7 @@ impl<'a> File<'a> { let path = path.as_ref(); match options.open(path) { Ok(inner) => Self { inner, path }, - Err(e) => panic_file(&e, FileAction::Open, path), + Err(e) => panic_action(&e, ErrAction::Open, path), } } @@ -63,7 +68,7 @@ impl<'a> File<'a> { match options.open(path) { Ok(inner) => Some(Self { inner, path }), Err(e) if e.kind() == io::ErrorKind::NotFound => None, - Err(e) => panic_file(&e, FileAction::Open, path), + Err(e) => panic_action(&e, ErrAction::Open, path), } } @@ -81,7 +86,7 @@ impl<'a> File<'a> { pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { match self.inner.read_to_string(dst) { Ok(_) => {}, - Err(e) => panic_file(&e, FileAction::Read, self.path), + Err(e) => panic_action(&e, ErrAction::Read, self.path), } dst } @@ -103,7 +108,7 @@ impl<'a> File<'a> { Err(e) => Err(e), }; if let Err(e) = res { - panic_file(&e, FileAction::Write, self.path); + panic_action(&e, ErrAction::Write, self.path); } } } @@ -165,9 +170,83 @@ impl Version { } } +enum TomlPart<'a> { + Table(&'a str), + Value(&'a str, &'a str), +} + +fn toml_iter(s: &str) -> impl Iterator)> { + let mut pos = 0; + s.split('\n') + .map(move |s| { + let x = pos; + pos += s.len() + 1; + (x, s) + }) + .filter_map(|(pos, s)| { + if let Some(s) = s.strip_prefix('[') { + s.split_once(']').map(|(name, _)| (pos, TomlPart::Table(name))) + } else if matches!(s.bytes().next(), Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')) { + s.split_once('=').map(|(key, value)| (pos, TomlPart::Value(key, value))) + } else { + None + } + }) +} + +pub struct CargoPackage<'a> { + pub name: &'a str, + pub version_range: Range, + pub not_a_platform_range: Range, +} + +#[must_use] +pub fn parse_cargo_package(s: &str) -> CargoPackage<'_> { + let mut in_package = false; + let mut in_platform_deps = false; + let mut name = ""; + let mut version_range = 0..0; + let mut not_a_platform_range = 0..0; + for (offset, part) in toml_iter(s) { + match part { + TomlPart::Table(name) => { + if in_platform_deps { + not_a_platform_range.end = offset; + } + in_package = false; + in_platform_deps = false; + + match name.trim() { + "package" => in_package = true, + "target.'cfg(NOT_A_PLATFORM)'.dependencies" => { + in_platform_deps = true; + not_a_platform_range.start = offset; + }, + _ => {}, + } + }, + TomlPart::Value(key, value) if in_package => match key.trim_end() { + "name" => name = value.trim(), + "version" => { + version_range.start = offset + (value.len() - value.trim().len()) + key.len() + 1; + version_range.end = offset + key.len() + value.trim_end().len() + 1; + }, + _ => {}, + }, + TomlPart::Value(..) => {}, + } + } + CargoPackage { + name, + version_range, + not_a_platform_range, + } +} + pub struct ClippyInfo { pub path: PathBuf, pub version: Version, + pub has_intellij_hook: bool, } impl ClippyInfo { #[must_use] @@ -177,35 +256,21 @@ impl ClippyInfo { loop { path.push("Cargo.toml"); if let Some(mut file) = File::open_if_exists(&path, OpenOptions::new().read(true)) { - let mut in_package = false; - let mut is_clippy = false; - let mut version: Option = None; - - // Ad-hoc parsing to avoid dependencies. We control all the file so this - // isn't actually a problem - for line in file.read_to_cleared_string(&mut buf).lines() { - if line.starts_with('[') { - in_package = line.starts_with("[package]"); - } else if in_package && let Some((name, value)) = line.split_once('=') { - match name.trim() { - "name" => is_clippy = value.trim() == "\"clippy\"", - "version" - if let Some(value) = value.trim().strip_prefix('"') - && let Some(value) = value.strip_suffix('"') => - { - version = value.parse().ok(); - }, - _ => {}, - } + file.read_to_cleared_string(&mut buf); + let package = parse_cargo_package(&buf); + if package.name == "\"clippy\"" { + if let Some(version) = buf[package.version_range].strip_prefix('"') + && let Some(version) = version.strip_suffix('"') + && let Ok(version) = version.parse() + { + path.pop(); + return ClippyInfo { + path, + version, + has_intellij_hook: !package.not_a_platform_range.is_empty(), + }; } - } - - if is_clippy { - let Some(version) = version else { - panic!("error reading clippy version from {}", file.path.display()); - }; - path.pop(); - return ClippyInfo { path, version }; + panic!("error reading clippy version from `{}`", file.path.display()); } } @@ -258,6 +323,11 @@ impl UpdateMode { pub fn from_check(check: bool) -> Self { if check { Self::Check } else { Self::Change } } + + #[must_use] + pub fn is_check(self) -> bool { + matches!(self, Self::Check) + } } #[derive(Default)] @@ -366,53 +436,11 @@ pub fn update_text_region_fn( move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert) } -#[must_use] -pub fn is_ident_char(c: u8) -> bool { - matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') -} - -pub struct StringReplacer<'a> { - searcher: AhoCorasick, - replacements: &'a [(&'a str, &'a str)], -} -impl<'a> StringReplacer<'a> { - #[must_use] - pub fn new(replacements: &'a [(&'a str, &'a str)]) -> Self { - Self { - searcher: AhoCorasickBuilder::new() - .match_kind(aho_corasick::MatchKind::LeftmostLongest) - .build(replacements.iter().map(|&(x, _)| x)) - .unwrap(), - replacements, - } - } - - /// Replace substrings if they aren't bordered by identifier characters. - pub fn replace_ident_fn(&self) -> impl Fn(&Path, &str, &mut String) -> UpdateStatus { - move |_, src, dst| { - let mut pos = 0; - let mut changed = false; - for m in self.searcher.find_iter(src) { - if !is_ident_char(src.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) - && !is_ident_char(src.as_bytes().get(m.end()).copied().unwrap_or(0)) - { - changed = true; - dst.push_str(&src[pos..m.start()]); - dst.push_str(self.replacements[m.pattern()].1); - pos = m.end(); - } - } - dst.push_str(&src[pos..]); - UpdateStatus::from_changed(changed) - } - } -} - #[derive(Clone, Copy)] -pub enum Token { - /// Matches any number of doc comments. - AnyDoc, - Ident(&'static str), +pub enum Token<'a> { + /// Matches any number of comments / doc comments. + AnyComment, + Ident(&'a str), CaptureIdent, LitStr, CaptureLitStr, @@ -431,29 +459,26 @@ pub enum Token { OpenBracket, OpenParen, Pound, + Semi, + Slash, } pub struct RustSearcher<'txt> { text: &'txt str, cursor: lexer::Cursor<'txt>, pos: u32, - - // Either the next token or a zero-sized whitespace sentinel. next_token: lexer::Token, } impl<'txt> RustSearcher<'txt> { #[must_use] + #[expect(clippy::inconsistent_struct_constructor)] pub fn new(text: &'txt str) -> Self { + let mut cursor = lexer::Cursor::new(text, FrontmatterAllowed::Yes); Self { text, - cursor: lexer::Cursor::new(text, FrontmatterAllowed::Yes), pos: 0, - - // Sentinel value indicating there is no read token. - next_token: lexer::Token { - len: 0, - kind: lexer::TokenKind::Whitespace, - }, + next_token: cursor.advance_token(), + cursor, } } @@ -462,6 +487,11 @@ impl<'txt> RustSearcher<'txt> { &self.text[self.pos as usize..(self.pos + self.next_token.len) as usize] } + #[must_use] + pub fn peek_len(&self) -> u32 { + self.next_token.len + } + #[must_use] pub fn peek(&self) -> lexer::TokenKind { self.next_token.kind @@ -485,37 +515,15 @@ impl<'txt> RustSearcher<'txt> { /// Consumes the next token if it matches the requested value and captures the value if /// requested. Returns true if a token was matched. - fn read_token(&mut self, token: Token, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { + fn read_token(&mut self, token: Token<'_>, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { loop { match (token, self.next_token.kind) { - // Has to be the first match arm so the empty sentinel token will be handled. - // This will also skip all whitespace/comments preceding any tokens. - ( - _, - lexer::TokenKind::Whitespace - | lexer::TokenKind::LineComment { doc_style: None } - | lexer::TokenKind::BlockComment { - doc_style: None, - terminated: true, - }, - ) => { - self.step(); - if self.at_end() { - // `AnyDoc` always matches. - return matches!(token, Token::AnyDoc); - } - }, - ( - Token::AnyDoc, + (_, lexer::TokenKind::Whitespace) + | ( + Token::AnyComment, lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, - ) => { - self.step(); - if self.at_end() { - // `AnyDoc` always matches. - return true; - } - }, - (Token::AnyDoc, _) => return true, + ) => self.step(), + (Token::AnyComment, _) => return true, (Token::Bang, lexer::TokenKind::Bang) | (Token::CloseBrace, lexer::TokenKind::CloseBrace) | (Token::CloseBracket, lexer::TokenKind::CloseBracket) @@ -529,6 +537,8 @@ impl<'txt> RustSearcher<'txt> { | (Token::OpenBracket, lexer::TokenKind::OpenBracket) | (Token::OpenParen, lexer::TokenKind::OpenParen) | (Token::Pound, lexer::TokenKind::Pound) + | (Token::Semi, lexer::TokenKind::Semi) + | (Token::Slash, lexer::TokenKind::Slash) | ( Token::LitStr, lexer::TokenKind::Literal { @@ -569,7 +579,7 @@ impl<'txt> RustSearcher<'txt> { } #[must_use] - pub fn find_token(&mut self, token: Token) -> bool { + pub fn find_token(&mut self, token: Token<'_>) -> bool { let mut capture = [].iter_mut(); while !self.read_token(token, &mut capture) { self.step(); @@ -581,7 +591,7 @@ impl<'txt> RustSearcher<'txt> { } #[must_use] - pub fn find_capture_token(&mut self, token: Token) -> Option<&'txt str> { + pub fn find_capture_token(&mut self, token: Token<'_>) -> Option<&'txt str> { let mut res = ""; let mut capture = &mut res; let mut capture = slice::from_mut(&mut capture).iter_mut(); @@ -595,7 +605,7 @@ impl<'txt> RustSearcher<'txt> { } #[must_use] - pub fn match_tokens(&mut self, tokens: &[Token], captures: &mut [&mut &'txt str]) -> bool { + pub fn match_tokens(&mut self, tokens: &[Token<'_>], captures: &mut [&mut &'txt str]) -> bool { let mut captures = captures.iter_mut(); tokens.iter().all(|&t| self.read_token(t, &mut captures)) } @@ -606,21 +616,107 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { Ok(file) => drop(file), Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, - Err(e) => panic_file(&e, FileAction::Create, new_name), + Err(ref e) => panic_action(e, ErrAction::Create, new_name), } match fs::rename(old_name, new_name) { Ok(()) => true, - Err(e) => { + Err(ref e) => { drop(fs::remove_file(new_name)); - if e.kind() == io::ErrorKind::NotFound { + // `NotADirectory` happens on posix when renaming a directory to an existing file. + // Windows will ignore this and rename anyways. + if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) { false } else { - panic_file(&e, FileAction::Rename, old_name); + panic_action(e, ErrAction::Rename, old_name); + } + }, + } +} + +#[expect(clippy::must_use_candidate)] +pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { + match fs::create_dir(new_name) { + Ok(()) => {}, + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(ref e) => panic_action(e, ErrAction::Create, new_name), + } + // Windows can't reliably rename to an empty directory. + #[cfg(windows)] + drop(fs::remove_dir(new_name)); + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(ref e) => { + // Already dropped earlier on windows. + #[cfg(not(windows))] + drop(fs::remove_dir(new_name)); + // `NotADirectory` happens on posix when renaming a file to an existing directory. + if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) { + false + } else { + panic_action(e, ErrAction::Rename, old_name); } }, } } pub fn write_file(path: &Path, contents: &str) { - fs::write(path, contents).unwrap_or_else(|e| panic_file(&e, FileAction::Write, path)); + fs::write(path, contents).unwrap_or_else(|e| panic_action(&e, ErrAction::Write, path)); +} + +#[must_use] +pub fn run_with_output(path: &(impl AsRef + ?Sized), cmd: &mut Command) -> Vec { + fn f(path: &Path, cmd: &mut Command) -> Vec { + match cmd + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .output() + { + Ok(x) => match x.status.exit_ok() { + Ok(()) => x.stdout, + Err(ref e) => panic_action(e, ErrAction::Run, path), + }, + Err(ref e) => panic_action(e, ErrAction::Run, path), + } + } + f(path.as_ref(), cmd) +} + +pub fn run_with_args_split( + mut make_cmd: impl FnMut() -> Command, + mut run_cmd: impl FnMut(&mut Command), + args: impl Iterator>, +) { + let mut cmd = make_cmd(); + let mut len = 0; + for arg in args { + len += arg.as_ref().len(); + cmd.arg(arg); + // Very conservative limit + if len > 10000 { + run_cmd(&mut cmd); + cmd = make_cmd(); + len = 0; + } + } + if len != 0 { + run_cmd(&mut cmd); + } +} + +#[expect(clippy::must_use_candidate)] +pub fn delete_file_if_exists(path: &Path) -> bool { + match fs::remove_file(path) { + Ok(()) => true, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::IsADirectory) => false, + Err(ref e) => panic_action(e, ErrAction::Delete, path), + } +} + +pub fn delete_dir_if_exists(path: &Path) { + match fs::remove_dir_all(path) { + Ok(()) => {}, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) => {}, + Err(ref e) => panic_action(e, ErrAction::Delete, path), + } } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 7e3cb40424792..39e4e2e365ea3 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_lints" -# begin autogenerated version version = "0.1.89" -# end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 272444475c0c4..5c1c85d39184d 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -6,9 +6,10 @@ use clippy_config::types::{ }; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::is_cfg_test; +use rustc_attr_data_structures::AttributeKind; use rustc_hir::{ - AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, Variant, - VariantData, + AssocItemKind, Attribute, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, + Variant, VariantData, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -28,6 +29,11 @@ declare_clippy_lint! { /// implemented in the code. Sometimes this will be referred to as /// "bikeshedding". /// + /// The content of items with a representation clause attribute, such as + /// `#[repr(C)]` will not be checked, as the order of their fields or + /// variants might be dictated by an external API (application binary + /// interface). + /// /// ### Default Ordering and Configuration /// /// As there is no generally applicable rule, and each project may have @@ -256,6 +262,15 @@ impl ArbitrarySourceItemOrdering { impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if cx + .tcx + .hir_attrs(item.hir_id()) + .iter() + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr(..)))) + { + // Do not lint items with a `#[repr]` attribute as their layout may be imposed by an external API. + return; + } match &item.kind { ItemKind::Enum(_, enum_def, _generics) if self.enable_ordering_for_enum => { let mut cur_v: Option<&Variant<'_>> = None; diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index c073dee855e20..6f2a6a36a38b7 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -3,14 +3,13 @@ use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_no use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{is_expr_final_block_expr, path_res}; +use clippy_utils::{is_expr_final_block_expr, path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -68,11 +67,11 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { return; } } - let (message, replacement) = match method_segment.ident.as_str() { - "is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => { + let (message, replacement) = match method_segment.ident.name { + sym::is_ok if type_suitable_to_unwrap(cx, args.type_at(1)) => { ("called `assert!` with `Result::is_ok`", "unwrap") }, - "is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => { + sym::is_err if type_suitable_to_unwrap(cx, args.type_at(0)) => { ("called `assert!` with `Result::is_err`", "unwrap_err") }, _ => return, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index e92879b853d7b..4120e5c8cb7d0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -56,7 +56,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b if signed { return nbits; } - let max_bits = if method.ident.as_str() == "min" { + let max_bits = if method.ident.name == sym::min { get_constant_bits(cx, right) } else { None @@ -64,7 +64,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX)) }, ExprKind::MethodCall(method, _, [lo, hi], _) => { - if method.ident.as_str() == "clamp" + if method.ident.name == sym::clamp //FIXME: make this a diagnostic item && let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index c8abf9dac9af5..9a1ad8a747386 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -4,10 +4,11 @@ use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{method_chain_args, sext}; +use clippy_utils::{method_chain_args, sext, sym}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::Symbol; use super::CAST_SIGN_LOSS; @@ -16,24 +17,24 @@ use super::CAST_SIGN_LOSS; /// /// Methods that can overflow and return a negative value must not be included in this list, /// because casting their return values can still result in sign loss. -const METHODS_RET_POSITIVE: &[&str] = &[ - "checked_abs", - "saturating_abs", - "isqrt", - "checked_isqrt", - "rem_euclid", - "checked_rem_euclid", - "wrapping_rem_euclid", +const METHODS_RET_POSITIVE: &[Symbol] = &[ + sym::checked_abs, + sym::saturating_abs, + sym::isqrt, + sym::checked_isqrt, + sym::rem_euclid, + sym::checked_rem_euclid, + sym::wrapping_rem_euclid, ]; /// A list of methods that act like `pow()`. See `pow_call_result_sign()` for details. /// /// Methods that can overflow and return a negative value must not be included in this list, /// because casting their return values can still result in sign loss. -const METHODS_POW: &[&str] = &["pow", "saturating_pow", "checked_pow"]; +const METHODS_POW: &[Symbol] = &[sym::pow, sym::saturating_pow, sym::checked_pow]; /// A list of methods that act like `unwrap()`, and don't change the sign of the inner value. -const METHODS_UNWRAP: &[&str] = &["unwrap", "unwrap_unchecked", "expect", "into_ok"]; +const METHODS_UNWRAP: &[Symbol] = &[sym::unwrap, sym::unwrap_unchecked, sym::expect, sym::into_ok]; pub(super) fn check<'cx>( cx: &LateContext<'cx>, @@ -129,7 +130,7 @@ fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: i // Calling on methods that always return non-negative values. if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind { - let mut method_name = path.ident.name.as_str(); + let mut method_name = path.ident.name; // Peel unwrap(), expect(), etc. while let Some(&found_name) = METHODS_UNWRAP.iter().find(|&name| &method_name == name) @@ -138,7 +139,7 @@ fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: i { // The original type has changed, but we can't use `ty` here anyway, because it has been // moved. - method_name = inner_path.ident.name.as_str(); + method_name = inner_path.ident.name; expr = recv; } diff --git a/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs index 31cdd078f45a0..769cc120c9504 100644 --- a/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArg, Ty}; +use rustc_span::Symbol; use rustc_span::def_id::DefId; -use rustc_span::{Symbol, sym}; use super::CONFUSING_METHOD_TO_NUMERIC_CAST; @@ -25,7 +26,6 @@ fn get_const_name_and_ty_name( method_def_id: DefId, generics: &[GenericArg<'_>], ) -> Option<(&'static str, &'static str)> { - let method_name = method_name.as_str(); let diagnostic_name = cx.tcx.get_diagnostic_name(method_def_id); let ty_name = if diagnostic_name.is_some_and(|diag| diag == sym::cmp_ord_min || diag == sym::cmp_ord_max) { @@ -39,14 +39,21 @@ fn get_const_name_and_ty_name( } } else if let Some(impl_id) = cx.tcx.impl_of_method(method_def_id) && let Some(ty_name) = get_primitive_ty_name(cx.tcx.type_of(impl_id).instantiate_identity()) - && ["min", "max", "minimum", "maximum", "min_value", "max_value"].contains(&method_name) + && matches!( + method_name, + sym::min | sym::max | sym::minimum | sym::maximum | sym::min_value | sym::max_value + ) { ty_name } else { return None; }; - let const_name = if method_name.starts_with("max") { "MAX" } else { "MIN" }; + let const_name = if matches!(method_name, sym::max | sym::maximum | sym::max_value) { + "MAX" + } else { + "MIN" + }; Some((const_name, ty_name)) } diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index 2471c7355518b..c0c0a47f85515 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; +use clippy_utils::{std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; -use rustc_span::sym; use super::PTR_CAST_CONSTNESS; @@ -78,9 +77,9 @@ pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) && let ExprKind::Call(func, []) = cast_expr.kind && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind && let Some(defid) = path.res.opt_def_id() - && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) { - (Some(sym::ptr_null), "cast_mut") => "null_mut", - (Some(sym::ptr_null_mut), "cast_const") => "null", + && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.name) { + (Some(sym::ptr_null), sym::cast_mut) => "null_mut", + (Some(sym::ptr_null_mut), sym::cast_const) => "null", _ => return, } && let Some(prefix) = std_or_core(cx) diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 1d44c7e9c88b0..5c64216dd92ca 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -3,14 +3,14 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn}; +use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn, sym}; use core::ops::ControlFlow; use rustc_hir::intravisit::FnKind; use rustc_hir::{Attribute, Body, Expr, ExprKind, FnDecl}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -104,7 +104,7 @@ impl CognitiveComplexity { FnKind::Closure => { let header_span = body_span.with_hi(decl.output.span().lo()); #[expect(clippy::range_plus_one)] - if let Some(range) = header_span.map_range(cx, |src, range| { + if let Some(range) = header_span.map_range(cx, |_, src, range| { let mut idxs = src.get(range.clone())?.match_indices('|'); Some(range.start + idxs.next()?.0..range.start + idxs.next()?.0 + 1) }) { @@ -157,9 +157,9 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { } fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - self.limit.push_attrs(cx.sess(), attrs, "cognitive_complexity"); + self.limit.push_attrs(cx.sess(), attrs, sym::cognitive_complexity); } fn check_attributes_post(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity"); + self.limit.pop_attrs(cx.sess(), attrs, sym::cognitive_complexity); } } diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index 9c3009a86cdc0..238ebd4a444cf 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -75,11 +75,15 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } // Check that there exists at least one explicit else condition - let (conds, _) = if_sequence(expr); + let (conds, blocks) = if_sequence(expr); if conds.len() < 2 { return; } + if blocks.len() < 3 { + return; + } + for cond in conds.windows(2) { if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) = (&cond[0].kind, &cond[1].kind) @@ -125,6 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { let ExprKind::Binary(_, lhs, rhs) = conds[0].kind else { unreachable!(); }; + let lhs = Sugg::hir(cx, lhs, "..").maybe_paren(); let rhs = Sugg::hir(cx, rhs, "..").addr(); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 42fbe6438d4ea..2467fc95fd055 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; use clippy_utils::visitors::for_each_expr_without_closures; @@ -258,7 +258,7 @@ fn lint_branches_sharing_code<'tcx>( let span = span.with_hi(last_block.span.hi()); // Improve formatting if the inner block has indentation (i.e. normal Rust formatting) let span = span - .map_range(cx, |src, range| { + .map_range(cx, |_, src, range| { (range.start > 4 && src.get(range.start - 4..range.start)? == " ") .then_some(range.start - 4..range.end) }) @@ -567,7 +567,7 @@ fn method_caller_is_mutable<'tcx>( /// Implementation of `IFS_SAME_COND`. fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { - for (i, j) in search_same( + for group in search_same( conds, |e| hash_expr(cx, e), |lhs, rhs| { @@ -584,14 +584,8 @@ fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mu } }, ) { - span_lint_and_note( - cx, - IFS_SAME_COND, - j.span, - "this `if` has the same condition as a previous `if`", - Some(i.span), - "same as this", - ); + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); } } @@ -609,14 +603,13 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { SpanlessEq::new(cx).eq_expr(lhs, rhs) }; - for (i, j) in search_same(conds, |e| hash_expr(cx, e), eq) { - span_lint_and_note( + for group in search_same(conds, |e| hash_expr(cx, e), eq) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint( cx, SAME_FUNCTIONS_IN_IF_CONDITION, - j.span, - "this `if` has the same function call as a previous `if`", - Some(i.span), - "same as this", + spans, + "these `if` branches have the same function call", ); } } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index bb825c7655f87..5fcb851dfebce 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -764,6 +764,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unwrap_in_result::UNWRAP_IN_RESULT_INFO, crate::upper_case_acronyms::UPPER_CASE_ACRONYMS_INFO, crate::use_self::USE_SELF_INFO, + crate::useless_concat::USELESS_CONCAT_INFO, crate::useless_conversion::USELESS_CONVERSION_INFO, crate::vec::USELESS_VEC_INFO, crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index 946515386690c..5204f73ea0a95 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -14,36 +14,36 @@ macro_rules! declare_with_version { #[rustfmt::skip] declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ - #[clippy::version = "pre 1.29.0"] - ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), + #[clippy::version = "1.30.0"] + ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), + #[clippy::version = "1.86.0"] + ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), + #[clippy::version = "pre 1.29.0"] + ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), + #[clippy::version = "1.86.0"] + ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), + #[clippy::version = "1.54.0"] + ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), #[clippy::version = "pre 1.29.0"] ("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"), + #[clippy::version = "1.47.0"] + ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), + #[clippy::version = "1.44.0"] + ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "pre 1.29.0"] - ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), + ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), #[clippy::version = "pre 1.29.0"] - ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), + ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), #[clippy::version = "pre 1.29.0"] - ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), - #[clippy::version = "1.30.0"] - ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), + ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), #[clippy::version = "pre 1.29.0"] - ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), + ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), #[clippy::version = "1.39.0"] ("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"), - #[clippy::version = "1.44.0"] - ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), - #[clippy::version = "1.47.0"] - ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), - #[clippy::version = "1.54.0"] - ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), #[clippy::version = "1.54.0"] ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), - #[clippy::version = "1.86.0"] - ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), - #[clippy::version = "1.86.0"] - ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), ]} #[rustfmt::skip] @@ -61,6 +61,12 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::box_vec", "clippy::box_collection"), #[clippy::version = ""] + ("clippy::cast_ref_to_mut", "invalid_reference_casting"), + #[clippy::version = ""] + ("clippy::clone_double_ref", "suspicious_double_ref_op"), + #[clippy::version = ""] + ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = ""] ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), #[clippy::version = ""] ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), @@ -70,15 +76,35 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::disallowed_method", "clippy::disallowed_methods"), #[clippy::version = ""] ("clippy::disallowed_type", "clippy::disallowed_types"), + #[clippy::version = "1.86.0"] + ("clippy::double_neg", "double_negations"), + #[clippy::version = ""] + ("clippy::drop_bounds", "drop_bounds"), + #[clippy::version = ""] + ("clippy::drop_copy", "dropping_copy_types"), + #[clippy::version = ""] + ("clippy::drop_ref", "dropping_references"), #[clippy::version = ""] ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - #[clippy::version = "1.51.0"] - ("clippy::find_map", "clippy::manual_find_map"), #[clippy::version = "1.53.0"] ("clippy::filter_map", "clippy::manual_filter_map"), + #[clippy::version = "1.51.0"] + ("clippy::find_map", "clippy::manual_find_map"), #[clippy::version = ""] ("clippy::fn_address_comparisons", "unpredictable_function_pointer_comparisons"), #[clippy::version = ""] + ("clippy::fn_null_check", "useless_ptr_null_checks"), + #[clippy::version = ""] + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::forget_copy", "forgetting_copy_types"), + #[clippy::version = ""] + ("clippy::forget_ref", "forgetting_references"), + #[clippy::version = ""] ("clippy::identity_conversion", "clippy::useless_conversion"), #[clippy::version = "pre 1.29.0"] ("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"), @@ -91,7 +117,25 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), #[clippy::version = ""] + ("clippy::into_iter_on_array", "array_into_iter"), + #[clippy::version = ""] + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + #[clippy::version = "CURRENT_RUSTC_VERSION"] + ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), + #[clippy::version = ""] + ("clippy::invalid_ref", "invalid_value"), + #[clippy::version = ""] + ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), + #[clippy::version = ""] + ("clippy::let_underscore_drop", "let_underscore_drop"), + #[clippy::version = ""] ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), + #[clippy::version = "1.80.0"] + ("clippy::maybe_misused_cfg", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), + #[clippy::version = "1.80.0"] + ("clippy::mismatched_target_os", "unexpected_cfgs"), #[clippy::version = ""] ("clippy::new_without_default_derive", "clippy::new_without_default"), #[clippy::version = ""] @@ -107,6 +151,10 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), #[clippy::version = ""] + ("clippy::panic_params", "non_fmt_panics"), + #[clippy::version = ""] + ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), + #[clippy::version = ""] ("clippy::ref_in_deref", "clippy::needless_borrow"), #[clippy::version = ""] ("clippy::result_expect_used", "clippy::expect_used"), @@ -115,67 +163,25 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::result_unwrap_used", "clippy::unwrap_used"), #[clippy::version = ""] + ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), + #[clippy::version = ""] ("clippy::single_char_push_str", "clippy::single_char_add_str"), #[clippy::version = ""] ("clippy::stutter", "clippy::module_name_repetitions"), #[clippy::version = ""] + ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"), + #[clippy::version = ""] ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), #[clippy::version = ""] ("clippy::to_string_in_display", "clippy::recursive_format_impl"), - #[clippy::version = ""] - ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), - #[clippy::version = ""] - ("clippy::zero_width_space", "clippy::invisible_characters"), - #[clippy::version = ""] - ("clippy::cast_ref_to_mut", "invalid_reference_casting"), - #[clippy::version = ""] - ("clippy::clone_double_ref", "suspicious_double_ref_op"), - #[clippy::version = ""] - ("clippy::cmp_nan", "invalid_nan_comparisons"), - #[clippy::version = "CURRENT_RUSTC_VERSION"] - ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), - #[clippy::version = "1.86.0"] - ("clippy::double_neg", "double_negations"), - #[clippy::version = ""] - ("clippy::drop_bounds", "drop_bounds"), - #[clippy::version = ""] - ("clippy::drop_copy", "dropping_copy_types"), - #[clippy::version = ""] - ("clippy::drop_ref", "dropping_references"), - #[clippy::version = ""] - ("clippy::fn_null_check", "useless_ptr_null_checks"), - #[clippy::version = ""] - ("clippy::for_loop_over_option", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::for_loop_over_result", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::forget_copy", "forgetting_copy_types"), - #[clippy::version = ""] - ("clippy::forget_ref", "forgetting_references"), - #[clippy::version = ""] - ("clippy::into_iter_on_array", "array_into_iter"), - #[clippy::version = ""] - ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), - #[clippy::version = ""] - ("clippy::invalid_ref", "invalid_value"), - #[clippy::version = ""] - ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), - #[clippy::version = ""] - ("clippy::let_underscore_drop", "let_underscore_drop"), - #[clippy::version = "1.80.0"] - ("clippy::maybe_misused_cfg", "unexpected_cfgs"), - #[clippy::version = ""] - ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), - #[clippy::version = "1.80.0"] - ("clippy::mismatched_target_os", "unexpected_cfgs"), - #[clippy::version = ""] - ("clippy::panic_params", "non_fmt_panics"), - #[clippy::version = ""] - ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), - #[clippy::version = ""] - ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_float_to_int", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_char", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_float", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), #[clippy::version = ""] ("clippy::undropped_manually_drops", "undropped_manually_drops"), #[clippy::version = ""] @@ -183,15 +189,9 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::unused_label", "unused_labels"), #[clippy::version = ""] + ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), + #[clippy::version = ""] ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), #[clippy::version = ""] - ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), - #[clippy::version = "1.88.0"] - ("clippy::transmute_int_to_float", "unnecessary_transmutes"), - #[clippy::version = "1.88.0"] - ("clippy::transmute_int_to_char", "unnecessary_transmutes"), - #[clippy::version = "1.88.0"] - ("clippy::transmute_float_to_int", "unnecessary_transmutes"), - #[clippy::version = "1.88.0"] - ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), + ("clippy::zero_width_space", "clippy::invisible_characters"), ]} diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 5edb5c235703e..a22a2ee66d258 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -22,6 +22,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -252,13 +253,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } let typeck = cx.typeck_results(); - let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { + let Some((kind, sub_expr, skip_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { report(cx, expr, state, data, typeck); } return; }; + self.skip_expr = skip_expr; match (self.state.take(), kind) { (None, kind) => { @@ -671,42 +673,38 @@ fn try_parse_ref_op<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, -) -> Option<(RefOp, &'tcx Expr<'tcx>)> { - let (is_ufcs, def_id, arg) = match expr.kind { - ExprKind::MethodCall(_, arg, [], _) => (false, typeck.type_dependent_def_id(expr.hir_id)?, arg), +) -> Option<(RefOp, &'tcx Expr<'tcx>, Option)> { + let (call_path_id, def_id, arg) = match expr.kind { + ExprKind::MethodCall(_, arg, [], _) => (None, typeck.type_dependent_def_id(expr.hir_id)?, arg), ExprKind::Call( - Expr { - kind: ExprKind::Path(path), + &Expr { + kind: ExprKind::Path(QPath::Resolved(None, path)), hir_id, .. }, [arg], - ) => (true, typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), + ) => (Some(hir_id), path.res.opt_def_id()?, arg), ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_raw_ptr() => { - return Some((RefOp::Deref, sub_expr)); + return Some((RefOp::Deref, sub_expr, None)); + }, + ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => { + return Some((RefOp::AddrOf(mutability), sub_expr, None)); }, - ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)), _ => return None, }; - if tcx.is_diagnostic_item(sym::deref_method, def_id) { - Some(( - RefOp::Method { - mutbl: Mutability::Not, - is_ufcs, - }, - arg, - )) - } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? { - Some(( - RefOp::Method { - mutbl: Mutability::Mut, - is_ufcs, - }, - arg, - )) - } else { - None - } + let mutbl = match tcx.get_diagnostic_name(def_id) { + Some(sym::deref_method) => Mutability::Not, + Some(sym::deref_mut_method) => Mutability::Mut, + _ => return None, + }; + Some(( + RefOp::Method { + mutbl, + is_ufcs: call_path_id.is_some(), + }, + arg, + call_path_id, + )) } // Checks if the adjustments contains a deref of `ManuallyDrop<_>` @@ -944,7 +942,7 @@ fn report<'tcx>( mutbl, } => { let mut app = Applicability::MachineApplicable; - let (expr_str, _expr_is_macro_call) = + let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); let (_, ref_count) = peel_middle_ty_refs(ty); @@ -968,20 +966,11 @@ fn report<'tcx>( "&" }; - // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's - // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary. - /* - expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < ExprPrecedence::Prefix { + let expr_str = if !expr_is_macro_call && is_ufcs && expr.precedence() < ExprPrecedence::Prefix { Cow::Owned(format!("({expr_str})")) } else { expr_str }; - */ - - // Fix #10850, do not lint if it's `Foo::deref` instead of `foo.deref()`. - if is_ufcs { - return; - } span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 039937e0207b0..9ee32fced8c44 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -113,7 +113,8 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { } // check for `unwrap` and `expect` for both `Option` and `Result` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or_else(|| method_chain_args(expr, &["expect"])) + if let Some(arglists) = + method_chain_args(expr, &[sym::unwrap]).or_else(|| method_chain_args(expr, &[sym::expect])) && let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs() && matches!( get_type_diagnostic_name(cx, receiver_ty), diff --git a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs index a38d6df89f2b9..8c12364883c7e 100644 --- a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs @@ -93,6 +93,7 @@ impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VA impl LateLintPass<'_> for EmptyWithBrackets { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Struct(ident, var_data, _) = &item.kind + && !item.span.from_expansion() && has_brackets(var_data) && let span_after_ident = item.span.with_lo(ident.span.hi()) && has_no_fields(cx, var_data, span_after_ident) diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index a5a4e05b3a6d2..085ee4448a4ee 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, path_def_id}; +use clippy_utils::{is_expn_of, path_def_id, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{ExpnId, sym}; +use rustc_span::ExpnId; declare_clippy_lint! { /// ### What it does @@ -72,9 +72,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { }; // ordering is important here, since `writeln!` uses `write!` internally - let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() { + let calling_macro = if is_expn_of(write_call.span, sym::writeln).is_some() { Some("writeln") - } else if is_expn_of(write_call.span, "write").is_some() { + } else if is_expn_of(write_call.span, sym::write).is_some() { Some("write") } else { None diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 6a217b6182ce7..c0b0fd88d9e16 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -273,7 +273,7 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { // Only lint on inherent methods, not trait methods. if let ImplItemKind::Fn(.., body_id) = item.kind && !item.generics.params.is_empty() - && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, item.owner_id).is_none() && !is_empty_body(cx, body_id) && (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id)) && !item.span.in_external_macro(cx.sess().source_map()) diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index c868b782f43c3..68d0cd19c8a69 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -82,7 +82,7 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl } // check for `unwrap` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]) { let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 012ad8e1a2298..c51267567d01d 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { }, ); } - } else if digits > max as usize && float_str.len() < sym_str.len() { + } else if digits > max as usize && count_digits(&float_str) < count_digits(sym_str) { span_lint_and_then( cx, EXCESSIVE_PRECISION, diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index e653a57196d61..3c7e83b069726 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -294,8 +294,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: && let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) - && let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind - && method_name.as_str() == "sqrt" + && let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = grandparent.kind + && method.name == sym::sqrt && detect_hypot(cx, receiver).is_some() { return; @@ -375,24 +375,10 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { } // check if expression of the form x.powi(2) + y.powi(2) - if let ExprKind::MethodCall( - PathSegment { - ident: lmethod_name, .. - }, - largs_0, - [largs_1, ..], - _, - ) = &add_lhs.kind - && let ExprKind::MethodCall( - PathSegment { - ident: rmethod_name, .. - }, - rargs_0, - [rargs_1, ..], - _, - ) = &add_rhs.kind - && lmethod_name.as_str() == "powi" - && rmethod_name.as_str() == "powi" + if let ExprKind::MethodCall(PathSegment { ident: lmethod, .. }, largs_0, [largs_1, ..], _) = &add_lhs.kind + && let ExprKind::MethodCall(PathSegment { ident: rmethod, .. }, rargs_0, [rargs_1, ..], _) = &add_rhs.kind + && lmethod.name == sym::powi + && rmethod.name == sym::powi && let ecx = ConstEvalCtxt::new(cx) && let Some(lvalue) = ecx.eval(largs_1) && let Some(rvalue) = ecx.eval(rargs_1) @@ -482,8 +468,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { ) = &expr.kind { if let Some(parent) = get_parent_expr(cx, expr) - && let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind - && method_name.as_str() == "sqrt" + && let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = parent.kind + && method.name == sym::sqrt && detect_hypot(cx, receiver).is_some() { return; @@ -623,27 +609,13 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { } fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { - if let ExprKind::MethodCall( - PathSegment { - ident: method_name_a, .. - }, - _, - args_a, - _, - ) = expr_a.kind - && let ExprKind::MethodCall( - PathSegment { - ident: method_name_b, .. - }, - _, - args_b, - _, - ) = expr_b.kind + if let ExprKind::MethodCall(PathSegment { ident: method_a, .. }, _, args_a, _) = expr_a.kind + && let ExprKind::MethodCall(PathSegment { ident: method_b, .. }, _, args_b, _) = expr_b.kind { - return method_name_a.as_str() == method_name_b.as_str() + return method_a.name == method_b.name && args_a.len() == args_b.len() - && (["ln", "log2", "log10"].contains(&method_name_a.as_str()) - || method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])); + && (matches!(method_a.name, sym::ln | sym::log2 | sym::log10) + || method_a.name == sym::log && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])); } false diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index c3e0d5e8b694d..70655838b6af0 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -55,7 +55,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); - } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { + } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id).is_none() { check_must_use_candidate( cx, sig.decl, diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index 07a92a4ed70c6..00ce4cfcc5266 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -55,7 +55,7 @@ pub(super) fn check_impl_item<'tcx>( // Don't lint if method is a trait's implementation, we can't do anything about those if let hir::ImplItemKind::Fn(ref sig, _) = item.kind && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) - && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, item.owner_id).is_none() { if cx.effective_visibilities.is_exported(item.owner_id.def_id) { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 4c17834c3adf9..cab7a9fb70990 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { } let generics_suggestion_span = impl_.generics.span.substitute_dummy({ - let range = (item.span.lo()..target.span().lo()).map_range(cx, |src, range| { + let range = (item.span.lo()..target.span().lo()).map_range(cx, |_, src, range| { Some(src.get(range.clone())?.find("impl")? + 4..range.end) }); if let Some(range) = range { @@ -165,11 +165,12 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { continue; } let generics_suggestion_span = generics.span.substitute_dummy({ - let range = (item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |src, range| { - let (pre, post) = src.get(range.clone())?.split_once("fn")?; - let pos = post.find('(')? + pre.len() + 2; - Some(pos..pos) - }); + let range = + (item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |_, src, range| { + let (pre, post) = src.get(range.clone())?.split_once("fn")?; + let pos = post.find('(')? + pre.len() + 2; + Some(pos..pos) + }); if let Some(range) = range { range.with_ctxt(item.span.ctxt()) } else { diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 989997d69f7c7..0b1cae30ca501 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; +use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local, sym}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -72,7 +72,7 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) - && (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) + && (!expr.span.from_expansion() || is_expn_of(expr.span, sym::if_chain).is_some()) && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) && let found_slices = find_slice_values(cx, let_pat) && !found_slices.is_empty() diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs index 3a28553b55e25..7a751514b6476 100644 --- a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs +++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs @@ -1,13 +1,13 @@ use crate::methods::method_call; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::peel_blocks; +use clippy_utils::{peel_blocks, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -57,7 +57,7 @@ fn index_if_arg_is_boolean(args: &[Expr<'_>], call_span: Span) -> Option { impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(("open", mut receiver, [_arg], _, _)) = method_call(expr) else { + let Some((sym::open, mut receiver, [_arg], _, _)) = method_call(expr) else { return; }; let receiver_ty = cx.typeck_results().expr_ty(receiver); @@ -70,9 +70,9 @@ impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { let mut write = None; while let Some((name, recv, args, _, span)) = method_call(receiver) { - if name == "append" { + if name == sym::append { append = index_if_arg_is_boolean(args, span); - } else if name == "write" { + } else if name == sym::write { write = index_if_arg_is_boolean(args, span); } receiver = recv; diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index c4e10837bf192..bf3eafe09b3d0 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -4,6 +4,7 @@ use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -119,33 +120,33 @@ use self::Heuristic::{All, Always, Any, First}; /// returns an infinite or possibly infinite iterator. The finiteness /// is an upper bound, e.g., some methods can return a possibly /// infinite iterator at worst, e.g., `take_while`. -const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [ - ("zip", 1, All, Infinite), - ("chain", 1, Any, Infinite), - ("cycle", 0, Always, Infinite), - ("map", 1, First, Infinite), - ("by_ref", 0, First, Infinite), - ("cloned", 0, First, Infinite), - ("rev", 0, First, Infinite), - ("inspect", 0, First, Infinite), - ("enumerate", 0, First, Infinite), - ("peekable", 1, First, Infinite), - ("fuse", 0, First, Infinite), - ("skip", 1, First, Infinite), - ("skip_while", 0, First, Infinite), - ("filter", 1, First, Infinite), - ("filter_map", 1, First, Infinite), - ("flat_map", 1, First, Infinite), - ("unzip", 0, First, Infinite), - ("take_while", 1, First, MaybeInfinite), - ("scan", 2, First, MaybeInfinite), +const HEURISTICS: [(Symbol, usize, Heuristic, Finiteness); 19] = [ + (sym::zip, 1, All, Infinite), + (sym::chain, 1, Any, Infinite), + (sym::cycle, 0, Always, Infinite), + (sym::map, 1, First, Infinite), + (sym::by_ref, 0, First, Infinite), + (sym::cloned, 0, First, Infinite), + (sym::rev, 0, First, Infinite), + (sym::inspect, 0, First, Infinite), + (sym::enumerate, 0, First, Infinite), + (sym::peekable, 1, First, Infinite), + (sym::fuse, 0, First, Infinite), + (sym::skip, 1, First, Infinite), + (sym::skip_while, 0, First, Infinite), + (sym::filter, 1, First, Infinite), + (sym::filter_map, 1, First, Infinite), + (sym::flat_map, 1, First, Infinite), + (sym::unzip, 0, First, Infinite), + (sym::take_while, 1, First, MaybeInfinite), + (sym::scan, 2, First, MaybeInfinite), ]; fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { for &(name, len, heuristic, cap) in &HEURISTICS { - if method.ident.name.as_str() == name && args.len() == len { + if method.ident.name == name && args.len() == len { return (match heuristic { Always => Infinite, First => is_infinite(cx, receiver), @@ -183,36 +184,36 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { /// the names and argument lengths of methods that *may* exhaust their /// iterators -const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [ - ("find", 1), - ("rfind", 1), - ("position", 1), - ("rposition", 1), - ("any", 1), - ("all", 1), +const POSSIBLY_COMPLETING_METHODS: [(Symbol, usize); 6] = [ + (sym::find, 1), + (sym::rfind, 1), + (sym::position, 1), + (sym::rposition, 1), + (sym::any, 1), + (sym::all, 1), ]; /// the names and argument lengths of methods that *always* exhaust /// their iterators -const COMPLETING_METHODS: [(&str, usize); 12] = [ - ("count", 0), - ("fold", 2), - ("for_each", 1), - ("partition", 1), - ("max", 0), - ("max_by", 1), - ("max_by_key", 1), - ("min", 0), - ("min_by", 1), - ("min_by_key", 1), - ("sum", 0), - ("product", 0), +const COMPLETING_METHODS: [(Symbol, usize); 12] = [ + (sym::count, 0), + (sym::fold, 2), + (sym::for_each, 1), + (sym::partition, 1), + (sym::max, 0), + (sym::max_by, 1), + (sym::max_by_key, 1), + (sym::min, 0), + (sym::min_by, 1), + (sym::min_by_key, 1), + (sym::sum, 0), + (sym::product, 0), ]; fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { - let method_str = method.ident.name.as_str(); + let method_str = method.ident.name; for &(name, len) in &COMPLETING_METHODS { if method_str == name && args.len() == len { return is_infinite(cx, receiver); diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index 1d582fb0223e1..7f2e25367a6a4 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -106,7 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { // Check if return type is String && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String) // Filters instances of to_string which are required by a trait - && trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, impl_item.owner_id).is_none() { show_lint(cx, impl_item); } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index ff028713bf049..92eb3d7a7c59d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -393,6 +393,7 @@ mod unwrap; mod unwrap_in_result; mod upper_case_acronyms; mod use_self; +mod useless_concat; mod useless_conversion; mod vec; mod vec_init_then_push; @@ -866,7 +867,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); store.register_late_pass(move |_| Box::new(lines_filter_map_ok::LinesFilterMapOk::new(conf))); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); - store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); + store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))); store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); @@ -937,6 +938,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_early_pass(|| Box::new(empty_line_after::EmptyLineAfter::new())); store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); + store.register_late_pass(|_| Box::new(useless_concat::UselessConcat)); store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 9a64226b1ed96..8fe0c9d60f961 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -159,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { if let ImplItemKind::Fn(ref sig, id) = item.kind { - let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none(); + let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id).is_none(); check_fn_inner( cx, sig, diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs index d8af44233d3ee..14ccb6fce2239 100644 --- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs +++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs @@ -2,12 +2,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id}; +use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id, sym}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::Symbol; pub struct LinesFilterMapOk { msrv: Msrv, @@ -74,17 +74,17 @@ impl LateLintPass<'_> for LinesFilterMapOk { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind && is_trait_method(cx, expr, sym::Iterator) - && let fm_method_str = fm_method.ident.as_str() - && matches!(fm_method_str, "filter_map" | "flat_map" | "flatten") + && let fm_method_name = fm_method.ident.name + && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines) - && should_lint(cx, fm_args, fm_method_str) + && should_lint(cx, fm_args, fm_method_name) && self.msrv.meets(cx, msrvs::MAP_WHILE) { span_lint_and_then( cx, LINES_FILTER_MAP_OK, fm_span, - format!("`{fm_method_str}()` will run forever if the iterator repeatedly produces an `Err`",), + format!("`{fm_method_name}()` will run forever if the iterator repeatedly produces an `Err`",), |diag| { diag.span_note( fm_receiver.span, @@ -101,9 +101,9 @@ impl LateLintPass<'_> for LinesFilterMapOk { } } -fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> bool { +fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> bool { match args { - [] => method_str == "flatten", + [] => method_name == sym::flatten, [fm_arg] => { match &fm_arg.kind { // Detect `Result::ok` @@ -120,7 +120,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> boo && path_to_local_id(receiver, param.pat.hir_id) && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id) { - is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok" + is_diag_item_method(cx, method_did, sym::Result) && method.ident.name == sym::ok } else { false } diff --git a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 8916454ada16e..a702e60f1c276 100644 --- a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -3,12 +3,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{eq_expr_value, higher, path_to_local_id}; +use clippy_utils::{eq_expr_value, higher, path_to_local_id, sym}; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{Expr, ExprKind, LangItem, Node, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use super::CHAR_INDICES_AS_BYTE_INDICES; @@ -16,22 +16,22 @@ use super::CHAR_INDICES_AS_BYTE_INDICES; // Note: `String` also has methods that work with byte indices, // but they all take `&mut self` and aren't worth considering since the user couldn't have called // them while the chars iterator is live anyway. -const BYTE_INDEX_METHODS: &[&str] = &[ - "is_char_boundary", - "floor_char_boundary", - "ceil_char_boundary", - "get", - "index", - "index_mut", - "get_mut", - "get_unchecked", - "get_unchecked_mut", - "slice_unchecked", - "slice_mut_unchecked", - "split_at", - "split_at_mut", - "split_at_checked", - "split_at_mut_checked", +const BYTE_INDEX_METHODS: &[Symbol] = &[ + sym::ceil_char_boundary, + sym::floor_char_boundary, + sym::get, + sym::get_mut, + sym::get_unchecked, + sym::get_unchecked_mut, + sym::index, + sym::index_mut, + sym::is_char_boundary, + sym::slice_mut_unchecked, + sym::slice_unchecked, + sym::split_at, + sym::split_at_checked, + sym::split_at_mut, + sym::split_at_mut_checked, ]; const CONTINUE: ControlFlow = ControlFlow::Continue(()); @@ -88,7 +88,7 @@ fn check_index_usage<'tcx>( // (contrary to the `ExprKind::Index` case which needs to handle both with `is_string_like` because `String` implements // `Index` directly and no deref to `str` would happen in that case). if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_str() - && BYTE_INDEX_METHODS.contains(&segment.ident.name.as_str()) + && BYTE_INDEX_METHODS.contains(&segment.ident.name) && eq_expr_value(cx, chars_recv, recv) => { "passing a character position to a method that expects a byte index" diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs index d5ddc33e928db..4aa1c2e211d33 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,7 +1,7 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -76,7 +76,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr< }; let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); + let object = snippet_with_context(cx, self_arg.span, call_expr.span.ctxt(), "_", &mut applicability).0; span_lint_and_sugg( cx, EXPLICIT_INTO_ITER_LOOP, diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index d0b26c91ffafb..010652e1cb902 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,7 +1,7 @@ use super::EXPLICIT_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sym; use clippy_utils::ty::{ implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, @@ -36,7 +36,7 @@ pub(super) fn check( } let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); + let object = snippet_with_context(cx, self_arg.span, call_expr.span.ctxt(), "_", &mut applicability).0; span_lint_and_sugg( cx, EXPLICIT_ITER_LOOP, diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 56d2bef2305a7..01c36b8cb12fc 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -25,8 +25,8 @@ mod while_let_loop; mod while_let_on_iterator; use clippy_config::Conf; -use clippy_utils::higher; use clippy_utils::msrvs::Msrv; +use clippy_utils::{higher, sym}; use rustc_ast::Label; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; @@ -909,15 +909,17 @@ impl Loops { } fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { - if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { - match method.ident.as_str() { - "iter" | "iter_mut" => { + if !arg.span.from_expansion() + && let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind + { + match method.ident.name { + sym::iter | sym::iter_mut => { explicit_iter_loop::check(cx, self_arg, arg, self.msrv, self.enforce_iter_loop_reborrow); }, - "into_iter" => { + sym::into_iter => { explicit_into_iter_loop::check(cx, self_arg, arg); }, - "next" => { + sym::next => { iter_next_loop::check(cx, arg); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs index b4cd988329d32..4439a28763a2f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; -use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal}; +use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -103,7 +103,7 @@ fn count_ones_receiver<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Optio } else { return None; }; - (method.ident.as_str() == "count_ones" && matches!(ty.kind(), ty::Uint(_))).then_some(receiver) + (method.ident.name == sym::count_ones && matches!(ty.kind(), ty::Uint(_))).then_some(receiver) } /// Return `greater` if `smaller == greater - 1` diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs index 2dad0fa4925e7..0c09a47c96518 100644 --- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs +++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs @@ -1,11 +1,13 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::{expr_or_init, is_in_const_context, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; declare_clippy_lint! { @@ -36,20 +38,33 @@ declare_clippy_lint! { complexity, "manual slice size calculation" } -declare_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]); +impl_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]); + +pub struct ManualSliceSizeCalculation { + msrv: Msrv, +} + +impl ManualSliceSizeCalculation { + pub fn new(conf: &Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Binary(ref op, left, right) = expr.kind && BinOpKind::Mul == op.node && !expr.span.from_expansion() - // Does not apply inside const because size_of_val is not cost in stable. - && !is_in_const_context(cx) && let Some((receiver, refs_count)) = simplify(cx, left, right) + && (!is_in_const_context(cx) || self.msrv.meets(cx, msrvs::CONST_SIZE_OF_VAL)) { let ctxt = expr.span.ctxt(); let mut app = Applicability::MachineApplicable; - let deref = "*".repeat(refs_count - 1); + let deref = if refs_count > 0 { + "*".repeat(refs_count - 1) + } else { + "&".into() + }; let val_name = snippet_with_context(cx, receiver.span, ctxt, "slice", &mut app).0; let Some(sugg) = std_or_core(cx) else { return }; diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index b607f8117eb89..af6a1b07a4929 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -255,7 +255,7 @@ impl LateLintPass<'_> for MapUnit { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { if let hir::StmtKind::Semi(expr) = stmt.kind && !stmt.span.from_expansion() - && let Some(arglists) = method_chain_args(expr, &["map"]) + && let Some(arglists) = method_chain_args(expr, &[sym::map]) { lint_map_unit_fn(cx, stmt, expr, arglists[0]); } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index a21597ffb93d7..dbb29ee776b18 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -1,8 +1,9 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{SpanlessEq, SpanlessHash, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::{SpanlessEq, SpanlessHash, fulfill_or_allowed, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; +use itertools::Itertools; use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -110,57 +111,68 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { && check_same_body() }; - let mut appl = Applicability::MaybeIncorrect; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); - for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) { - if matches!(arm2.pat.kind, PatKind::Wild) { - if !cx.tcx.features().non_exhaustive_omitted_patterns_lint() - || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id) - { - let arm_span = adjusted_arm_span(cx, arm1.span); - span_lint_hir_and_then( - cx, - MATCH_SAME_ARMS, - arm1.hir_id, - arm_span, - "this match arm has an identical body to the `_` wildcard arm", - |diag| { - diag.span_suggestion(arm_span, "try removing the arm", "", appl) - .help("or try changing either arm body") - .span_note(arm2.span, "`_` wildcard arm here"); - }, - ); - } - } else { - let back_block = backwards_blocking_idxs[j]; - let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) { - (arm1, arm2) - } else { - (arm2, arm1) - }; - - span_lint_hir_and_then( - cx, - MATCH_SAME_ARMS, - keep_arm.hir_id, - keep_arm.span, - "this match arm has an identical body to another arm", - |diag| { - let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "", &mut appl); - let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "", &mut appl); + for mut group in search_same(&indexed_arms, hash, eq) { + // Filter out (and fulfill) `#[allow]`ed and `#[expect]`ed arms + group.retain(|(_, arm)| !fulfill_or_allowed(cx, MATCH_SAME_ARMS, [arm.hir_id])); - diag.multipart_suggestion( - "or try merging the arm patterns and removing the obsolete arm", - vec![ - (keep_arm.pat.span, format!("{keep_pat_snip} | {move_pat_snip}")), - (adjusted_arm_span(cx, move_arm.span), String::new()), - ], - appl, - ) - .help("try changing either arm body"); - }, - ); + if group.len() < 2 { + continue; } + + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + group.iter().map(|(_, arm)| arm.span).collect_vec(), + "these match arms have identical bodies", + |diag| { + diag.help("if this is unintentional make the arms return different values"); + + if let [prev @ .., (_, last)] = group.as_slice() + && is_wildcard_arm(last.pat) + && is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, last.hir_id) + { + diag.span_label(last.span, "the wildcard arm"); + + let s = if prev.len() > 1 { "s" } else { "" }; + diag.multipart_suggestion_verbose( + format!("otherwise remove the non-wildcard arm{s}"), + prev.iter() + .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .collect(), + Applicability::MaybeIncorrect, + ); + } else if let &[&(first_idx, _), .., &(last_idx, _)] = group.as_slice() { + let back_block = backwards_blocking_idxs[last_idx]; + let split = if back_block < first_idx + || (back_block == 0 && forwards_blocking_idxs[first_idx] <= last_idx) + { + group.split_first() + } else { + group.split_last() + }; + + if let Some(((_, dest), src)) = split + && let Some(pat_snippets) = group + .iter() + .map(|(_, arm)| arm.pat.span.get_source_text(cx)) + .collect::>>() + { + let mut suggs = src + .iter() + .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .collect_vec(); + + suggs.push((dest.pat.span, pat_snippets.iter().join(" | "))); + diag.multipart_suggestion_verbose( + "otherwise merge the patterns into a single arm", + suggs, + Applicability::MaybeIncorrect, + ); + } + } + }, + ); } } @@ -450,3 +462,11 @@ fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool { pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.swap_remove(&id)); result && ids.is_empty() } + +fn is_wildcard_arm(pat: &Pat<'_>) -> bool { + match pat.kind { + PatKind::Wild => true, + PatKind::Or([.., last]) => matches!(last.kind, PatKind::Wild), + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index c6ebd6144c76f..c128fc40b7334 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -28,7 +28,7 @@ use clippy_config::Conf; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::walk_span_to_context; use clippy_utils::{ - higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, + higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, sym, }; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -1053,13 +1053,13 @@ impl_lint_pass!(Matches => [ impl<'tcx> LateLintPass<'tcx> for Matches { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_direct_expn_of(expr.span, "matches").is_none() && expr.span.in_external_macro(cx.sess().source_map()) { + if is_direct_expn_of(expr.span, sym::matches).is_none() && expr.span.in_external_macro(cx.sess().source_map()) { return; } let from_expansion = expr.span.from_expansion(); if let ExprKind::Match(ex, arms, source) = expr.kind { - if is_direct_expn_of(expr.span, "matches").is_some() + if is_direct_expn_of(expr.span, sym::matches).is_some() && let [arm, _] = arms { redundant_pattern_match::check_match(cx, expr, ex, arms); diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 6c5d7cab2036e..b04db03f8d2e7 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -74,7 +74,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) } if let PatKind::Wild = arm.pat.kind { - if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) { + if !eq_expr_value(cx, match_expr, arm_expr) { return false; } } else if !pat_same_as_expr(arm.pat, arm_expr) { @@ -103,27 +103,18 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool if matches!(else_expr.kind, ExprKind::Block(..)) { return false; } - let ret = strip_return(else_expr); let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { - return is_res_lang_ctor(cx, path_res(cx, ret), OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); + return is_res_lang_ctor(cx, path_res(cx, else_expr), OptionNone) + || eq_expr_value(cx, if_let.let_expr, else_expr); } - return eq_expr_value(cx, if_let.let_expr, ret); + return eq_expr_value(cx, if_let.let_expr, else_expr); } } false } -/// Strip `return` keyword if the expression type is `ExprKind::Ret`. -fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { - if let ExprKind::Ret(Some(ret)) = expr.kind { - ret - } else { - expr - } -} - /// Manually check for coercion casting by checking if the type of the match operand or let expr /// differs with the assigned local variable or the function return type. fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { @@ -161,7 +152,6 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> } fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { - let expr = strip_return(expr); match (&pat.kind, &expr.kind) { // Example: `Some(val) => Some(val)` (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => { diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index 9bbef8da0a466..7c6d45e424006 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -3,14 +3,14 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; -use clippy_utils::{is_in_const_context, path_to_local}; +use clippy_utils::{is_in_const_context, path_to_local, sym}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_span::symbol::Ident; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -95,7 +95,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: } else if let ExprKind::MethodCall(path, recv, args, ..) = guard.kind && let Some(binding) = get_pat_binding(cx, recv, outer_arm) { - check_method_calls(cx, outer_arm, path.ident.name.as_str(), recv, args, guard, &binding); + check_method_calls(cx, outer_arm, path.ident.name, recv, args, guard, &binding); } } } @@ -103,7 +103,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: fn check_method_calls<'tcx>( cx: &LateContext<'tcx>, arm: &Arm<'tcx>, - method: &str, + method: Symbol, recv: &Expr<'_>, args: &[Expr<'_>], if_expr: &Expr<'_>, @@ -112,7 +112,7 @@ fn check_method_calls<'tcx>( let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let slice_like = ty.is_slice() || ty.is_array(); - let sugg = if method == "is_empty" { + let sugg = if method == sym::is_empty { // `s if s.is_empty()` becomes "" // `arr if arr.is_empty()` becomes [] @@ -137,9 +137,9 @@ fn check_method_calls<'tcx>( if needles.is_empty() { sugg.insert_str(1, ".."); - } else if method == "starts_with" { + } else if method == sym::starts_with { sugg.insert_str(sugg.len() - 1, ", .."); - } else if method == "ends_with" { + } else if method == sym::ends_with { sugg.insert_str(1, ".., "); } else { return; diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index db20be40f27ea..aa9be61bf4d43 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -273,7 +273,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); if let Some((good_method, maybe_guard)) = found_good_method(cx, arms, node_pair) { - let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span)); + let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); let result_expr = match &op.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index d7dc7604088f7..0f3ad40784d36 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -4,7 +4,7 @@ use crate::FxHashSet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{first_line_of_span, indent_of, snippet}; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; -use clippy_utils::{get_attr, is_lint_allowed}; +use clippy_utils::{get_attr, is_lint_allowed, sym}; use itertools::Itertools; use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexSet; @@ -186,7 +186,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { && get_attr( self.cx.sess(), self.cx.tcx.get_attrs_unchecked(adt.did()), - "has_significant_drop", + sym::has_significant_drop, ) .count() > 0 diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index de22514c37c66..02fc09170e59c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; @@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E if let Some(parent) = clippy_utils::get_parent_expr(cx, expr) && let Some((name, _, _, _, _)) = method_call(parent) - && name == "unwrap" + && name == sym::unwrap { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs index 4ae0aeea2d1c5..de27a45ba4d92 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs @@ -5,12 +5,13 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; use rustc_middle::ty; +use rustc_span::Symbol; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. pub(super) fn check( cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>, - chain_methods: &[&str], + chain_methods: &[Symbol], lint: &'static Lint, suggest: &str, ) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index 9c45ec2e56cbe..1c72a973cfa13 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -5,12 +5,13 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; +use rustc_span::Symbol; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. pub(super) fn check( cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>, - chain_methods: &[&str], + chain_methods: &[Symbol], lint: &'static Lint, suggest: &str, ) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs index 2efff4c3c5497..8729e91d191f8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs @@ -1,13 +1,14 @@ use crate::methods::chars_cmp; +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { + if chars_cmp::check(cx, info, &[sym::chars, sym::last], CHARS_LAST_CMP, "ends_with") { true } else { - chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") + chars_cmp::check(cx, info, &[sym::chars, sym::next_back], CHARS_LAST_CMP, "ends_with") } } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs index 5b8713f7d7903..027d0a3947bfb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs @@ -1,13 +1,26 @@ use crate::methods::chars_cmp_with_unwrap; +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { + if chars_cmp_with_unwrap::check( + cx, + info, + &[sym::chars, sym::last, sym::unwrap], + CHARS_LAST_CMP, + "ends_with", + ) { true } else { - chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") + chars_cmp_with_unwrap::check( + cx, + info, + &[sym::chars, sym::next_back, sym::unwrap], + CHARS_LAST_CMP, + "ends_with", + ) } } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs index b631fecab9729..2438843bf3ab1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs @@ -1,8 +1,9 @@ +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") + crate::methods::chars_cmp::check(cx, info, &[sym::chars, sym::next], CHARS_NEXT_CMP, "starts_with") } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs index caf21d3ff3bcb..9b3609f19d724 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs @@ -1,8 +1,15 @@ +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") + crate::methods::chars_cmp_with_unwrap::check( + cx, + info, + &[sym::chars, sym::next, sym::unwrap], + CHARS_NEXT_CMP, + "starts_with", + ) } diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs index f7bf8764bdebc..6d0b944df55d6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{eq_expr_value, get_parent_expr}; +use clippy_utils::{eq_expr_value, get_parent_expr, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir as hir; @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( // If the parent node's `to` argument is the same as the `to` argument // of the last replace call in the current chain, don't lint as it was already linted if let Some(parent) = get_parent_expr(cx, expr) - && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent) + && let Some((sym::replace, _, [current_from, current_to], _, _)) = method_call(parent) && eq_expr_value(cx, to, current_to) && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() { @@ -47,7 +47,7 @@ fn collect_replace_calls<'tcx>( let mut from_args = VecDeque::new(); let _: Option<()> = for_each_expr_without_closures(expr, |e| { - if let Some(("replace", _, [from, to], _, _)) = method_call(e) { + if let Some((sym::replace, _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); from_args.push_front(from); diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index f5688e370a478..82e5a6d5a4120 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; use super::EXPECT_FUN_CALL; @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, - name: &str, + name: Symbol, receiver: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], ) { @@ -114,7 +114,7 @@ pub(super) fn check<'tcx>( } } - if args.len() != 1 || name != "expect" || !is_call(&args[0].kind) { + if args.len() != 1 || name != sym::expect || !is_call(&args[0].kind) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs index 460ec7b3640a6..db60061904f6f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use super::EXTEND_WITH_DRAIN; @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: if is_type_diagnostic_item(cx, ty, sym::Vec) //check source object && let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind - && src_method.ident.as_str() == "drain" + && src_method.ident.name == sym::drain && let src_ty = cx.typeck_results().expr_ty(drain_vec) //check if actual src type is mutable for code suggestion && let immutable = src_ty.is_mutable_ptr() diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index 519091406ccf3..9724463f0c084 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -1,15 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs}; +use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::Symbol; use super::IMPLICIT_CLONE; -pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { +pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && is_clone_like(cx, method_name, method_def_id) && let return_type = cx.typeck_results().expr_ty(expr) @@ -43,12 +43,12 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv /// Note that `to_string` is not flagged by `implicit_clone`. So other lints that call /// `is_clone_like` and that do flag `to_string` must handle it separately. See, e.g., /// `is_to_owned_like` in `unnecessary_to_owned.rs`. -pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir::def_id::DefId) -> bool { +pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: hir::def_id::DefId) -> bool { match method_name { - "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr), - "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), - "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), - "to_vec" => cx + sym::to_os_string => is_diag_item_method(cx, method_def_id, sym::OsStr), + sym::to_owned => is_diag_trait_item(cx, method_def_id, sym::ToOwned), + sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), + sym::to_vec => cx .tcx .impl_of_method(method_def_id) .filter(|&impl_did| { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs index 17cc07b91c5da..b4ab313fe98d1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs @@ -5,11 +5,16 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::ITER_CLONED_COLLECT; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + method_name: Symbol, + expr: &hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, +) { let expr_ty = cx.typeck_results().expr_ty(expr); if is_type_diagnostic_item(cx, expr_ty, sym::Vec) && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs index 209cf2fcc0a44..6b64cc8b50ae7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs @@ -5,11 +5,11 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::ITER_COUNT; -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: &str) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: Symbol) { let ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { "slice" diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs index 3ac9299ba9157..c88462129af02 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -1,12 +1,12 @@ use super::ITER_KV_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::pat_is_wild; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{pat_is_wild, sym}; use rustc_hir::{Body, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::Symbol; /// lint use of: /// @@ -16,13 +16,13 @@ use rustc_span::sym; /// on `HashMaps` and `BTreeMaps` in std pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - map_type: &'tcx str, // iter / into_iter + map_type: Symbol, // iter / into_iter expr: &'tcx Expr<'tcx>, // .iter().map(|(_, v_| v)) recv: &'tcx Expr<'tcx>, // hashmap m_arg: &'tcx Expr<'tcx>, // |(_, v)| v msrv: Msrv, ) { - if map_type == "into_iter" && !msrv.meets(cx, msrvs::INTO_KEYS) { + if map_type == sym::into_iter && !msrv.meets(cx, msrvs::INTO_KEYS) { return; } if !expr.span.from_expansion() @@ -42,7 +42,7 @@ pub(super) fn check<'tcx>( { let mut applicability = rustc_errors::Applicability::MachineApplicable; let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); - let into_prefix = if map_type == "into_iter" { "into_" } else { "" }; + let into_prefix = if map_type == sym::into_iter { "into_" } else { "" }; if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind && let [local_ident] = path.segments diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs index 82bda5d951225..1fdbd81bf240e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sym; use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use super::ITER_NTH; @@ -12,7 +12,7 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_recv: &'tcx hir::Expr<'tcx>, - iter_method: &str, + iter_method: Symbol, iter_span: Span, nth_span: Span, ) -> bool { @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( expr.span, format!("called `.{iter_method}().nth()` on a {caller_type}"), |diag| { - let get_method = if iter_method == "iter_mut" { "get_mut" } else { "get" }; + let get_method = if iter_method == sym::iter_mut { "get_mut" } else { "get" }; diag.span_suggestion_verbose( iter_span.to(nth_span), format!("`{get_method}` is equivalent but more concise"), diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 9d562f5e51d2c..c0366765234f9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -2,7 +2,7 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core}; +use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -10,6 +10,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirId; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; +use rustc_span::Symbol; use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS}; @@ -51,7 +52,7 @@ fn is_arg_ty_unified_in_fn<'tcx>( .any(|(i, ty)| i != arg_id_in_args && ty.skip_binder().walk().any(|arg| arg.as_type() == Some(arg_ty_in_args))) } -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: &str, recv: &'tcx Expr<'tcx>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, recv: &'tcx Expr<'tcx>) { let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), @@ -60,9 +61,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method _ => return, }; let iter_type = match method_name { - "iter" => IterType::Iter, - "iter_mut" => IterType::IterMut, - "into_iter" => IterType::IntoIter, + sym::iter => IterType::Iter, + sym::iter_mut => IterType::IterMut, + sym::into_iter => IterType::IntoIter, _ => return, }; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index 7bb625222ec0f..f5fe4316eb0d3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -8,7 +8,7 @@ use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, Pl use rustc_lint::LateContext; use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::ITER_OVEREAGER_CLONED; use crate::redundant_clone::REDUNDANT_CLONE; @@ -26,7 +26,7 @@ pub(super) enum Op<'a> { // later `.cloned()` // and add `&` to the parameter of closure parameter // e.g. `find` `filter` - FixClosure(&'a str, &'a Expr<'a>), + FixClosure(Symbol, &'a Expr<'a>), // later `.cloned()` // e.g. `skip` `take` diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs index 3fa83cd39d1d2..a8445b68dd608 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs @@ -168,7 +168,7 @@ fn rewrite_as_cstr(cx: &LateContext<'_>, span: Span) -> Option { fn get_cast_target<'tcx>(e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { match &e.kind { - ExprKind::MethodCall(method, receiver, [], _) if method.ident.as_str() == "cast" => Some(receiver), + ExprKind::MethodCall(method, receiver, [], _) if method.ident.name == sym::cast => Some(receiver), ExprKind::Cast(expr, _) => Some(expr), _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs index 173ebcb7020b7..21f2ce8b7c900 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs @@ -3,18 +3,18 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_span::{DUMMY_SP, Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use super::MANUAL_INSPECT; #[expect(clippy::too_many_lines)] -pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: &str, name_span: Span, msrv: Msrv) { +pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: Symbol, name_span: Span, msrv: Msrv) { if let ExprKind::Closure(c) = arg.kind && matches!(c.kind, ClosureKind::Closure) && let typeck = cx.typeck_results() @@ -101,7 +101,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: UseKind::Return(s) => edits.push((s.with_leading_whitespace(cx).with_ctxt(s.ctxt()), String::new())), UseKind::Borrowed(s) => { #[expect(clippy::range_plus_one)] - let range = s.map_range(cx, |src, range| { + let range = s.map_range(cx, |_, src, range| { let src = src.get(range.clone())?; let trimmed = src.trim_start_matches([' ', '\t', '\n', '\r', '(']); trimmed.starts_with('&').then(|| { @@ -168,8 +168,8 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: edits.extend(addr_of_edits); } let edit = match name { - "map" => "inspect", - "map_err" => "inspect_err", + sym::map => "inspect", + sym::map_err => "inspect_err", _ => return, }; edits.push((name_span, edit.to_string())); diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 0536014465731..98def66ca1499 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -5,7 +5,7 @@ use rustc_ast::BindingMode; use rustc_errors::Applicability; use rustc_hir::{self as hir, Node, PatKind}; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use super::MAP_IDENTITY; @@ -14,7 +14,7 @@ pub(super) fn check( expr: &hir::Expr<'_>, caller: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, - name: &str, + name: Symbol, _map_span: Span, ) { let caller_ty = cx.typeck_results().expr_ty(caller); diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index e0e6a1a59b623..d2d59f0013c0c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -150,7 +150,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; @@ -159,7 +159,7 @@ use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, kw}; declare_clippy_lint! { /// ### What it does @@ -4711,17 +4711,15 @@ impl_lint_pass!(Methods => [ /// Extracts a method call name, args, and `Span` of the method name. /// This ensures that neither the receiver nor any of the arguments /// come from expansion. -pub fn method_call<'tcx>( - recv: &'tcx Expr<'tcx>, -) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { +pub fn method_call<'tcx>(recv: &'tcx Expr<'tcx>) -> Option<(Symbol, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind && !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { - let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span, call_span)); + Some((path.ident.name, receiver, args, path.ident.span, call_span)) + } else { + None } - None } impl<'tcx> LateLintPass<'tcx> for Methods { @@ -4743,13 +4741,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); + or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args); expect_fun_call::check( cx, &self.format_args, expr, method_span, - method_call.ident.as_str(), + method_call.ident.name, receiver, args, ); @@ -4778,7 +4776,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; } - let name = impl_item.ident.name.as_str(); + let name = impl_item.ident.name; let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir_expect_item(parent); let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); @@ -4851,7 +4849,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - if name == "new" && ret_ty != self_ty { + if name == sym::new && ret_ty != self_ty { span_lint( cx, NEW_RET_NO_SELF, @@ -4881,7 +4879,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); wrong_self_convention::check( cx, - item.ident.name.as_str(), + item.ident.name, self_ty, first_arg_ty, first_arg_hir_ty.span, @@ -4912,14 +4910,17 @@ impl Methods { // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { - ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { + ( + sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub, + [_arg], + ) => { zst_offset::check(cx, expr, recv); }, - ("all", [arg]) => { + (sym::all, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, true); match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => { + Some((sym::cloned, recv2, [], _, _)) => { iter_overeager_cloned::check( cx, expr, @@ -4929,13 +4930,13 @@ impl Methods { false, ); }, - Some(("map", _, [map_arg], _, map_call_span)) => { + Some((sym::map, _, [map_arg], _, map_call_span)) => { map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "all"); }, _ => {}, } }, - ("and_then", [arg]) => { + (sym::and_then, [arg]) => { let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { @@ -4945,11 +4946,11 @@ impl Methods { } } }, - ("any", [arg]) => { + (sym::any, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, false); match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -4957,80 +4958,79 @@ impl Methods { iter_overeager_cloned::Op::NeedlessMove(arg), false, ), - Some(("chars", recv, _, _, _)) + Some((sym::chars, recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind && let body = cx.tcx.hir_body(arg.body) && let [param] = body.params => { string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), self.msrv); }, - Some(("map", _, [map_arg], _, map_call_span)) => { + Some((sym::map, _, [map_arg], _, map_call_span)) => { map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any"); }, - Some(("iter", iter_recv, ..)) => { + Some((sym::iter, iter_recv, ..)) => { manual_contains::check(cx, expr, iter_recv, arg); }, _ => {}, } }, - ("arg", [arg]) => { + (sym::arg, [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); }, - ("as_deref" | "as_deref_mut", []) => { + (sym::as_deref | sym::as_deref_mut, []) => { needless_option_as_deref::check(cx, expr, recv, name); }, - ("as_bytes", []) => { - if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { + (sym::as_bytes, []) => { + if let Some((sym::as_str, recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); } sliced_string_as_bytes::check(cx, expr, recv); }, - ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), - ("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, self.msrv), - ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), - ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("bytes", []) => unbuffered_bytes::check(cx, expr, recv), - ("cloned", []) => { + (sym::as_mut | sym::as_ref, []) => useless_asref::check(cx, expr, name, recv), + (sym::as_ptr, []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, self.msrv), + (sym::assume_init, []) => uninit_assumed_init::check(cx, expr, recv), + (sym::bytes, []) => unbuffered_bytes::check(cx, expr, recv), + (sym::cloned, []) => { cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv); option_as_ref_cloned::check(cx, recv, span); }, - ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { + (sym::collect, []) if is_trait_method(cx, expr, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { - Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => { + Some((name @ (sym::cloned | sym::copied), recv2, [], _, _)) => { iter_cloned_collect::check(cx, name, expr, recv2); }, - Some(("map", m_recv, [m_arg], m_ident_span, _)) => { + Some((sym::map, m_recv, [m_arg], m_ident_span, _)) => { map_collect_result_unit::check(cx, expr, m_recv, m_arg); format_collect::check(cx, expr, m_arg, m_ident_span); }, - Some(("take", take_self_arg, [take_arg], _, _)) => { + Some((sym::take, take_self_arg, [take_arg], _, _)) => { if self.msrv.meets(cx, msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, - Some(("drain", recv, args, ..)) => { + Some((sym::drain, recv, args, ..)) => { drain_collect::check(cx, args, expr, recv); }, _ => {}, } }, - ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => { + (sym::count, []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { + Some((sym::cloned, recv2, [], _, _)) => { iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false); }, - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => { + Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { iter_count::check(cx, expr, recv2, name2); }, - Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), - Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), - Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, - ("min" | "max", [arg]) => { + (sym::min | sym::max, [arg]) => { unnecessary_min_or_max::check(cx, expr, name, recv, arg); }, - ("drain", ..) => { + (sym::drain, ..) => { if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.parent_hir_node(expr.hir_id) && matches!(kind, StmtKind::Semi(_)) && args.len() <= 1 @@ -5040,31 +5040,31 @@ impl Methods { iter_with_drain::check(cx, expr, recv, span, arg); } }, - ("ends_with", [arg]) => { + (sym::ends_with, [arg]) => { if let ExprKind::MethodCall(.., span) = expr.kind { case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg, self.msrv); } path_ends_with_ext::check(cx, recv, arg, expr, self.msrv, &self.allowed_dotfiles); }, - ("expect", [_]) => { + (sym::expect, [_]) => { match method_call(recv) { - Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span, _)) => { + Some((sym::ok, recv, [], _, _)) => ok_expect::check(cx, expr, recv), + Some((sym::err, recv, [], err_span, _)) => { err_expect::check(cx, expr, recv, span, err_span, self.msrv); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("expect_err", [_]) | ("unwrap_err" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => { + (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_unchecked | sym::unwrap_err_unchecked, []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("extend", [arg]) => { + (sym::extend, [arg]) => { string_extend_chars::check(cx, expr, recv, arg); extend_with_drain::check(cx, expr, recv, arg); }, - ("filter", [arg]) => { - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + (sym::filter, [arg]) => { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { // if `arg` has side-effect, the semantic will change iter_overeager_cloned::check( cx, @@ -5080,8 +5080,8 @@ impl Methods { iter_filter::check(cx, expr, arg, span); } }, - ("find", [arg]) => { - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + (sym::find, [arg]) => { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { // if `arg` has side-effect, the semantic will change iter_overeager_cloned::check( cx, @@ -5093,26 +5093,26 @@ impl Methods { ); } }, - ("filter_map", [arg]) => { + (sym::filter_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); }, - ("find_map", [arg]) => { + (sym::find_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); }, - ("flat_map", [arg]) => { + (sym::flat_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); }, - ("flatten", []) => match method_call(recv) { - Some(("map", recv, [map_arg], map_span, _)) => { + (sym::flatten, []) => match method_call(recv) { + Some((sym::map, recv, [map_arg], map_span, _)) => { map_flatten::check(cx, expr, recv, map_arg, map_span); }, - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5122,15 +5122,15 @@ impl Methods { ), _ => {}, }, - ("fold", [init, acc]) => { + (sym::fold, [init, acc]) => { manual_try_fold::check(cx, expr, init, acc, call_span, self.msrv); unnecessary_fold::check(cx, expr, init, acc, span); }, - ("for_each", [arg]) => { + (sym::for_each, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); match method_call(recv) { - Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5141,44 +5141,44 @@ impl Methods { _ => {}, } }, - ("get", [arg]) => { + (sym::get, [arg]) => { get_first::check(cx, expr, recv, arg); get_last_with_len::check(cx, expr, recv, arg); }, - ("get_or_insert_with", [arg]) => { + (sym::get_or_insert_with, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"); }, - ("hash", [arg]) => { + (sym::hash, [arg]) => { unit_hash::check(cx, expr, recv, arg); }, - ("is_empty", []) => { + (sym::is_empty, []) => { match method_call(recv) { - Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => { - needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span); + Some((prev_method @ (sym::as_bytes | sym::bytes), prev_recv, [], _, _)) => { + needless_as_bytes::check(cx, prev_method, name, prev_recv, expr.span); }, - Some(("as_str", recv, [], as_str_span, _)) => { + Some((sym::as_str, recv, [], as_str_span, _)) => { redundant_as_str::check(cx, expr, recv, as_str_span, span); }, _ => {}, } is_empty::check(cx, expr, recv); }, - ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), - ("is_none", []) => check_is_some_is_none(cx, expr, recv, call_span, false), - ("is_some", []) => check_is_some_is_none(cx, expr, recv, call_span, true), - ("iter" | "iter_mut" | "into_iter", []) => { + (sym::is_file, []) => filetype_is_file::check(cx, expr, recv), + (sym::is_digit, [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), + (sym::is_none, []) => check_is_some_is_none(cx, expr, recv, call_span, false), + (sym::is_some, []) => check_is_some_is_none(cx, expr, recv, call_span, true), + (sym::iter | sym::iter_mut | sym::into_iter, []) => { iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, - ("join", [join_arg]) => { - if let Some(("collect", _, _, span, _)) = method_call(recv) { + (sym::join, [join_arg]) => { + if let Some((sym::collect, _, _, span, _)) = method_call(recv) { unnecessary_join::check(cx, expr, recv, join_arg, span); } else { join_absolute_paths::check(cx, recv, join_arg, expr.span); } }, - ("last", []) => { - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + (sym::last, []) => { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, expr, @@ -5190,24 +5190,24 @@ impl Methods { } double_ended_iterator_last::check(cx, expr, recv, call_span); }, - ("len", []) => { - if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) { - needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span); + (sym::len, []) => { + if let Some((prev_method @ (sym::as_bytes | sym::bytes), prev_recv, [], _, _)) = method_call(recv) { + needless_as_bytes::check(cx, prev_method, sym::len, prev_recv, expr.span); } }, - ("lock", []) => { + (sym::lock, []) => { mut_mutex_lock::check(cx, expr, recv, span); }, - (name @ ("map" | "map_err"), [m_arg]) => { - if name == "map" { + (name @ (sym::map | sym::map_err), [m_arg]) => { + if name == sym::map { unused_enumerate_index::check(cx, expr, recv, m_arg); map_clone::check(cx, expr, recv, m_arg, self.msrv); map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span); match method_call(recv) { - Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => { + Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) => { iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv); }, - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5222,12 +5222,12 @@ impl Methods { } if let Some((name, recv2, args, span2, _)) = method_call(recv) { match (name, args) { - ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), - ("filter", [f_arg]) => { + (sym::as_mut, []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), + (sym::as_ref, []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), + (sym::filter, [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); }, - ("find", [f_arg]) => { + (sym::find, [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true); }, _ => {}, @@ -5237,22 +5237,22 @@ impl Methods { manual_inspect::check(cx, expr, m_arg, name, span, self.msrv); crate::useless_conversion::check_function_application(cx, expr, recv, m_arg); }, - ("map_break" | "map_continue", [m_arg]) => { + (sym::map_break | sym::map_continue, [m_arg]) => { crate::useless_conversion::check_function_application(cx, expr, recv, m_arg); }, - ("map_or", [def, map]) => { + (sym::map_or, [def, map]) => { option_map_or_none::check(cx, expr, recv, def, map); manual_ok_or::check(cx, expr, recv, def, map); unnecessary_map_or::check(cx, expr, recv, def, map, span, self.msrv); }, - ("map_or_else", [def, map]) => { + (sym::map_or_else, [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); unnecessary_result_map_or_else::check(cx, expr, recv, def, map); }, - ("next", []) => { + (sym::next, []) => { if let Some((name2, recv2, args2, _, _)) = method_call(recv) { match (name2, args2) { - ("cloned", []) => iter_overeager_cloned::check( + (sym::cloned, []) => iter_overeager_cloned::check( cx, expr, recv, @@ -5260,19 +5260,19 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), - ("iter", []) => iter_next_slice::check(cx, expr, recv2), - ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), - ("skip_while", [_]) => skip_while_next::check(cx, expr), - ("rev", []) => manual_next_back::check(cx, expr, recv, recv2), + (sym::filter, [arg]) => filter_next::check(cx, expr, recv2, arg), + (sym::filter_map, [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), + (sym::iter, []) => iter_next_slice::check(cx, expr, recv2), + (sym::skip, [arg]) => iter_skip_next::check(cx, expr, recv2, arg), + (sym::skip_while, [_]) => skip_while_next::check(cx, expr), + (sym::rev, []) => manual_next_back::check(cx, expr, recv, recv2), _ => {}, } } }, - ("nth", [n_arg]) => match method_call(recv) { - Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + (sym::nth, [n_arg]) => match method_call(recv) { + Some((sym::bytes, recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5280,54 +5280,54 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - Some((iter_method @ ("iter" | "iter_mut"), iter_recv, [], iter_span, _)) => { + Some((iter_method @ (sym::iter | sym::iter_mut), iter_recv, [], iter_span, _)) => { if !iter_nth::check(cx, expr, iter_recv, iter_method, iter_span, span) { iter_nth_zero::check(cx, expr, recv, n_arg); } }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, - ("ok_or_else", [arg]) => { + (sym::ok_or_else, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); }, - ("open", [_]) => { + (sym::open, [_]) => { open_options::check(cx, expr, recv); }, - ("or_else", [arg]) => { + (sym::or_else, [arg]) => { if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, - ("push", [arg]) => { + (sym::push, [arg]) => { path_buf_push_overwrite::check(cx, expr, arg); }, - ("read_to_end", [_]) => { + (sym::read_to_end, [_]) => { verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG); }, - ("read_to_string", [_]) => { + (sym::read_to_string, [_]) => { verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG); }, - ("read_line", [arg]) => { + (sym::read_line, [arg]) => { read_line_without_trim::check(cx, expr, recv, arg); }, - ("repeat", [arg]) => { + (sym::repeat, [arg]) => { repeat_once::check(cx, expr, recv, arg); }, - (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => { + (name @ (sym::replace | sym::replacen), [arg1, arg2] | [arg1, arg2, _]) => { no_effect_replace::check(cx, expr, arg1, arg2); // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint if self.msrv.meets(cx, msrvs::PATTERN_TRAIT_CHAR_ARRAY) - && name == "replace" - && let Some(("replace", ..)) = method_call(recv) + && name == sym::replace + && let Some((sym::replace, ..)) = method_call(recv) { collapsible_str_replace::check(cx, expr, arg1, arg2); } }, - ("resize", [count_arg, default_arg]) => { + (sym::resize, [count_arg, default_arg]) => { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, - ("seek", [arg]) => { + (sym::seek, [arg]) => { if self.msrv.meets(cx, msrvs::SEEK_FROM_CURRENT) { seek_from_current::check(cx, expr, recv, arg); } @@ -5335,11 +5335,11 @@ impl Methods { seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); } }, - ("skip", [arg]) => { + (sym::skip, [arg]) => { iter_skip_zero::check(cx, expr, arg); iter_out_of_bounds::check_skip(cx, expr, recv, arg); - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, expr, @@ -5350,34 +5350,34 @@ impl Methods { ); } }, - ("sort", []) => { + (sym::sort, []) => { stable_sort_primitive::check(cx, expr, recv); }, - ("sort_by", [arg]) => { + (sym::sort_by, [arg]) => { unnecessary_sort_by::check(cx, expr, recv, arg, false); }, - ("sort_unstable_by", [arg]) => { + (sym::sort_unstable_by, [arg]) => { unnecessary_sort_by::check(cx, expr, recv, arg, true); }, - ("split", [arg]) => { + (sym::split, [arg]) => { str_split::check(cx, expr, recv, arg); }, - ("splitn" | "rsplitn", [count_arg, pat_arg]) => { + (sym::splitn | sym::rsplitn, [count_arg, pat_arg]) => { if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv); } }, - ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { + (sym::splitn_mut | sym::rsplitn_mut, [count_arg, _]) => { if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); } }, - ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("take", [arg]) => { + (sym::step_by, [arg]) => iterator_step_by_zero::check(cx, expr, arg), + (sym::take, [arg]) => { iter_out_of_bounds::check_take(cx, expr, recv, arg); manual_repeat_n::check(cx, expr, recv, arg, self.msrv); - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, expr, @@ -5388,74 +5388,89 @@ impl Methods { ); } }, - ("take", []) => needless_option_take::check(cx, expr, recv), - ("then", [arg]) => { + (sym::take, []) => needless_option_take::check(cx, expr, recv), + (sym::then, [arg]) => { if !self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { return; } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); }, - ("try_into", []) if is_trait_method(cx, expr, sym::TryInto) => { + (sym::try_into, []) if is_trait_method(cx, expr, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, - ("to_owned", []) => { + (sym::to_owned, []) => { if !suspicious_to_owned::check(cx, expr, recv) { implicit_clone::check(cx, name, expr, recv); } }, - ("to_os_string" | "to_path_buf" | "to_vec", []) => { + (sym::to_os_string | sym::to_path_buf | sym::to_vec, []) => { implicit_clone::check(cx, name, expr, recv); }, - ("type_id", []) => { + (sym::type_id, []) => { type_id_on_box::check(cx, recv, expr.span); }, - ("unwrap", []) => { + (sym::unwrap, []) => { match method_call(recv) { - Some(("get", recv, [get_arg], _, _)) => { + Some((sym::get, recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, false); }, - Some(("get_mut", recv, [get_arg], _, _)) => { + Some((sym::get_mut, recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, - Some(("or", recv, [or_arg], or_span, _)) => { + Some((sym::or, recv, [or_arg], or_span, _)) => { or_then_unwrap::check(cx, expr, recv, or_arg, or_span); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_or", [u_arg]) => { + (sym::unwrap_or, [u_arg]) => { match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => { - manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); + Some((arith @ (sym::checked_add | sym::checked_sub | sym::checked_mul), lhs, [rhs], _, _)) => { + manual_saturating_arithmetic::check( + cx, + expr, + lhs, + rhs, + u_arg, + &arith.as_str()[const { "checked_".len() }..], + ); }, - Some(("map", m_recv, [m_arg], span, _)) => { + Some((sym::map, m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); }, - Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, "unwrap_or"); + Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, name); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_or_default", []) => { + (sym::unwrap_or_default, []) => { match method_call(recv) { - Some(("map", m_recv, [arg], span, _)) => { + Some((sym::map, m_recv, [arg], span, _)) => { manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv); }, - Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, None, then_method, "unwrap_or_default"); + Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + None, + then_method, + sym::unwrap_or_default, + ); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_or_else", [u_arg]) => { + (sym::unwrap_or_else, [u_arg]) => { match method_call(recv) { - Some(("map", recv, [map_arg], _, _)) + Some((sym::map, recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, - Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { + Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { obfuscated_if_else::check( cx, expr, @@ -5463,7 +5478,7 @@ impl Methods { t_arg, Some(u_arg), then_method, - "unwrap_or_else", + sym::unwrap_or_else, ); }, _ => { @@ -5472,13 +5487,13 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("wake", []) => { + (sym::wake, []) => { waker_clone_wake::check(cx, expr, recv); }, - ("write", []) => { + (sym::write, []) => { readonly_write_lock::check(cx, expr, recv); }, - ("zip", [arg]) => { + (sym::zip, [arg]) => { if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind && name.ident.name == sym::iter { @@ -5490,8 +5505,8 @@ impl Methods { } // Handle method calls whose receiver and arguments may come from expansion if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { - match (path.ident.name.as_str(), args) { - ("expect", [_]) if !matches!(method_call(recv), Some(("ok" | "err", _, [], _, _))) => { + match (path.ident.name, args) { + (sym::expect, [_]) if !matches!(method_call(recv), Some((sym::ok | sym::err, _, [], _, _))) => { unwrap_expect_used::check( cx, expr, @@ -5502,7 +5517,7 @@ impl Methods { unwrap_expect_used::Variant::Expect, ); }, - ("expect_err", [_]) => { + (sym::expect_err, [_]) => { unwrap_expect_used::check( cx, expr, @@ -5513,7 +5528,7 @@ impl Methods { unwrap_expect_used::Variant::Expect, ); }, - ("unwrap", []) => { + (sym::unwrap, []) => { unwrap_expect_used::check( cx, expr, @@ -5524,7 +5539,7 @@ impl Methods { unwrap_expect_used::Variant::Unwrap, ); }, - ("unwrap_err", []) => { + (sym::unwrap_err, []) => { unwrap_expect_used::check( cx, expr, @@ -5543,13 +5558,13 @@ impl Methods { fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, call_span: Span, is_some: bool) { match method_call(recv) { - Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) => { + Some((name @ (sym::find | sym::position | sym::rposition), f_recv, [arg], span, _)) => { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); }, - Some(("get", f_recv, [arg], _, _)) => { + Some((sym::get, f_recv, [arg], _, _)) => { unnecessary_get_then_check::check(cx, call_span, recv, f_recv, arg, is_some); }, - Some(("first", f_recv, [], _, _)) => { + Some((sym::first, f_recv, [], _, _)) => { unnecessary_first_then_check::check(cx, call_span, recv, f_recv, is_some); }, _ => {}, @@ -5593,7 +5608,7 @@ const FN_HEADER: hir::FnHeader = hir::FnHeader { struct ShouldImplTraitCase { trait_name: &'static str, - method_name: &'static str, + method_name: Symbol, param_count: usize, fn_header: hir::FnHeader, // implicit self kind expected (none, self, &self, ...) @@ -5606,7 +5621,7 @@ struct ShouldImplTraitCase { impl ShouldImplTraitCase { const fn new( trait_name: &'static str, - method_name: &'static str, + method_name: Symbol, param_count: usize, fn_header: hir::FnHeader, self_kind: SelfKind, @@ -5639,36 +5654,36 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), - ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), - ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), - ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs index 7c9f7bae99063..635d06330e058 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs @@ -4,11 +4,11 @@ use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use super::NEEDLESS_AS_BYTES; -pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, prev_recv: &Expr<'_>, span: Span) { +pub fn check(cx: &LateContext<'_>, prev_method: Symbol, method: Symbol, prev_recv: &Expr<'_>, span: Span) { let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs index 538aa9097a40f..d77d044340dca 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,22 +1,22 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_res; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; +use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_hir::def::Res; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::Symbol; use super::NEEDLESS_OPTION_AS_DEREF; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: &str) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: Symbol) { let typeck = cx.typeck_results(); let outer_ty = typeck.expr_ty(expr); if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { - if name == "as_deref_mut" && recv.is_syntactic_place_expr() { + if name == sym::as_deref_mut && recv.is_syntactic_place_expr() { let Res::Local(binding_id) = path_res(cx, recv) else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs index cd1b97f3c51b5..1544a12e6ba83 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::NEEDLESS_OPTION_TAKE; @@ -42,20 +42,20 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// When this function is called, we are reasonably certain that the `ExprKind` is either /// `Call` or `MethodCall` because we already checked that the expression is not /// `is_syntactic_place_expr()`. -fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> { +fn source_of_temporary_value(expr: &Expr<'_>) -> Option { match expr.peel_borrows().kind { ExprKind::Call(function, _) => { if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind && !func_path.segments.is_empty() { - return Some(func_path.segments[0].ident.name.as_str()); + return Some(func_path.segments[0].ident.name); } if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind { - return Some(func_path_segment.ident.name.as_str()); + return Some(func_path_segment.ident.name); } None }, - ExprKind::MethodCall(path_segment, ..) => Some(path_segment.ident.name.as_str()), + ExprKind::MethodCall(path_segment, ..) => Some(path_segment.ident.name), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs index 1cc56de48763e..604b48656aeac 100644 --- a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs @@ -1,13 +1,14 @@ use super::OBFUSCATED_IF_ELSE; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_eager_eval; -use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; +use clippy_utils::{get_parent_expr, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind; use rustc_lint::LateContext; +use rustc_span::Symbol; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -15,8 +16,8 @@ pub(super) fn check<'tcx>( then_recv: &'tcx hir::Expr<'_>, then_arg: &'tcx hir::Expr<'_>, unwrap_arg: Option<&'tcx hir::Expr<'_>>, - then_method_name: &str, - unwrap_method_name: &str, + then_method_name: Symbol, + unwrap_method_name: Symbol, ) { let recv_ty = cx.typeck_results().expr_ty(then_recv); @@ -31,25 +32,25 @@ pub(super) fn check<'tcx>( }; let if_then = match then_method_name { - "then" if let ExprKind::Closure(closure) = then_arg.kind => { + sym::then if let ExprKind::Closure(closure) = then_arg.kind => { let body = cx.tcx.hir_body(closure.body); snippet_with_applicability(cx, body.value.span, "..", &mut applicability) }, - "then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + sym::then_some => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), _ => return, }; // FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol let els = match unwrap_method_name { - "unwrap_or" => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), - "unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { + sym::unwrap_or => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), + sym::unwrap_or_else if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { let body = cx.tcx.hir_body(closure.body); snippet_with_applicability(cx, body.value.span, "..", &mut applicability) }, - "unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { + sym::unwrap_or_else if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()" }, - "unwrap_or_default" => "Default::default()".into(), + sym::unwrap_or_default => "Default::default()".into(), _ => return, }; diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs index 9b22494888fb2..3c38deca6cd1f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -1,14 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::{OPTION_AS_REF_CLONED, method_call}; pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { - if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) + if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = + method_call(cloned_recv) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index b78b082e460ef..c74c42e9e5bd5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_span: Span, - name: &str, + name: Symbol, receiver: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], ) { @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. fn check_unwrap_or_default( cx: &LateContext<'_>, - name: &str, + name: Symbol, receiver: &hir::Expr<'_>, fun: &hir::Expr<'_>, call_expr: Option<&hir::Expr<'_>>, @@ -66,8 +66,8 @@ pub(super) fn check<'tcx>( }; let sugg = match (name, call_expr.is_some()) { - ("unwrap_or", true) | ("unwrap_or_else", false) => sym::unwrap_or_default, - ("or_insert", true) | ("or_insert_with", false) => sym::or_default, + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, _ => return false, }; @@ -126,7 +126,7 @@ pub(super) fn check<'tcx>( #[expect(clippy::too_many_arguments)] fn check_or_fn_call<'tcx>( cx: &LateContext<'tcx>, - name: &str, + name: Symbol, method_span: Span, self_expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, @@ -137,11 +137,16 @@ pub(super) fn check<'tcx>( fun_span: Option, ) -> bool { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ - (sym::BTreeEntry, false, &["or_insert"], "with"), - (sym::HashMapEntry, false, &["or_insert"], "with"), - (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (sym::Result, true, &["or", "unwrap_or"], "else"), + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 4] = [ + (sym::BTreeEntry, false, &[sym::or_insert], "with"), + (sym::HashMapEntry, false, &[sym::or_insert], "with"), + ( + sym::Option, + false, + &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], + "else", + ), + (sym::Result, true, &[sym::or, sym::unwrap_or], "else"), ]; if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index 97c8ce2bcdd28..855babb797a21 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -2,14 +2,13 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs}; +use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs, sym}; use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use super::SEARCH_IS_SOME; @@ -19,7 +18,7 @@ use super::SEARCH_IS_SOME; pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &'tcx hir::Expr<'_>, - search_method: &str, + search_method: Symbol, is_some: bool, search_recv: &hir::Expr<'_>, search_arg: &'tcx hir::Expr<'_>, @@ -35,7 +34,7 @@ pub(super) fn check<'tcx>( // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` let mut applicability = Applicability::MachineApplicable; - let any_search_snippet = if search_method == "find" + let any_search_snippet = if search_method == sym::find && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind && let closure_body = cx.tcx.hir_body(body) && let Some(closure_arg) = closure_body.params.first() @@ -107,7 +106,7 @@ pub(super) fn check<'tcx>( } } // lint if `find()` is called by `String` or `&str` - else if search_method == "find" { + else if search_method == sym::find { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); if is_type_lang_item(cx, self_ty, hir::LangItem::String) { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_split.rs b/src/tools/clippy/clippy_lints/src/methods/str_split.rs index fb4ac7b3613dd..479064a0671e5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_split.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_split.rs @@ -15,7 +15,7 @@ pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &' // or `"\r\n"`). There are a lot of ways to specify a pattern, and this lint only checks the most // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. if let ExprKind::MethodCall(trim_method_name, trim_recv, [], _) = split_recv.kind - && trim_method_name.ident.as_str() == "trim" + && trim_method_name.ident.name == sym::trim && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() && !is_const_evaluatable(cx, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index c8efb600f576f..6935ae1f391fa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, path_to_local_id, paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -12,13 +12,13 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, Symbol, SyntaxContext, sym}; +use rustc_span::{Span, Symbol, SyntaxContext}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; pub(super) fn check( cx: &LateContext<'_>, - method_name: &str, + method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, @@ -45,9 +45,9 @@ pub(super) fn check( } } -fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { +fn lint_needless(cx: &LateContext<'_>, method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { let mut app = Applicability::MachineApplicable; - let r = if method_name == "splitn" { "" } else { "r" }; + let r = if method_name == sym::splitn { "" } else { "r" }; span_lint_and_sugg( cx, @@ -66,14 +66,14 @@ fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_ fn check_manual_split_once( cx: &LateContext<'_>, - method_name: &str, + method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, usage: &IterUsage, ) { let ctxt = expr.span.ctxt(); - let (msg, reverse) = if method_name == "splitn" { + let (msg, reverse) = if method_name == sym::splitn { ("manual implementation of `split_once`", false) } else { ("manual implementation of `rsplit_once`", true) @@ -121,7 +121,7 @@ fn check_manual_split_once( /// ``` fn check_manual_split_once_indirect( cx: &LateContext<'_>, - method_name: &str, + method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, @@ -143,7 +143,7 @@ fn check_manual_split_once_indirect( && first.name != second.name && !local_used_after_expr(cx, iter_binding_id, second.init_expr) { - let (r, lhs, rhs) = if method_name == "splitn" { + let (r, lhs, rhs) = if method_name == sym::splitn { ("", first.name, second.name) } else { ("r", second.name, first.name) diff --git a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs index c7885f689d75b..f11a41f90f1ab 100644 --- a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs +++ b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{method_chain_args, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { return; } - if let Some(arglists) = method_chain_args(arg, &["chars"]) { + if let Some(arglists) = method_chain_args(arg, &[sym::chars]) { let target = &arglists[0].0; let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if self_ty.is_str() { diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs index ff5c1d1a40193..f8b6d4349fbeb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_note; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_span::Symbol; use rustc_span::source_map::Spanned; use super::SUSPICIOUS_SPLITN; -pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { +pub(super) fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { if count <= 1 && let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(call_id) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index 79ed352193fd7..d260e0ef6e197 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -9,10 +9,16 @@ use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_span::Symbol; use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + name: Symbol, +) { if !is_trait_method(cx, expr, sym::Iterator) { return; } @@ -38,7 +44,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { // Check if the closure is .filter_map(|x| Some(x)) - if name == "filter_map" + if name == sym::filter_map && let hir::ExprKind::Call(expr, args) = body.value.kind && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) && let hir::ExprKind::Path(_) = args[0].kind @@ -51,7 +57,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a ); return; } - if name == "filter_map" { + if name == sym::filter_map { "map(..)" } else { "map(..).next()" @@ -61,7 +67,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) => { - if name == "filter_map" { "filter(..)" } else { "find(..)" } + if name == sym::filter_map { + "filter(..)" + } else { + "find(..)" + } }, _ => return, } @@ -70,7 +80,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a }; span_lint( cx, - if name == "filter_map" { + if name == sym::filter_map { UNNECESSARY_FILTER_MAP } else { UNNECESSARY_FIND_MAP diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index fa3a29e366709..cc4448192d3e7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res}; +use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; +use rustc_span::Symbol; use super::UNNECESSARY_LITERAL_UNWRAP; @@ -25,7 +26,7 @@ pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, - method: &str, + method: Symbol, args: &[hir::Expr<'_>], ) { let init = clippy_utils::expr_or_init(cx, recv); @@ -42,17 +43,17 @@ pub(super) fn check( let res = cx.qpath_res(qpath, call.hir_id()); if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) { - ("Some", call_args, get_ty_from_args(args, 0)) + (sym::Some, call_args, get_ty_from_args(args, 0)) } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultOk) { - ("Ok", call_args, get_ty_from_args(args, 0)) + (sym::Ok, call_args, get_ty_from_args(args, 0)) } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultErr) { - ("Err", call_args, get_ty_from_args(args, 1)) + (sym::Err, call_args, get_ty_from_args(args, 1)) } else { return; } } else if is_res_lang_ctor(cx, path_res(cx, init), hir::LangItem::OptionNone) { let call_args: &[hir::Expr<'_>] = &[]; - ("None", call_args, None) + (sym::None, call_args, None) } else { return; }; @@ -62,12 +63,12 @@ pub(super) fn check( span_lint_and_then(cx, UNNECESSARY_LITERAL_UNWRAP, expr.span, help_message, |diag| { let suggestions = match (constructor, method, ty) { - ("None", "unwrap", _) => Some(vec![(expr.span, "panic!()".to_string())]), - ("None", "expect", _) => Some(vec![ + (sym::None, sym::unwrap, _) => Some(vec![(expr.span, "panic!()".to_string())]), + (sym::None, sym::expect, _) => Some(vec![ (expr.span.with_hi(args[0].span.lo()), "panic!(".to_string()), (expr.span.with_lo(args[0].span.hi()), ")".to_string()), ]), - ("Some" | "Ok", "unwrap_unchecked", _) | ("Err", "unwrap_err_unchecked", _) => { + (sym::Some | sym::Ok, sym::unwrap_unchecked, _) | (sym::Err, sym::unwrap_err_unchecked, _) => { let mut suggs = vec![ (recv.span.with_hi(call_args[0].span.lo()), String::new()), (expr.span.with_lo(call_args[0].span.hi()), String::new()), @@ -83,7 +84,7 @@ pub(super) fn check( } Some(suggs) }, - ("None", "unwrap_or_default", _) => { + (sym::None, sym::unwrap_or_default, _) => { let ty = cx.typeck_results().expr_ty(expr); let default_ty_string = if let ty::Adt(def, ..) = ty.kind() { with_forced_trimmed_paths!(format!("{}", cx.tcx.def_path_str(def.did()))) @@ -92,11 +93,11 @@ pub(super) fn check( }; Some(vec![(expr.span, format!("{default_ty_string}::default()"))]) }, - ("None", "unwrap_or", _) => Some(vec![ + (sym::None, sym::unwrap_or, _) => Some(vec![ (expr.span.with_hi(args[0].span.lo()), String::new()), (expr.span.with_lo(args[0].span.hi()), String::new()), ]), - ("None", "unwrap_or_else", _) => match args[0].kind { + (sym::None, sym::unwrap_or_else, _) => match args[0].kind { hir::ExprKind::Closure(hir::Closure { body, .. }) => Some(vec![ (expr.span.with_hi(cx.tcx.hir_body(*body).value.span.lo()), String::new()), (expr.span.with_lo(args[0].span.hi()), String::new()), @@ -105,14 +106,14 @@ pub(super) fn check( }, _ if call_args.is_empty() => None, (_, _, Some(_)) => None, - ("Ok", "unwrap_err", None) | ("Err", "unwrap", None) => Some(vec![ + (sym::Ok, sym::unwrap_err, None) | (sym::Err, sym::unwrap, None) => Some(vec![ ( recv.span.with_hi(call_args[0].span.lo()), "panic!(\"{:?}\", ".to_string(), ), (expr.span.with_lo(call_args[0].span.hi()), ")".to_string()), ]), - ("Ok", "expect_err", None) | ("Err", "expect", None) => Some(vec![ + (sym::Ok, sym::expect_err, None) | (sym::Err, sym::expect, None) => Some(vec![ ( recv.span.with_hi(call_args[0].span.lo()), "panic!(\"{1}: {:?}\", ".to_string(), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs index 7d01bdc2269bd..413881d5ec99e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -5,16 +5,17 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, - name: &str, + name: Symbol, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, ) { @@ -47,10 +48,10 @@ pub(super) fn check<'tcx>( } } -fn lint(cx: &LateContext<'_>, expr: &Expr<'_>, name: &str, lhs: Span, rhs: Span, order: Ordering) { +fn lint(cx: &LateContext<'_>, expr: &Expr<'_>, name: Symbol, lhs: Span, rhs: Span, order: Ordering) { let cmp_str = if order.is_ge() { "smaller" } else { "greater" }; - let suggested_value = if (name == "min" && order.is_ge()) || (name == "max" && order.is_le()) { + let suggested_value = if (name == sym::min && order.is_ge()) || (name == sym::max && order.is_le()) { snippet(cx, rhs, "..") } else { snippet(cx, lhs, "..") diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 87bb8d46a1d6a..29a0d2950bc6a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -619,7 +619,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { - is_clone_like(cx, method_name.as_str(), method_def_id) + is_clone_like(cx, method_name, method_def_id) || is_cow_into_owned(cx, method_name, method_def_id) || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) } @@ -655,11 +655,18 @@ fn is_to_string_on_string_like<'a>( } } -fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashSet) - || is_type_diagnostic_item(cx, ty, sym::HashMap) - || is_type_diagnostic_item(cx, ty, sym::BTreeMap) - || is_type_diagnostic_item(cx, ty, sym::BTreeSet) +fn std_map_key<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) + if matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::BTreeMap | sym::BTreeSet | sym::HashMap | sym::HashSet) + ) => + { + Some(args.type_at(0)) + }, + _ => None, + } } fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { @@ -679,11 +686,11 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = arg.kind && let ExprKind::MethodCall(method_path, caller, &[], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let method_name = method_path.ident.name.as_str() + && let method_name = method_path.ident.name && match method_name { - "to_owned" => cx.tcx.is_diagnostic_item(sym::to_owned_method, method_def_id), - "to_string" => cx.tcx.is_diagnostic_item(sym::to_string_method, method_def_id), - "to_vec" => cx + sym::to_owned => cx.tcx.is_diagnostic_item(sym::to_owned_method, method_def_id), + sym::to_string => cx.tcx.is_diagnostic_item(sym::to_string_method, method_def_id), + sym::to_vec => cx .tcx .impl_of_method(method_def_id) .filter(|&impl_did| cx.tcx.type_of(impl_did).instantiate_identity().is_slice()) @@ -721,6 +728,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx // 1. This is a method with only one argument that doesn't come from a trait. // 2. That it has `Borrow` in its generic predicates. // 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, `BTreeSet`, `BTreeMap`). +// 4. The key to the "map type" is not a reference. fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) @@ -738,7 +746,9 @@ fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { }) && let caller_ty = cx.typeck_results().expr_ty(caller) // For now we limit it to "map types". - && is_a_std_map_type(cx, caller_ty) + && let Some(key_ty) = std_map_key(cx, caller_ty) + // We need to check that the key type is not a reference. + && !key_ty.is_ref() { check_if_applicable_to_argument(cx, &arg); } diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index 17e2620d9dd47..d30c12e0c4830 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -7,7 +7,7 @@ use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use core::ops::ControlFlow; @@ -39,7 +39,7 @@ fn get_enum_ty(enum_ty: Ty<'_>) -> Option> { } /// Checks for the `USELESS_ASREF` lint. -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbol, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { @@ -79,9 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, applicability, ); } - } else if let Some(impl_id) = cx.tcx.opt_parent(def_id) + } else if let Some(impl_id) = cx.tcx.impl_of_method(def_id) && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() - && (cx.tcx.lang_items().option_type() == Some(adt.did()) || cx.tcx.is_diagnostic_item(sym::Result, adt.did())) + && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Option | sym::Result)) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); @@ -161,7 +161,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { } } -fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: &str) { +fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: Symbol) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs index 7384e534ed7d7..ad9b3c3645425 100644 --- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_copy; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::fmt; use super::WRONG_SELF_CONVENTION; @@ -83,17 +83,18 @@ impl fmt::Display for Convention { #[allow(clippy::too_many_arguments)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - item_name: &str, + item_name: Symbol, self_ty: Ty<'tcx>, first_arg_ty: Ty<'tcx>, first_arg_span: Span, implements_trait: bool, is_trait_item: bool, ) { + let item_name_str = item_name.as_str(); if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { convs .iter() - .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item)) + .all(|conv| conv.check(cx, self_ty, item_name_str, implements_trait, is_trait_item)) }) { // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032) if implements_trait diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index f3e24044fb6c2..a6be7581c9a38 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -7,7 +7,7 @@ use rustc_abi::ExternAbi; use rustc_errors::Applicability; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; -use rustc_hir::{self as hir, Body, Constness, FnDecl, GenericParamKind}; +use rustc_hir::{self as hir, Body, Constness, FnDecl, GenericParamKind, OwnerId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -125,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { } }, FnKind::Method(_, sig, ..) => { - if already_const(sig.header) || trait_ref_of_method(cx, def_id).is_some() { + if already_const(sig.header) || trait_ref_of_method(cx, OwnerId { def_id }).is_some() { return; } }, diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index a45031ce22b91..98a9a98d281af 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind - && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, item.owner_id).is_none() { self.check_sig(cx, item.owner_id.def_id, sig.decl); } diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index f768e11a4a2bb..3ed4b1c2ea947 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, - is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, + is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, sym, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -320,7 +320,7 @@ fn check_comparison<'a, 'tcx>( cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(right_side), ); - if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() { + if is_expn_of(left_side.span, sym::cfg).is_some() || is_expn_of(right_side.span, sym::cfg).is_some() { return; } if l_ty.is_bool() && r_ty.is_bool() { diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 275d710c76a9b..95623467b8153 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -212,7 +212,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_diagnostic_item(cx, ty, sym::Vec) - && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]) + && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path .segments @@ -253,8 +253,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_lang_item(cx, ty, LangItem::String) - && let Some(clone_spans) = - get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) + && let Some(clone_spans) = get_spans( + cx, + Some(body.id()), + idx, + &[(sym::clone, ".to_string()"), (sym::as_str, "")], + ) { diag.span_suggestion( input.span, diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 7ab7976d5697a..02c48166131ef 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -221,10 +221,16 @@ fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } +/// Checks if dropping `expr` might have a visible side effect. +fn expr_ty_has_significant_drop(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + ty.has_significant_drop(cx.tcx, cx.typing_env()) +} + fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Lit(..) | ExprKind::Closure { .. } => true, - ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)), + ExprKind::Path(..) => !expr_ty_has_significant_drop(cx, expr), ExprKind::Index(a, b, _) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)), ExprKind::Repeat(inner, _) @@ -233,8 +239,8 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | ExprKind::Unary(_, inner) | ExprKind::Field(inner, _) | ExprKind::AddrOf(_, _, inner) => has_no_effect(cx, inner), - ExprKind::Struct(_, fields, ref base) => { - !has_drop(cx, cx.typeck_results().expr_ty(expr)) + ExprKind::Struct(_, fields, base) => { + !expr_ty_has_significant_drop(cx, expr) && fields.iter().all(|field| has_no_effect(cx, field.expr)) && match &base { StructTailExpr::None | StructTailExpr::DefaultFields(_) => true, @@ -252,7 +258,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) ); if def_matched || is_range_literal(expr) { - !has_drop(cx, cx.typeck_results().expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg)) + !expr_ty_has_significant_drop(cx, expr) && args.iter().all(|arg| has_no_effect(cx, arg)) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 6d3e77b6b6e97..a27c6aa75e369 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -1,56 +1,78 @@ -use std::ptr; +// Implementation for lints detecting interior mutability in constants. +// +// For `declare_interior_mutable_const` there are three strategies used to +// determine if a value has interior mutability: +// * A type-based check. This is the least accurate, but can always run. +// * A const-eval based check. This is the most accurate, but this requires that the value is +// defined and does not work with generics. +// * A HIR-tree based check. This is less accurate than const-eval, but it can be applied to generic +// values. +// +// For `borrow_interior_mutable_const` the same three strategies are applied +// when checking a constant's value, but field and array index projections at +// the borrow site are taken into account as well. As an example: `FOO.bar` may +// have interior mutability, but `FOO.baz` may not. When borrowing `FOO.baz` no +// warning will be issued. +// +// No matter the lint or strategy, a warning should only be issued if a value +// definitely contains interior mutability. use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::is_in_const_context; use clippy_utils::macros::macro_backtrace; -use clippy_utils::ty::{InteriorMut, implements_trait}; -use rustc_abi::VariantIdx; +use clippy_utils::paths::{PathNS, lookup_path_str}; +use clippy_utils::ty::{get_field_idx_by_name, implements_trait}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ - BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, + Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, StructTailExpr, TraitItem, TraitItemKind, UnOp, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::mir::{ConstValue, UnevaluatedConst}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::{ + self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, + TypeckResults, TypingEnv, }; -use rustc_lint::{LateContext, LateLintPass, Lint}; -use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId, ReportedErrorInfo}; -use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, sym}; +use std::collections::hash_map::Entry; -// FIXME: this is a correctness problem but there's no suitable -// warn-by-default category. declare_clippy_lint! { /// ### What it does - /// Checks for declaration of `const` items which is interior - /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). + /// Checks for the declaration of named constant which contain interior mutability. /// /// ### Why is this bad? - /// Consts are copied everywhere they are referenced, i.e., - /// every time you refer to the const a fresh instance of the `Cell` or `Mutex` - /// or `AtomicXxxx` will be created, which defeats the whole purpose of using - /// these types in the first place. + /// Named constants are copied at every use site which means any change to their value + /// will be lost after the newly created value is dropped. e.g. /// - /// The `const` should better be replaced by a `static` item if a global - /// variable is wanted, or replaced by a `const fn` if a constructor is wanted. + /// ```rust + /// use core::sync::atomic::{AtomicUsize, Ordering}; + /// const ATOMIC: AtomicUsize = AtomicUsize::new(0); + /// fn add_one() -> usize { + /// // This will always return `0` since `ATOMIC` is copied before it's used. + /// ATOMIC.fetch_add(1, Ordering::AcqRel) + /// } + /// ``` /// - /// ### Known problems - /// A "non-constant" const item is a legacy way to supply an - /// initialized value to downstream `static` items (e.g., the - /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, - /// and this lint should be suppressed. + /// If shared modification of the value is desired, a `static` item is needed instead. + /// If that is not desired, a `const fn` constructor should be used to make it obvious + /// at the use site that a new value is created. /// - /// Even though the lint avoids triggering on a constant whose type has enums that have variants - /// with interior mutability, and its value uses non interior mutable variants (see - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples); - /// it complains about associated constants without default values only based on its types; - /// which might not be preferable. - /// There're other enums plus associated constants cases that the lint cannot handle. + /// ### Known problems + /// Prior to `const fn` stabilization this was the only way to provide a value which + /// could initialize a `static` item (e.g. the `std::sync::ONCE_INIT` constant). In + /// this case the use of `const` is required and this lint should be suppressed. /// - /// Types that have underlying or potential interior mutability trigger the lint whether - /// the interior mutable field is used or not. See issue - /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) + /// There also exists types which contain private fields with interior mutability, but + /// no way to both create a value as a constant and modify any mutable field using the + /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to + /// scan a crate's interface to see if this is the case, all such types will be linted. + /// If this happens use the `ignore-interior-mutability` configuration option to allow + /// the type. /// /// ### Example /// ```no_run @@ -74,20 +96,44 @@ declare_clippy_lint! { "declaring `const` with interior mutability" } -// FIXME: this is a correctness problem but there's no suitable -// warn-by-default category. declare_clippy_lint! { /// ### What it does - /// Checks if `const` items which is interior mutable (e.g., - /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. + /// Checks for a borrow of a named constant with interior mutability. /// /// ### Why is this bad? - /// Consts are copied everywhere they are referenced, i.e., - /// every time you refer to the const a fresh instance of the `Cell` or `Mutex` - /// or `AtomicXxxx` will be created, which defeats the whole purpose of using - /// these types in the first place. + /// Named constants are copied at every use site which means any change to their value + /// will be lost after the newly created value is dropped. e.g. + /// + /// ```rust + /// use core::sync::atomic::{AtomicUsize, Ordering}; + /// const ATOMIC: AtomicUsize = AtomicUsize::new(0); + /// fn add_one() -> usize { + /// // This will always return `0` since `ATOMIC` is copied before it's borrowed + /// // for use by `fetch_add`. + /// ATOMIC.fetch_add(1, Ordering::AcqRel) + /// } + /// ``` /// - /// The `const` value should be stored inside a `static` item. + /// ### Known problems + /// This lint does not, and cannot in general, determine if the borrow of the constant + /// is used in a way which causes a mutation. e.g. + /// + /// ```rust + /// use core::cell::Cell; + /// const CELL: Cell = Cell::new(0); + /// fn get_cell() -> Cell { + /// // This is fine. It borrows a copy of `CELL`, but never mutates it through the + /// // borrow. + /// CELL.clone() + /// } + /// ``` + /// + /// There also exists types which contain private fields with interior mutability, but + /// no way to both create a value as a constant and modify any mutable field using the + /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to + /// scan a crate's interface to see if this is the case, all such types will be linted. + /// If this happens use the `ignore-interior-mutability` configuration option to allow + /// the type. /// /// ### Example /// ```no_run @@ -113,60 +159,101 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } -#[derive(Copy, Clone)] -enum Source<'tcx> { - Item { item: Span, ty: Ty<'tcx> }, - Assoc { item: Span }, - Expr { expr: Span }, +#[derive(Clone, Copy)] +enum IsFreeze { + /// The type and all possible values are `Freeze` + Yes, + /// The type itself is non-`Freeze`, but not all values are. + Maybe, + /// The type and all possible values are non-`Freeze` + No, } +impl IsFreeze { + /// Merges the variants of a sum type (i.e. an enum). + fn from_variants(iter: impl Iterator) -> Self { + iter.fold(Self::Yes, |x, y| match (x, y) { + (Self::Maybe, _) | (_, Self::Maybe) | (Self::No, Self::Yes) | (Self::Yes, Self::No) => Self::Maybe, + (Self::No, Self::No) => Self::No, + (Self::Yes, Self::Yes) => Self::Yes, + }) + } -impl Source<'_> { - #[must_use] - fn lint(&self) -> (&'static Lint, &'static str, Span) { - match self { - Self::Item { item, .. } | Self::Assoc { item, .. } => ( - DECLARE_INTERIOR_MUTABLE_CONST, - "a `const` item should not be interior mutable", - *item, - ), - Self::Expr { expr } => ( - BORROW_INTERIOR_MUTABLE_CONST, - "a `const` item with interior mutability should not be borrowed", - *expr, - ), - } + /// Merges the fields of a product type (e.g. a struct or tuple). + fn from_fields(mut iter: impl Iterator) -> Self { + iter.try_fold(Self::Yes, |x, y| match (x, y) { + (Self::No, _) | (_, Self::No) => None, + (Self::Maybe, _) | (_, Self::Maybe) => Some(Self::Maybe), + (Self::Yes, Self::Yes) => Some(Self::Yes), + }) + .unwrap_or(Self::No) + } + + /// Checks if this is definitely `Freeze`. + fn is_freeze(self) -> bool { + matches!(self, Self::Yes) + } + + /// Checks if this is definitely not `Freeze`. + fn is_not_freeze(self) -> bool { + matches!(self, Self::No) } } -fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) { - let (lint, msg, span) = source.lint(); - span_lint_and_then(cx, lint, span, msg, |diag| { - if span.from_expansion() { - return; // Don't give suggestions into macros. - } - match source { - Source::Item { ty, .. } => { - let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { - return; - }; - if implements_trait(cx, ty, sync_trait, &[]) { - diag.help("consider making this a static item"); - } else { - diag.help( - "consider making this `Sync` so that it can go in a static item or using a `thread_local`", - ); - } - }, - Source::Assoc { .. } => (), - Source::Expr { .. } => { - diag.help("assign this const to a local or static variable, and use the variable here"); +/// What operation caused a borrow to occur. +#[derive(Clone, Copy)] +enum BorrowCause { + Borrow, + Deref, + Index, + AutoDeref, + AutoBorrow, + AutoDerefField, +} +impl BorrowCause { + fn note(self) -> Option<&'static str> { + match self { + Self::Borrow => None, + Self::Deref => Some("this deref expression is a call to `Deref::deref`"), + Self::Index => Some("this index expression is a call to `Index::index`"), + Self::AutoDeref => Some("there is a compiler inserted call to `Deref::deref` here"), + Self::AutoBorrow => Some("there is a compiler inserted borrow here"), + Self::AutoDerefField => { + Some("there is a compiler inserted call to `Deref::deref` when accessing this field") }, } - }); + } +} + +/// The source of a borrow. Both what caused it and where. +struct BorrowSource<'tcx> { + expr: &'tcx Expr<'tcx>, + cause: BorrowCause, +} +impl<'tcx> BorrowSource<'tcx> { + fn new(tcx: TyCtxt<'tcx>, expr: &'tcx Expr<'tcx>, cause: BorrowCause) -> Self { + // Custom deref and index impls will always have an auto-borrow inserted since we + // never work with reference types. + let (expr, cause) = if matches!(cause, BorrowCause::AutoBorrow) + && let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + { + match parent.kind { + ExprKind::Unary(UnOp::Deref, _) => (parent, BorrowCause::Deref), + ExprKind::Index(..) => (parent, BorrowCause::Index), + ExprKind::Field(..) => (parent, BorrowCause::AutoDerefField), + _ => (expr, cause), + } + } else { + (expr, cause) + }; + Self { expr, cause } + } } pub struct NonCopyConst<'tcx> { - interior_mut: InteriorMut<'tcx>, + ignore_tys: DefIdSet, + // Cache checked types. We can recurse through a type multiple times so this + // can be hit quite frequently. + freeze_tys: FxHashMap, IsFreeze>, } impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); @@ -174,332 +261,629 @@ impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTE impl<'tcx> NonCopyConst<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability), + ignore_tys: conf + .ignore_interior_mutability + .iter() + .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty)) + .collect(), + freeze_tys: FxHashMap::default(), } } - fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { - // No branch that we check (yet) should continue if val isn't a branch - let Some(branched_val) = val.try_to_branch() else { - return false; - }; - match *ty.kind() { - // the fact that we have to dig into every structs to search enums - // leads us to the point checking `UnsafeCell` directly is the only option. - ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true, - // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the - // contained value. - ty::Adt(def, ..) if def.is_union() => false, - ty::Array(ty, _) => branched_val - .iter() - .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Adt(def, args) if def.is_enum() => { - let Some((&variant_valtree, fields)) = branched_val.split_first() else { - return false; - }; - let variant_index = variant_valtree.unwrap_leaf(); - let variant_index = VariantIdx::from_u32(variant_index.to_u32()); - fields - .iter() - .copied() - .zip( - def.variants()[variant_index] + /// Checks if a value of the given type is `Freeze`, or may be depending on the value. + fn is_ty_freeze(&mut self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>) -> IsFreeze { + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.freeze_tys.entry(ty) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let e = e.insert(IsFreeze::Yes); + if ty.is_freeze(tcx, typing_env) { + return IsFreeze::Yes; + } + let is_freeze = match *ty.kind() { + ty::Adt(adt, _) if adt.is_unsafe_cell() => { + *e = IsFreeze::No; + return IsFreeze::No; + }, + ty::Adt(adt, _) if self.ignore_tys.contains(&adt.did()) => return IsFreeze::Yes, + ty::Adt(adt, args) if adt.is_enum() => IsFreeze::from_variants(adt.variants().iter().map(|v| { + IsFreeze::from_fields( + v.fields + .iter() + .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))), + ) + })), + // Workaround for `ManuallyDrop`-like unions. + ty::Adt(adt, args) + if adt.is_union() + && adt.non_enum_variant().fields.iter().any(|f| { + tcx.layout_of(typing_env.as_query_input(f.ty(tcx, args))) + .is_ok_and(|l| l.layout.size().bytes() == 0) + }) => + { + return IsFreeze::Yes; + }, + // Rust doesn't have the concept of an active union field so we have + // to treat all fields as active. + ty::Adt(adt, args) => IsFreeze::from_fields( + adt.non_enum_variant() .fields .iter() - .map(|field| field.ty(cx.tcx, args)), - ) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty)) - }, - ty::Adt(def, args) => branched_val - .iter() - .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Tuple(tys) => branched_val - .iter() - .zip(tys) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) { - Ok(normalized_ty) if ty != normalized_ty => Self::is_value_unfrozen_raw_inner(cx, val, normalized_ty), - _ => false, + .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))), + ), + ty::Array(ty, _) | ty::Pat(ty, _) => self.is_ty_freeze(tcx, typing_env, ty), + ty::Tuple(tys) => { + IsFreeze::from_fields(tys.iter().map(|ty| self.is_ty_freeze(tcx, typing_env, ty))) + }, + // Treat type parameters as though they were `Freeze`. + ty::Param(_) | ty::Alias(..) => return IsFreeze::Yes, + // TODO: check other types. + _ => { + *e = IsFreeze::No; + return IsFreeze::No; + }, + }; + if !is_freeze.is_freeze() { + self.freeze_tys.insert(ty, is_freeze); + } + is_freeze }, - _ => false, } } - fn is_value_unfrozen_raw( - cx: &LateContext<'tcx>, - result: Result, Ty<'tcx>>, ErrorHandled>, + /// Checks if the given constant value is `Freeze`. Returns `Err` if the constant + /// cannot be read, but the result depends on the value. + fn is_value_freeze( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>, - ) -> bool { - result.map_or_else( - |err| { - // Consider `TooGeneric` cases as being unfrozen. - // This causes a false positive where an assoc const whose type is unfrozen - // have a value that is a frozen variant with a generic param (an example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`). - // However, it prevents a number of false negatives that is, I think, important: - // 1. assoc consts in trait defs referring to consts of themselves (an example is - // `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`). - // 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait - // defs (i.e. without substitute for `Self`). (e.g. borrowing - // `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`) - // 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no - // enums. (An example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`). - // One might be able to prevent these FNs correctly, and replace this with `false`; - // e.g. implementing `has_frozen_variant` described above, and not running this function - // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd - // case (that actually removes another suboptimal behavior (I won't say 'false positive') where, - // similar to 2., but with a frozen variant) (e.g. borrowing - // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). - // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). - matches!(err, ErrorHandled::TooGeneric(..)) + val: ConstValue<'tcx>, + ) -> Result { + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.is_ty_freeze(tcx, typing_env, ty) { + IsFreeze::Yes => Ok(true), + IsFreeze::Maybe if matches!(ty.kind(), ty::Adt(..) | ty::Array(..) | ty::Tuple(..)) => { + for &(val, ty) in tcx + .try_destructure_mir_constant_for_user_output(val, ty) + .ok_or(())? + .fields + { + if !self.is_value_freeze(tcx, typing_env, ty, val)? { + return Ok(false); + } + } + Ok(true) }, - |val| val.map_or(true, |val| Self::is_value_unfrozen_raw_inner(cx, val, ty)), - ) - } - - fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { - let def_id = body_id.hir_id.owner.to_def_id(); - let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); - let instance = ty::Instance::new_raw(def_id, args); - let cid = GlobalId { - instance, - promoted: None, - }; - let typing_env = ty::TypingEnv::post_analysis(cx.tcx, def_id); - let result = cx.tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP); - Self::is_value_unfrozen_raw(cx, result, ty) - } - - fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { - let args = cx.typeck_results().node_args(hir_id); - - let result = Self::const_eval_resolve( - cx.tcx, - cx.typing_env(), - ty::UnevaluatedConst::new(def_id, args), - DUMMY_SP, - ); - Self::is_value_unfrozen_raw(cx, result, ty) + IsFreeze::Maybe | IsFreeze::No => Ok(false), + } } - pub fn const_eval_resolve( + /// Checks if the given expression creates a value which is `Freeze`. + /// + /// This will return `true` if the type is maybe `Freeze`, but it cannot be + /// determined for certain from the value. + /// + /// `typing_env` and `gen_args` are from the constant's use site. + /// `typeck` and `e` are from the constant's definition site. + fn is_init_expr_freeze( + &mut self, tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ct: ty::UnevaluatedConst<'tcx>, - span: Span, - ) -> EvalToValTreeResult<'tcx> { - match ty::Instance::try_resolve(tcx, typing_env, ct.def, ct.args) { - Ok(Some(instance)) => { - let cid = GlobalId { - instance, - promoted: None, - }; - tcx.const_eval_global_id_for_typeck(typing_env, cid, span) + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + gen_args: GenericArgsRef<'tcx>, + e: &'tcx Expr<'tcx>, + ) -> bool { + // Make sure to instantiate all types coming from `typeck` with `gen_args`. + let ty = EarlyBinder::bind(typeck.expr_ty(e)).instantiate(tcx, gen_args); + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.is_ty_freeze(tcx, typing_env, ty) { + IsFreeze::Yes => true, + IsFreeze::No => false, + IsFreeze::Maybe => match e.kind { + ExprKind::Block(b, _) + if !b.targeted_by_break + && b.stmts.is_empty() + && let Some(e) = b.expr => + { + self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e) + }, + ExprKind::Path(ref p) => { + let res = typeck.qpath_res(p, e.hir_id); + let gen_args = EarlyBinder::bind(typeck.node_args(e.hir_id)).instantiate(tcx, gen_args); + match res { + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Ok(val) = + tcx.const_eval_resolve(typing_env, UnevaluatedConst::new(did, gen_args), DUMMY_SP) + && let Ok(is_freeze) = self.is_value_freeze(tcx, typing_env, ty, val) => + { + is_freeze + }, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Some((typeck, init)) = get_const_hir_value(tcx, typing_env, did, gen_args) => + { + self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, init) + }, + // Either this is a unit constructor, or some unknown value. + // In either case we consider the value to be `Freeze`. + _ => true, + } + }, + ExprKind::Call(callee, args) + if let ExprKind::Path(p) = &callee.kind + && let res = typeck.qpath_res(p, callee.hir_id) + && matches!(res, Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_)) => + { + args.iter() + .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)) + }, + ExprKind::Struct(_, fields, StructTailExpr::None) => fields + .iter() + .all(|f| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, f.expr)), + ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs + .iter() + .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)), + ExprKind::Repeat(e, _) => self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e), + _ => true, }, - Ok(None) => Err(ErrorHandled::TooGeneric(span)), - Err(err) => Err(ErrorHandled::Reported( - ReportedErrorInfo::non_const_eval_error(err), - span, - )), } } -} -impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(.., body_id) = it.kind { - let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); - if !ignored_macro(cx, it) - && self.interior_mut.is_interior_mut_ty(cx, ty) - && Self::is_value_unfrozen_poly(cx, body_id, ty) - { - lint(cx, Source::Item { item: it.span, ty }); + /// Checks if the given expression (or a local projection of it) is both borrowed and + /// definitely a non-`Freeze` type. + fn is_non_freeze_expr_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + ) -> Option> { + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + loop { + let ty = typeck.expr_ty(src_expr); + // Normalized as we need to check if this is an array later. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + let is_freeze = self.is_ty_freeze(tcx, typing_env, ty); + if is_freeze.is_freeze() { + return None; } - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind { - let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity(); - - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound. - let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); - if self.interior_mut.is_interior_mut_ty(cx, normalized) - // When there's no default value, lint it only according to its type; - // in other words, lint consts whose value *could* be unfrozen, not definitely is. - // This feels inconsistent with how the lint treats generic types, - // which avoids linting types which potentially become unfrozen. - // One could check whether an unfrozen type have a *frozen variant* - // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`), - // and do the same as the case of generic types at impl items. - // Note that it isn't sufficient to check if it has an enum - // since all of that enum's variants can be unfrozen: - // i.e. having an enum doesn't necessary mean a type has a frozen variant. - // And, implementing it isn't a trivial task; it'll probably end up - // re-implementing the trait predicate evaluation specific to `Freeze`. - && body_id_opt.is_none_or(|body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized)) - { - lint(cx, Source::Assoc { item: trait_item.span }); + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + return does_adjust_borrow(adjust) + .filter(|_| is_freeze.is_not_freeze()) + .map(|cause| BorrowSource::new(tcx, src_expr, cause)); + } + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return None; + }; + match use_expr.kind { + ExprKind::Field(..) => {}, + ExprKind::Index(..) if ty.is_array() => {}, + ExprKind::AddrOf(..) if is_freeze.is_not_freeze() => { + return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)); + }, + // All other expressions use the value. + _ => return None, } + src_expr = use_expr; } } - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(_, body_id) = &impl_item.kind { - let item_def_id = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir_expect_item(item_def_id); - - match &item.kind { - ItemKind::Impl(Impl { - of_trait: Some(of_trait_ref), - .. - }) => { - if let Some(of_trait_def_id) = of_trait_ref.trait_def_id() - // Lint a trait impl item only when the definition is a generic type, - // assuming an assoc const is not meant to be an interior mutable type. - && let Some(of_assoc_item) = cx - .tcx - .associated_item(impl_item.owner_id) - .trait_item_def_id - && cx - .tcx - .layout_of(ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id).as_query_input( - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound at the trait defs; - // and, in that case, the definition is *not* generic. - cx.tcx.normalize_erasing_regions( - ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id), - cx.tcx.type_of(of_assoc_item).instantiate_identity(), - ), - )) - .is_err() - // If there were a function like `has_frozen_variant` described above, - // we should use here as a frozen variant is a potential to be frozen - // similar to unknown layouts. - // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` - && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity() - && let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty) - && self.interior_mut.is_interior_mut_ty(cx, normalized) - && Self::is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint(cx, Source::Assoc { item: impl_item.span }); + /// Checks if the given value (or a local projection of it) is both borrowed and + /// definitely non-`Freeze`. Returns `Err` if the constant cannot be read, but the + /// result depends on the value. + fn is_non_freeze_val_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + mut val: ConstValue<'tcx>, + ) -> Result>, ()> { + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + let mut ty = typeck.expr_ty(src_expr); + loop { + // Normalized as we need to check if this is an array later. + ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + let res = if let Some(cause) = does_adjust_borrow(adjust) + && !self.is_value_freeze(tcx, typing_env, ty, val)? + { + Some(BorrowSource::new(tcx, src_expr, cause)) + } else { + None + }; + return Ok(res); + } + // Check only the type here as the result gets cached for each type. + if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { + return Ok(None); + } + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return Ok(None); + }; + let next_val = match use_expr.kind { + ExprKind::Field(_, name) => { + if let Some(idx) = get_field_idx_by_name(ty, name.name) { + tcx.try_destructure_mir_constant_for_user_output(val, ty) + .ok_or(())? + .fields + .get(idx) + } else { + return Ok(None); } }, - ItemKind::Impl(Impl { of_trait: None, .. }) => { - let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); - // Normalize assoc types originated from generic params. - let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); - - if self.interior_mut.is_interior_mut_ty(cx, normalized) - && Self::is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint(cx, Source::Assoc { item: impl_item.span }); + ExprKind::Index(_, idx, _) if ty.is_array() => { + let val = tcx.try_destructure_mir_constant_for_user_output(val, ty).ok_or(())?; + if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) { + val.fields.get(idx as usize) + } else { + // It's some value in the array so check all of them. + for &(val, _) in val.fields { + if let Some(src) = + self.is_non_freeze_val_borrowed(tcx, typing_env, typeck, use_expr, val)? + { + return Ok(Some(src)); + } + } + return Ok(None); } }, - _ => (), + ExprKind::AddrOf(..) if !self.is_value_freeze(tcx, typing_env, ty, val)? => { + return Ok(Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow))); + }, + // All other expressions use the value. + _ => return Ok(None), + }; + src_expr = use_expr; + if let Some(&(next_val, next_ty)) = next_val { + ty = next_ty; + val = next_val; + } else { + return Ok(None); } } } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Path(qpath) = &expr.kind { - // Only lint if we use the const item inside a function. - if is_in_const_context(cx) { - return; + /// Checks if the given value (or a local projection of it) is both borrowed and + /// definitely non-`Freeze`. + /// + /// `typing_env` and `init_args` are from the constant's use site. + /// `init_typeck` and `init_expr` are from the constant's definition site. + #[expect(clippy::too_many_arguments, clippy::too_many_lines)] + fn is_non_freeze_init_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + mut init_typeck: &'tcx TypeckResults<'tcx>, + mut init_args: GenericArgsRef<'tcx>, + mut init_expr: &'tcx Expr<'tcx>, + ) -> Option> { + // Make sure to instantiate all types coming from `init_typeck` with `init_args`. + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + loop { + // First handle any adjustments since they are cheap to check. + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + return does_adjust_borrow(adjust) + .filter(|_| !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr)) + .map(|cause| BorrowSource::new(tcx, src_expr, cause)); } - // Make sure it is a const item. - let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else { - return; - }; - - // Climb up to resolve any field access and explicit referencing. - let mut cur_expr = expr; - let mut dereferenced_expr = expr; - let mut needs_check_adjustment = true; + // Then read through constants and blocks on the init expression before + // applying the next use expression. loop { - let parent_id = cx.tcx.parent_hir_id(cur_expr.hir_id); - if parent_id == cur_expr.hir_id { - break; + match init_expr.kind { + ExprKind::Block(b, _) + if !b.targeted_by_break + && b.stmts.is_empty() + && let Some(next_init) = b.expr => + { + init_expr = next_init; + }, + ExprKind::Path(ref init_path) => { + let next_init_args = + EarlyBinder::bind(init_typeck.node_args(init_expr.hir_id)).instantiate(tcx, init_args); + match init_typeck.qpath_res(init_path, init_expr.hir_id) { + Res::Def(DefKind::Ctor(..), _) => return None, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Ok(val) = tcx.const_eval_resolve( + typing_env, + UnevaluatedConst::new(did, next_init_args), + DUMMY_SP, + ) && let Ok(res) = + self.is_non_freeze_val_borrowed(tcx, typing_env, init_typeck, src_expr, val) => + { + return res; + }, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Some((next_typeck, value)) = + get_const_hir_value(tcx, typing_env, did, next_init_args) => + { + init_typeck = next_typeck; + init_args = next_init_args; + init_expr = value; + }, + // There's no more that we can read from the init expression. Switch to a + // type based check. + _ => { + return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, src_expr); + }, + } + }, + _ => break, } - if let Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) { - match &parent_expr.kind { - ExprKind::AddrOf(..) => { - // `&e` => `e` must be referenced. - needs_check_adjustment = false; - }, - ExprKind::Field(..) => { - needs_check_adjustment = true; + } - // Check whether implicit dereferences happened; - // if so, no need to go further up - // because of the same reason as the `ExprKind::Unary` case. - if cx - .typeck_results() - .expr_adjustments(dereferenced_expr) - .iter() - .any(|adj| matches!(adj.kind, Adjust::Deref(_))) - { - break; - } + // Then a type check. Note we only check the type here as the result + // gets cached. + let ty = EarlyBinder::bind(typeck.expr_ty(src_expr)).instantiate(tcx, init_args); + // Normalized as we need to check if this is an array later. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { + return None; + } - dereferenced_expr = parent_expr; - }, - ExprKind::Index(e, _, _) if ptr::eq(&raw const **e, cur_expr) => { - // `e[i]` => desugared to `*Index::index(&e, i)`, - // meaning `e` must be referenced. - // no need to go further up since a method call is involved now. - needs_check_adjustment = false; - break; - }, - ExprKind::Unary(UnOp::Deref, _) => { - // `*e` => desugared to `*Deref::deref(&e)`, - // meaning `e` must be referenced. - // no need to go further up since a method call is involved now. - needs_check_adjustment = false; - break; + // Finally reduce the init expression using the next use expression. + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return None; + }; + init_expr = match &use_expr.kind { + ExprKind::Field(_, name) => match init_expr.kind { + ExprKind::Struct(_, fields, _) + if let Some(field) = fields.iter().find(|f| f.ident.name == name.name) => + { + field.expr + }, + ExprKind::Tup(fields) + if let Ok(idx) = name.as_str().parse::() + && let Some(field) = fields.get(idx) => + { + field + }, + ExprKind::Call(callee, args) + if let ExprKind::Path(callee_path) = &callee.kind + && matches!( + init_typeck.qpath_res(callee_path, callee.hir_id), + Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_) + ) + && let Ok(idx) = name.as_str().parse::() + && let Some(arg) = args.get(idx) => + { + arg + }, + // Revert to a type based check as we don't know the field's value. + _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr), + }, + ExprKind::Index(_, idx, _) if ty.is_array() => match init_expr.kind { + ExprKind::Array(fields) => { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) { + // If the index is out of bounds it means the code + // unconditionally panics. In that case there is no borrow. + fields.get(idx as usize)? + } else { + // Unknown index, just run the check for all values. + return fields.iter().find_map(|f| { + self.is_non_freeze_init_borrowed( + tcx, + typing_env, + typeck, + use_expr, + init_typeck, + init_args, + f, + ) + }); + } + }, + // Just assume the index expression doesn't panic here. + ExprKind::Repeat(field, _) => field, + _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr), + }, + ExprKind::AddrOf(..) + if !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr) => + { + return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)); + }, + // All other expressions use the value. + _ => return None, + }; + src_expr = use_expr; + } + } +} + +impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Const(ident, .., body_id) = item.kind + && !ident.is_special() + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::No => true, + IsFreeze::Yes => false, + IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), + }, + } + && !item.span.in_external_macro(cx.sess().source_map()) + // Only needed when compiling `std` + && !is_thread_local(cx, item) + { + span_lint_and_then( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + ident.span, + "named constant with interior mutability", + |diag| { + let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { + return; + }; + if implements_trait(cx, ty, sync_trait, &[]) { + diag.help("did you mean to make this a `static` item"); + } else { + diag.help("did you mean to make this a `thread_local!` item"); + } + }, + ); + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Const(_, body_id_opt) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::No => true, + IsFreeze::Maybe if let Some(body_id) = body_id_opt => { + match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => { + !is_freeze }, - _ => break, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), } - cur_expr = parent_expr; - } else { - break; - } + }, + IsFreeze::Yes | IsFreeze::Maybe => false, } + && !item.span.in_external_macro(cx.sess().source_map()) + { + span_lint( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + item.ident.span, + "named constant with interior mutability", + ); + } + } - let ty = if needs_check_adjustment { - let adjustments = cx.typeck_results().expr_adjustments(dereferenced_expr); - if let Some(i) = adjustments - .iter() - .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_))) - { - if i == 0 { - cx.typeck_results().expr_ty(dereferenced_expr) + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Const(_, body_id) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::Yes => false, + IsFreeze::No => { + // If this is a trait impl, check if the trait definition is the source + // of the cell. + if let Node::Item(parent_item) = cx.tcx.parent_hir_node(item.hir_id()) + && let ItemKind::Impl(impl_block) = parent_item.kind + && let Some(of_trait) = impl_block.of_trait + && let Some(trait_id) = of_trait.trait_def_id() + { + // Replace all instances of `::AssocType` with the + // unit type and check again. If the result is the same then the + // trait definition is the cause. + let ty = (ReplaceAssocFolder { + tcx: cx.tcx, + trait_id, + self_ty: cx.tcx.type_of(parent_item.owner_id).instantiate_identity(), + }) + .fold_ty(cx.tcx.type_of(item.owner_id).instantiate_identity()); + // `ty` may not be normalizable, but that should be fine. + !self.is_ty_freeze(cx.tcx, cx.typing_env(), ty).is_not_freeze() } else { - adjustments[i - 1].target + true } + }, + // Even if this is from a trait, there are values which don't have + // interior mutability. + IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), + }, + } + && !item.span.in_external_macro(cx.sess().source_map()) + { + span_lint( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + item.ident.span, + "named constant with interior mutability", + ); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Path(qpath) = &e.kind + && let typeck = cx.typeck_results() + && let Res::Def(DefKind::Const | DefKind::AssocConst, did) = typeck.qpath_res(qpath, e.hir_id) + // As of `1.80` constant contexts can't borrow any type with interior mutability + && !is_in_const_context(cx) + && !self.is_ty_freeze(cx.tcx, cx.typing_env(), typeck.expr_ty(e)).is_freeze() + && let Some(borrow_src) = { + // The extra block helps formatting a lot. + if let Ok(val) = cx.tcx.const_eval_resolve( + cx.typing_env(), + UnevaluatedConst::new(did, typeck.node_args(e.hir_id)), + DUMMY_SP, + ) && let Ok(src) = self.is_non_freeze_val_borrowed(cx.tcx, cx.typing_env(), typeck, e, val) + { + src + } else if let init_args = typeck.node_args(e.hir_id) + && let Some((init_typeck, init)) = get_const_hir_value(cx.tcx, cx.typing_env(), did, init_args) + { + self.is_non_freeze_init_borrowed(cx.tcx, cx.typing_env(), typeck, e, init_typeck, init_args, init) } else { - // No borrow adjustments means the entire const is moved. - return; + self.is_non_freeze_expr_borrowed(cx.tcx, cx.typing_env(), typeck, e) } - } else { - cx.typeck_results().expr_ty(dereferenced_expr) - }; - - if self.interior_mut.is_interior_mut_ty(cx, ty) - && Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) - { - lint(cx, Source::Expr { expr: expr.span }); } + && !borrow_src.expr.span.in_external_macro(cx.sess().source_map()) + { + span_lint_and_then( + cx, + BORROW_INTERIOR_MUTABLE_CONST, + borrow_src.expr.span, + "borrow of a named constant with interior mutability", + |diag| { + if let Some(note) = borrow_src.cause.note() { + diag.note(note); + } + diag.help("this lint can be silenced by assigning the value to a local variable before borrowing"); + }, + ); + } + } +} + +struct ReplaceAssocFolder<'tcx> { + tcx: TyCtxt<'tcx>, + trait_id: DefId, + self_ty: Ty<'tcx>, +} +impl<'tcx> TypeFolder> for ReplaceAssocFolder<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(AliasTyKind::Projection, ty) = ty.kind() + && ty.trait_def_id(self.tcx) == self.trait_id + && ty.self_ty() == self.self_ty + { + self.tcx.types.unit + } else { + ty.super_fold_with(self) } } } -fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool { +fn is_thread_local(cx: &LateContext<'_>, it: &Item<'_>) -> bool { macro_backtrace(it.span).any(|macro_call| { matches!( cx.tcx.get_diagnostic_name(macro_call.def_id), @@ -507,3 +891,42 @@ fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool { ) }) } + +/// Checks if the adjustment causes a borrow of the original value. Returns +/// `None` if the value is consumed instead of borrowed. +fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option { + match adjust.kind { + Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow), + // Custom deref calls `::deref(&x)` resulting in a borrow. + Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref), + // All other adjustments read the value. + _ => None, + } +} + +/// Attempts to get the value of a constant as a HIR expression. Also gets the +/// `TypeckResults` associated with the constant's body. +fn get_const_hir_value<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + did: DefId, + args: GenericArgsRef<'tcx>, +) -> Option<(&'tcx TypeckResults<'tcx>, &'tcx Expr<'tcx>)> { + let did = did.as_local()?; + let (did, body_id) = match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) { + Node::Item(item) if let ItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::TraitItem(_) + if let Ok(Some(inst)) = Instance::try_resolve(tcx, typing_env, did.into(), args) + && let Some(did) = inst.def_id().as_local() => + { + match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) { + Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::TraitItem(item) if let TraitItemKind::Const(.., Some(body_id)) = item.kind => (did, body_id), + _ => return None, + } + }, + _ => return None, + }; + Some((tcx.typeck(did), tcx.hir_body(body_id).value)) +} diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 03b907ebdf4d8..4be42267b14b3 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( let rty = cx.typeck_results().expr_ty(rhs); if let Some((_, lang_item)) = binop_traits(op.node) && let Some(trait_id) = cx.tcx.lang_items().get(lang_item) - && let parent_fn = cx.tcx.hir_get_parent_item(e.hir_id).def_id + && let parent_fn = cx.tcx.hir_get_parent_item(e.hir_id) && trait_ref_of_method(cx, parent_fn).is_none_or(|t| t.path.res.def_id() != trait_id) && implements_trait(cx, ty, trait_id, &[rty.into()]) { diff --git a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs index e3029f8438e5f..6c9be7c5e90b3 100644 --- a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs @@ -1,11 +1,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::sym; use super::DURATION_SUBSEC; @@ -21,9 +21,9 @@ pub(crate) fn check<'tcx>( && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) { - let suggested_fn = match (method_path.ident.as_str(), divisor) { - ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", - ("subsec_nanos", 1_000) => "subsec_micros", + let suggested_fn = match (method_path.ident.name, divisor) { + (sym::subsec_micros, 1_000) | (sym::subsec_nanos, 1_000_000) => "subsec_millis", + (sym::subsec_nanos, 1_000) => "subsec_micros", _ => return, }; let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs index d16f5f8e112c3..64ad92b1ebb50 100644 --- a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs @@ -37,7 +37,7 @@ impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind && matches!(seg.ident.name, sym::expect | sym::unwrap) - && is_direct_expn_of(receiver.span, "option_env").is_some() + && is_direct_expn_of(receiver.span, sym::option_env).is_some() { span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 122d97fdf8193..ab9b0f88f936c 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -423,7 +423,14 @@ fn check_final_expr<'tcx>( _ => return, } - emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id); + emit_return_lint( + cx, + peeled_drop_expr.span, + ret_span, + semi_spans, + &replacement, + expr.hir_id, + ); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); @@ -448,6 +455,7 @@ fn check_final_expr<'tcx>( fn emit_return_lint( cx: &LateContext<'_>, + lint_span: Span, ret_span: Span, semi_spans: Vec, replacement: &RetReplacement<'_>, @@ -457,7 +465,7 @@ fn emit_return_lint( cx, NEEDLESS_RETURN, at, - ret_span, + lint_span, "unneeded `return` statement", |diag| { let suggestions = std::iter::once((ret_span, replacement.to_string())) diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index ccb1209c6fcbe..f3fea3add592d 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary}; +use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Ident; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, Span}; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -169,7 +169,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { let mut iter = get_attr( self.cx.sess(), self.cx.tcx.get_attrs_unchecked(adt.did()), - "has_significant_drop", + sym::has_significant_drop, ); if iter.next().is_some() { return true; diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index 835ec1e4ca1c7..3623039aece15 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::sym; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -62,17 +63,17 @@ fn get_pointee_ty_and_count_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { - const METHODS: [&str; 10] = [ - "copy_to", - "copy_from", - "copy_to_nonoverlapping", - "copy_from_nonoverlapping", - "add", - "wrapping_add", - "sub", - "wrapping_sub", - "offset", - "wrapping_offset", + const METHODS: [Symbol; 10] = [ + sym::copy_to, + sym::copy_from, + sym::copy_to_nonoverlapping, + sym::copy_from_nonoverlapping, + sym::add, + sym::wrapping_add, + sym::sub, + sym::wrapping_sub, + sym::offset, + sym::wrapping_offset, ]; if let ExprKind::Call(func, [.., count]) = expr.kind @@ -97,7 +98,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( } if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind // Find calls to copy_{from,to}{,_nonoverlapping} - && let method_ident = method_path.ident.as_str() + && let method_ident = method_path.ident.name && METHODS.contains(&method_ident) // Get the pointee type diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 30a5fe4db27e1..f497d0700b8eb 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -266,7 +266,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { let is_matching_resize = if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // If we have a size expression, check that it is equal to what's passed to `resize` SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) - || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity") + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity) } else { self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); true @@ -288,7 +288,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // Check that len expression is equals to `with_capacity` expression return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) - || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity"); + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity); } self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index 5c95dfe834730..f63e6b3087b9e 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -5,9 +5,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::path_to_local_id; use clippy_utils::source::{snippet, str_literal_to_char_literal}; use clippy_utils::visitors::{Descend, for_each_expr}; +use clippy_utils::{path_to_local_id, sym}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; @@ -15,7 +15,7 @@ use rustc_hir::{Expr, ExprKind, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -83,29 +83,29 @@ impl StringPatterns { impl_lint_pass!(StringPatterns => [MANUAL_PATTERN_CHAR_COMPARISON, SINGLE_CHAR_PATTERN]); -const PATTERN_METHODS: [(&str, usize); 22] = [ - ("contains", 0), - ("starts_with", 0), - ("ends_with", 0), - ("find", 0), - ("rfind", 0), - ("split", 0), - ("split_inclusive", 0), - ("rsplit", 0), - ("split_terminator", 0), - ("rsplit_terminator", 0), - ("splitn", 1), - ("rsplitn", 1), - ("split_once", 0), - ("rsplit_once", 0), - ("matches", 0), - ("rmatches", 0), - ("match_indices", 0), - ("rmatch_indices", 0), - ("trim_start_matches", 0), - ("trim_end_matches", 0), - ("replace", 0), - ("replacen", 0), +const PATTERN_METHODS: [(Symbol, usize); 22] = [ + (sym::contains, 0), + (sym::starts_with, 0), + (sym::ends_with, 0), + (sym::find, 0), + (sym::rfind, 0), + (sym::split, 0), + (sym::split_inclusive, 0), + (sym::rsplit, 0), + (sym::split_terminator, 0), + (sym::rsplit_terminator, 0), + (sym::splitn, 1), + (sym::rsplitn, 1), + (sym::split_once, 0), + (sym::rsplit_once, 0), + (sym::matches, 0), + (sym::rmatches, 0), + (sym::match_indices, 0), + (sym::rmatch_indices, 0), + (sym::trim_start_matches, 0), + (sym::trim_end_matches, 0), + (sym::replace, 0), + (sym::replacen, 0), ]; fn check_single_char_pattern_lint(cx: &LateContext<'_>, arg: &Expr<'_>) { @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for StringPatterns { && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() && ty.is_str() - && let method_name = method.ident.name.as_str() + && let method_name = method.ident.name && let Some(&(_, pos)) = PATTERN_METHODS .iter() .find(|(array_method_name, _)| *array_method_name == method_name) diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs index 1ada7094dc550..33856c750d7e9 100644 --- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs +++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_libc_symbol; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::is_expr_unsafe; +use clippy_utils::{match_libc_symbol, sym}; use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -44,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { && let ExprKind::Call(func, [recv]) = expr.kind && let ExprKind::Path(path) = &func.kind && let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id() - && match_libc_symbol(cx, did, "strlen") + && match_libc_symbol(cx, did, sym::strlen) && let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind && !recv.span.from_expansion() && path.ident.name == sym::as_ptr diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index 83241f97a99ac..edb7600b7c067 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -80,7 +80,7 @@ fn check_expr_inner<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, bin && let hir::Node::ImplItem(impl_item) = cx.tcx.hir_node_by_def_id(parent_fn) && let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind && let body = cx.tcx.hir_body(body_id) - && let parent_fn = cx.tcx.hir_get_parent_item(expr.hir_id).def_id + && let parent_fn = cx.tcx.hir_get_parent_item(expr.hir_id) && let Some(trait_ref) = trait_ref_of_method(cx, parent_fn) && let trait_id = trait_ref.path.res.def_id() && ![binop_trait_id, op_assign_trait_id].contains(&trait_id) diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index bcd05cceca9c3..54a7efc090ade 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; @@ -78,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { fn_kind: FnKind<'tcx>, fn_decl: &FnDecl<'tcx>, body: &Body<'tcx>, - span: Span, + _span: Span, def_id: LocalDefId, ) { // Abort if public function/method or closure. @@ -147,19 +149,22 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { "remove the return type...".to_string(), // FIXME: we should instead get the span including the `->` and suggest an // empty string for this case. - "()".to_string(), - "...and then remove returned values", + Cow::Borrowed("()"), + Cow::Borrowed("...and then remove returned values"), ) } else { + let wrapper = if lang_item == OptionSome { "Some" } else { "Ok" }; ( format!("this function's return value is unnecessarily wrapped by `{return_type_label}`"), format!("remove `{return_type_label}` from the return type..."), - inner_type.to_string(), - "...and then change returning expressions", + Cow::Owned(inner_type.to_string()), + Cow::Owned(format!( + "...and then remove the surrounding `{wrapper}()` from returning expressions" + )), ) }; - span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg, |diag| { + span_lint_and_then(cx, UNNECESSARY_WRAPS, cx.tcx.def_span(def_id), lint_msg, |diag| { diag.span_suggestion( fn_decl.output.span(), return_type_sugg_msg, diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs index 7487e273caa7e..1f5351e32aacf 100644 --- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators}; +use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::ops::ControlFlow; declare_clippy_lint! { @@ -150,10 +149,10 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { remaining_args, _, ) => { - let method_name = method_name_ident.name.as_str(); + let method_name = method_name_ident.name; // `Peekable` methods - if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq") + if matches!(method_name, sym::peek | sym::peek_mut | sym::next_if | sym::next_if_eq) && arg_is_mut_peekable(self.cx, self_arg) { return ControlFlow::Break(()); @@ -167,7 +166,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { } // foo.by_ref(), keep checking for `peek` - if method_name == "by_ref" { + if method_name == sym::by_ref { continue; } diff --git a/src/tools/clippy/clippy_lints/src/unused_rounding.rs b/src/tools/clippy/clippy_lints/src/unused_rounding.rs index 3e5afec541c49..c21004b5362dd 100644 --- a/src/tools/clippy/clippy_lints/src/unused_rounding.rs +++ b/src/tools/clippy/clippy_lints/src/unused_rounding.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -30,19 +32,20 @@ declare_clippy_lint! { } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); -fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> { +fn is_useless_rounding(cx: &EarlyContext<'_>, expr: &Expr) -> Option<(Symbol, String)> { if let ExprKind::MethodCall(box MethodCall { seg: name_ident, receiver, .. }) = &expr.kind - && let method_name = name_ident.ident.name.as_str() - && (method_name == "ceil" || method_name == "round" || method_name == "floor") + && let method_name = name_ident.ident.name + && matches!(method_name, sym::ceil | sym::floor | sym::round) && let ExprKind::Lit(token_lit) = &receiver.kind && token_lit.is_semantic_float() && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::() + && f.fract() == 0.0 { - (f.fract() == 0.0).then(|| (method_name, snippet(cx, receiver.span, "..").to_string())) + Some((method_name, snippet(cx, receiver.span, "..").into())) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index f870eb71e19b9..7bec212a23ca0 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -79,7 +79,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc let mut result = Vec::new(); let _: Option = for_each_expr(cx, body.value, |e| { // check for `expect` - if let Some(arglists) = method_chain_args(e, &["expect"]) { + if let Some(arglists) = method_chain_args(e, &[sym::expect]) { let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); if is_type_diagnostic_item(cx, receiver_ty, sym::Option) || is_type_diagnostic_item(cx, receiver_ty, sym::Result) @@ -89,7 +89,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc } // check for `unwrap` - if let Some(arglists) = method_chain_args(e, &["unwrap"]) { + if let Some(arglists) = method_chain_args(e, &[sym::unwrap]) { let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); if is_type_diagnostic_item(cx, receiver_ty, sym::Option) || is_type_diagnostic_item(cx, receiver_ty, sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/useless_concat.rs b/src/tools/clippy/clippy_lints/src/useless_concat.rs new file mode 100644 index 0000000000000..1ed1fbb3b9c69 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/useless_concat.rs @@ -0,0 +1,104 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::macros::macro_backtrace; +use clippy_utils::paths::CONCAT; +use clippy_utils::source::snippet_opt; +use clippy_utils::tokenize_with_text; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lexer::TokenKind; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks that the `concat!` macro has at least two arguments. + /// + /// ### Why is this bad? + /// If there are less than 2 arguments, then calling the macro is doing nothing. + /// + /// ### Example + /// ```no_run + /// let x = concat!("a"); + /// ``` + /// Use instead: + /// ```no_run + /// let x = "a"; + /// ``` + #[clippy::version = "1.89.0"] + pub USELESS_CONCAT, + complexity, + "checks that the `concat` macro has at least two arguments" +} + +declare_lint_pass!(UselessConcat => [USELESS_CONCAT]); + +impl LateLintPass<'_> for UselessConcat { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + // Check that the expression is generated by a macro. + if expr.span.from_expansion() + // Check that it's a string literal. + && let ExprKind::Lit(lit) = expr.kind + && let LitKind::Str(lit_s, _) = lit.node + // Get the direct parent of the expression. + && let Some(macro_call) = macro_backtrace(expr.span).next() + // Check if the `concat` macro from the `core` library. + && CONCAT.matches(cx, macro_call.def_id) + // We get the original code to parse it. + && let Some(original_code) = snippet_opt(cx, macro_call.span) + // This check allows us to ensure that the code snippet: + // 1. Doesn't come from proc-macro expansion. + // 2. Doesn't come from foreign macro expansion. + // + // It works as follows: if the snippet we get doesn't contain `concat!(`, then it + // means it's not code written in the current crate so we shouldn't lint. + && let mut parts = original_code.split('!') + && parts.next().is_some_and(|p| p.trim() == "concat") + && parts.next().is_some_and(|p| p.trim().starts_with('(')) + { + let mut literal = None; + let mut nb_commas = 0; + let mut nb_idents = 0; + for (token_kind, token_s, _) in tokenize_with_text(&original_code) { + match token_kind { + TokenKind::Eof => break, + TokenKind::Literal { .. } => { + if literal.is_some() { + return; + } + literal = Some(token_s); + }, + TokenKind::Ident => { + if token_s == "true" || token_s == "false" { + literal = Some(token_s); + } else { + nb_idents += 1; + } + }, + TokenKind::Comma => { + nb_commas += 1; + if nb_commas > 1 { + return; + } + }, + // We're inside a macro definition and we are manipulating something we likely + // shouldn't, so aborting. + TokenKind::Dollar => return, + _ => {}, + } + } + // There should always be the ident of the `concat` macro. + if nb_idents == 1 { + span_lint_and_sugg( + cx, + USELESS_CONCAT, + macro_call.span, + "unneeded use of `concat!` macro", + "replace with", + format!("{lit_s:?}"), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 3a9c997a579d1..380ddea4e1e83 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -3,11 +3,11 @@ use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ - get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, + get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; @@ -15,7 +15,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; declare_clippy_lint! { @@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if is_trait_method(cx, e, sym::Into) && name.ident.as_str() == "into" { + if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_and_consts(a, b) { @@ -298,6 +298,33 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // implements Copy, in which case .into_iter() returns a copy of the receiver and // cannot be safely omitted. if same_type_and_consts(a, b) && !is_copy(cx, b) { + // Below we check if the parent method call meets the following conditions: + // 1. First parameter is `&mut self` (requires mutable reference) + // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any) + // For methods satisfying these conditions (like any), .into_iter() must be preserved. + if let Some(parent) = get_parent_expr(cx, e) + && let ExprKind::MethodCall(_, recv, _, _) = parent.kind + && recv.hir_id == e.hir_id + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && let sig = cx.tcx.fn_sig(def_id).skip_binder().skip_binder() + && let inputs = sig.inputs() + && inputs.len() >= 2 + && let Some(self_ty) = inputs.first() + && let ty::Ref(_, _, Mutability::Mut) = self_ty.kind() + && let Some(second_ty) = inputs.get(1) + && let predicates = cx.tcx.param_env(def_id).caller_bounds() + && predicates.iter().any(|pred| { + if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() { + trait_pred.self_ty() == *second_ty + && cx.tcx.lang_items().fn_mut_trait() == Some(trait_pred.def_id()) + } else { + false + } + }) + { + return; + } + let sugg = snippet(cx, recv.span, "").into_owned(); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 812c4df4ddde5..3a08531cf1c9c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -1,4 +1,4 @@ -use clippy_utils::{MaybePath, get_attr, higher, path_def_id}; +use clippy_utils::{MaybePath, get_attr, higher, path_def_id, sym}; use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; @@ -826,5 +826,5 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); - get_attr(cx.sess(), attrs, "author").count() > 0 + get_attr(cx.sess(), attrs, sym::author).count() > 0 } diff --git a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs index 9910be9bc2853..d6cf07fdaf3f8 100644 --- a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs +++ b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs @@ -1,4 +1,4 @@ -use clippy_utils::get_attr; +use clippy_utils::{get_attr, sym}; use hir::TraitItem; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -60,5 +60,5 @@ impl<'tcx> LateLintPass<'tcx> for DumpHir { fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); - get_attr(cx.sess(), attrs, "dump").count() > 0 + get_attr(cx.sess(), attrs, sym::dump).count() > 0 } diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 3346b15dae9ca..7b6a25123e85e 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -8,14 +8,14 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment}; +use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; -use rustc_span::{DesugaringKind, Span, sym}; +use rustc_span::{DesugaringKind, Span}; pub struct UselessVec { too_large_for_stack: u64, @@ -249,10 +249,8 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// that also exists on slices. If this returns true, it means that /// this expression does not actually require a `Vec` and could just work with an array. pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - const ALLOWED_METHOD_NAMES: &[&str] = &["len", "as_ptr", "is_empty"]; - if let ExprKind::MethodCall(path, _, [], _) = e.kind { - ALLOWED_METHOD_NAMES.contains(&path.ident.name.as_str()) + matches!(path.ident.name, sym::as_ptr | sym::is_empty | sym::len) } else { is_trait_method(cx, e, sym::IntoIterator) } diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs index 24b1381ba458c..1550872bca2b9 100644 --- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs +++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs @@ -51,9 +51,11 @@ impl LateLintPass<'_> for ZeroSizedMapValues { && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) && let ty::Adt(_, args) = ty.kind() && let ty = args.type_at(1) - // Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of - // https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968 - && !ty.has_escaping_bound_vars() + // Ensure that no type information is missing, to avoid a delayed bug in the compiler if this is not the case. + // This might happen when computing a reference/pointer metadata on a type for which we + // cannot check if it is `Sized` or not, such as an incomplete associated type in a + // type alias. See an example in `issue14822()` of `tests/ui/zero_sized_hashmap_values.rs`. + && !ty.has_non_region_param() && let Ok(layout) = cx.layout_of(ty) && layout.is_zst() { diff --git a/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs index 4fd5ea459a554..311f76fee6b8b 100644 --- a/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs +++ b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs @@ -3,8 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use regex::Regex; use rustc_hir::{Attribute, Item, ItemKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; -use rustc_session::impl_lint_pass; +use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_tool_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs index 407deb45db0a7..7c9e7286925ef 100644 --- a/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs @@ -4,8 +4,7 @@ use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use std::borrow::{Borrow, Cow}; diff --git a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs index 655d8fb8d1bfb..0edeef3ab8559 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs @@ -10,9 +10,8 @@ use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; use rustc_middle::hir::nested_filter; -use rustc_session::impl_lint_pass; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; diff --git a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs index d48d8dc57b240..441c688485236 100644 --- a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs @@ -4,9 +4,8 @@ use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_lint_defs::declare_tool_lint; use rustc_middle::ty::{self, EarlyBinder, GenericArgKind}; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_tool_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs index 40951443a48a9..5cdc66ae90536 100644 --- a/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs @@ -1,12 +1,11 @@ use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_lint_allowed, method_calls}; +use clippy_utils::{is_lint_allowed, method_calls, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; -use rustc_span::symbol::Symbol; declare_tool_lint! { /// ### What it does @@ -40,8 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { } let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if let ["expn_data", "outer_expn"] = method_names.as_slice() + if let [sym::expn_data, sym::outer_expn] = method_names.as_slice() && let (self_arg, args) = arg_lists[1] && args.is_empty() && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs() diff --git a/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs index 14e93dc6d5f13..3a813b4b9a223 100644 --- a/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs +++ b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs @@ -1,8 +1,7 @@ use rustc_ast::ast::NodeId; use rustc_ast::visit::FnKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_lint_defs::declare_tool_lint; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; declare_tool_lint! { diff --git a/src/tools/clippy/clippy_lints_internal/src/symbols.rs b/src/tools/clippy/clippy_lints_internal/src/symbols.rs index 5aee545fb0f5b..7b5d58824c38f 100644 --- a/src/tools/clippy/clippy_lints_internal/src/symbols.rs +++ b/src/tools/clippy/clippy_lints_internal/src/symbols.rs @@ -6,10 +6,9 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Lit, Node, Pat, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; use rustc_middle::mir::ConstValue; use rustc_middle::ty; -use rustc_session::impl_lint_pass; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; diff --git a/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs b/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs index 8e281ecb2ee44..9ca4ae31d455c 100644 --- a/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs +++ b/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::sym; use rustc_ast::ast::{Crate, ItemKind, ModKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_lint_defs::declare_tool_lint; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_tool_lint! { /// ### What it does @@ -26,11 +26,11 @@ impl EarlyLintPass for UnsortedClippyUtilsPaths { if let Some(utils) = krate .items .iter() - .find(|item| item.kind.ident().is_some_and(|i| i.name.as_str() == "utils")) + .find(|item| item.kind.ident().is_some_and(|i| i.name == sym::utils)) && let ItemKind::Mod(_, _, ModKind::Loaded(ref items, ..)) = utils.kind && let Some(paths) = items .iter() - .find(|item| item.kind.ident().is_some_and(|i| i.name.as_str() == "paths")) + .find(|item| item.kind.ident().is_some_and(|i| i.name == sym::paths)) && let ItemKind::Mod(_, _, ModKind::Loaded(ref items, ..)) = paths.kind { let mut last_name: Option = None; diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index ac970e1c4b0a5..615c0995e8b17 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_utils" -# begin autogenerated version version = "0.1.89" -# end autogenerated version edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index d4080d06d3ca8..c9083f654c4cb 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-05-14 +nightly-2025-05-21 ``` diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 09de5c055379f..8a0ff5323c98d 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -5,11 +5,11 @@ use rustc_lexer::TokenKind; use rustc_lint::LateContext; use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use std::str::FromStr; use crate::source::SpanRangeExt; -use crate::tokenize_with_text; +use crate::{sym, tokenize_with_text}; /// Deprecation status of attributes known by Clippy. pub enum DeprecationStatus { @@ -21,17 +21,17 @@ pub enum DeprecationStatus { } #[rustfmt::skip] -pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ - ("author", DeprecationStatus::None), - ("version", DeprecationStatus::None), - ("cognitive_complexity", DeprecationStatus::None), - ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")), - ("dump", DeprecationStatus::None), - ("msrv", DeprecationStatus::None), +pub const BUILTIN_ATTRIBUTES: &[(Symbol, DeprecationStatus)] = &[ + (sym::author, DeprecationStatus::None), + (sym::version, DeprecationStatus::None), + (sym::cognitive_complexity, DeprecationStatus::None), + (sym::cyclomatic_complexity, DeprecationStatus::Replaced("cognitive_complexity")), + (sym::dump, DeprecationStatus::None), + (sym::msrv, DeprecationStatus::None), // The following attributes are for the 3rd party crate authors. // See book/src/attribs.md - ("has_significant_drop", DeprecationStatus::None), - ("format_args", DeprecationStatus::None), + (sym::has_significant_drop, DeprecationStatus::None), + (sym::format_args, DeprecationStatus::None), ]; pub struct LimitStack { @@ -52,11 +52,11 @@ impl LimitStack { pub fn limit(&self) -> u64 { *self.stack.last().expect("there should always be a value in the stack") } - pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: &'static str) { + pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { let stack = &mut self.stack; parse_attrs(sess, attrs, name, |val| stack.push(val)); } - pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: &'static str) { + pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { let stack = &mut self.stack; parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val))); } @@ -65,7 +65,7 @@ impl LimitStack { pub fn get_attr<'a, A: AttributeExt + 'a>( sess: &'a Session, attrs: &'a [A], - name: &'static str, + name: Symbol, ) -> impl Iterator { attrs.iter().filter(move |attr| { let Some(attr_segments) = attr.ident_path() else { @@ -75,8 +75,8 @@ pub fn get_attr<'a, A: AttributeExt + 'a>( if attr_segments.len() == 2 && attr_segments[0].name == sym::clippy { BUILTIN_ATTRIBUTES .iter() - .find_map(|&(builtin_name, ref deprecation_status)| { - if attr_segments[1].name.as_str() == builtin_name { + .find_map(|(builtin_name, deprecation_status)| { + if attr_segments[1].name == *builtin_name { Some(deprecation_status) } else { None @@ -108,7 +108,7 @@ pub fn get_attr<'a, A: AttributeExt + 'a>( }, DeprecationStatus::None => { diag.cancel(); - attr_segments[1].as_str() == name + attr_segments[1].name == name }, } }, @@ -119,9 +119,9 @@ pub fn get_attr<'a, A: AttributeExt + 'a>( }) } -fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: &'static str, mut f: F) { +fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) { for attr in get_attr(sess, attrs, name) { - if let Some(ref value) = attr.value_str() { + if let Some(value) = attr.value_str() { if let Ok(value) = FromStr::from_str(value.as_str()) { f(value); } else { @@ -133,7 +133,7 @@ fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: } } -pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: &'static str) -> Option<&'a A> { +pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> { let mut unique_attr: Option<&A> = None; for attr in get_attr(sess, attrs, name) { if let Some(duplicate) = unique_attr { diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index b9928b8eed497..6f5b0ec54cd85 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -487,7 +487,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, "cfg").is_some() { + if is_direct_expn_of(e.span, sym::cfg).is_some() { None } else { Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) @@ -565,7 +565,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { }) }, ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, "cfg").is_some() { + if is_direct_expn_of(e.span, sym::cfg).is_some() { None } else { match &lit.node { @@ -654,7 +654,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { span, .. }) = self.tcx.hir_node(body_id.hir_id) - && is_direct_expn_of(*span, "cfg").is_some() + && is_direct_expn_of(*span, sym::cfg).is_some() { return None; } diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 4543a20cc2cd4..9d38672efada9 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -10,6 +10,7 @@ //! - option-if-let-else use crate::consts::{ConstEvalCtxt, FullInt}; +use crate::sym; use crate::ty::{all_predicates_of, is_copy}; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; @@ -19,7 +20,7 @@ use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use std::{cmp, ops}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -49,14 +50,13 @@ impl ops::BitOrAssign for EagernessSuggestion { /// Determine the eagerness of the given function call. fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; - let name = name.as_str(); let ty = match cx.tcx.impl_of_method(fn_id) { Some(id) => cx.tcx.type_of(id).instantiate_identity(), None => return Lazy, }; - if (name.starts_with("as_") || name == "len" || name == "is_empty") && have_one_arg { + if (matches!(name, sym::is_empty | sym::len) || name.as_str().starts_with("as_")) && have_one_arg { if matches!( cx.tcx.crate_name(fn_id.krate), sym::std | sym::core | sym::alloc | sym::proc_macro diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index dbb993482902f..6971b488013c4 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -299,7 +299,7 @@ impl<'a> VecArgs<'a> { pub fn hir(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option> { if let ExprKind::Call(fun, args) = expr.kind && let ExprKind::Path(ref qpath) = fun.kind - && is_expn_of(fun.span, "vec").is_some() + && is_expn_of(fun.span, sym::vec).is_some() && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() { return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 4fb608a64828a..3a31917657103 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -25,6 +25,7 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate indexmap; extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_attr_data_structures; @@ -82,7 +83,6 @@ pub use self::hir_utils::{ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; -use std::hash::BuildHasherDefault; use std::iter::{once, repeat_n}; use std::sync::{Mutex, MutexGuard, OnceLock}; @@ -92,7 +92,7 @@ use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; -use rustc_data_structures::unhash::UnhashMap; +use rustc_data_structures::unhash::UnindexMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; @@ -524,12 +524,8 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> /// } /// } /// ``` -pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> { - // Get the implemented trait for the current function - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - let parent_impl = cx.tcx.hir_get_parent_item(hir_id); - if parent_impl != hir::CRATE_OWNER_ID - && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id) +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> { + if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner)) && let ItemKind::Impl(impl_) = &item.kind { return impl_.of_trait.as_ref(); @@ -1102,13 +1098,13 @@ pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec(expr: &'a Expr<'_>, methods: &[&str]) -> Option, &'a [Expr<'a>])>> { +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option, &'a [Expr<'a>])>> { let mut current = expr; let mut matched = Vec::with_capacity(methods.len()); for method_name in methods.iter().rev() { // method chains are stored last -> first if let ExprKind::MethodCall(path, receiver, args, _) = current.kind { - if path.ident.name.as_str() == *method_name { + if path.ident.name == *method_name { if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) { return None; } @@ -1494,14 +1490,14 @@ pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// macro `name`. /// See also [`is_direct_expn_of`]. #[must_use] -pub fn is_expn_of(mut span: Span, name: &str) -> Option { +pub fn is_expn_of(mut span: Span, name: Symbol) -> Option { loop { if span.from_expansion() { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind - && mac_name.as_str() == name + && mac_name == name { return Some(new_span); } @@ -1524,13 +1520,13 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { /// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only /// from `bar!` by `is_direct_expn_of`. #[must_use] -pub fn is_direct_expn_of(span: Span, name: &str) -> Option { +pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option { if span.from_expansion() { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind - && mac_name.as_str() == name + && mac_name == name { return Some(new_span); } @@ -1794,11 +1790,11 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool { } /// Checks if the given `DefId` matches the `libc` item. -pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { +pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool { let path = cx.get_def_path(did); // libc is meant to be used as a flat list of names, but they're all actually defined in different // modules based on the target platform. Ignore everything but crate name and the item name. - path.first().is_some_and(|s| *s == sym::libc) && path.last().is_some_and(|s| s.as_str() == name) + path.first().is_some_and(|s| *s == sym::libc) && path.last().copied() == Some(name) } /// Returns the list of condition expressions and the list of blocks in a @@ -2197,45 +2193,46 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)> +pub fn search_same(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec> where Hash: FnMut(&T) -> u64, Eq: FnMut(&T, &T) -> bool, { match exprs { - [a, b] if eq(a, b) => return vec![(a, b)], + [a, b] if eq(a, b) => return vec![vec![a, b]], _ if exprs.len() <= 2 => return vec![], _ => {}, } - let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); - - let mut map: UnhashMap> = - UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); + let mut buckets: UnindexMap>> = UnindexMap::default(); for expr in exprs { - match map.entry(hash(expr)) { - Entry::Occupied(mut o) => { - for o in o.get() { - if eq(o, expr) { - match_expr_list.push((o, expr)); - } + match buckets.entry(hash(expr)) { + indexmap::map::Entry::Occupied(mut o) => { + let bucket = o.get_mut(); + match bucket.iter_mut().find(|group| eq(expr, group[0])) { + Some(group) => group.push(expr), + None => bucket.push(vec![expr]), } - o.get_mut().push(expr); }, - Entry::Vacant(v) => { - v.insert(vec![expr]); + indexmap::map::Entry::Vacant(v) => { + v.insert(vec![vec![expr]]); }, } } - match_expr_list + buckets + .into_values() + .flatten() + .filter(|group| group.len() > 1) + .collect() } /// Peels off all references on the pattern. Returns the underlying pattern and the number of diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index dfb30b9c21864..ba126fcd05de9 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -2,8 +2,8 @@ use std::sync::{Arc, OnceLock}; -use crate::get_unique_attr; use crate::visitors::{Descend, for_each_expr_without_closures}; +use crate::{get_unique_attr, sym}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; @@ -12,7 +12,7 @@ use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; -use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol, sym}; +use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; use std::ops::ControlFlow; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -42,7 +42,7 @@ pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { } else { // Allow users to tag any macro as being format!-like // TODO: consider deleting FORMAT_MACRO_DIAG_ITEMS and using just this method - get_unique_attr(cx.sess(), cx.tcx.get_attrs_unchecked(macro_def_id), "format_args").is_some() + get_unique_attr(cx.sess(), cx.tcx.get_attrs_unchecked(macro_def_id), sym::format_args).is_some() } } @@ -248,10 +248,10 @@ impl<'a> PanicExpn<'a> { let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None; }; - let name = path.segments.last().unwrap().ident.as_str(); + let name = path.segments.last().unwrap().ident.name; // This has no argument - if name == "panic_cold_explicit" { + if name == sym::panic_cold_explicit { return Some(Self::Empty); } @@ -259,18 +259,18 @@ impl<'a> PanicExpn<'a> { return None; }; let result = match name { - "panic" if arg.span.eq_ctxt(expr.span) => Self::Empty, - "panic" | "panic_str" => Self::Str(arg), - "panic_display" | "panic_cold_display" => { + sym::panic if arg.span.eq_ctxt(expr.span) => Self::Empty, + sym::panic | sym::panic_str => Self::Str(arg), + sym::panic_display | sym::panic_cold_display => { let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None; }; Self::Display(e) }, - "panic_fmt" => Self::Format(arg), + sym::panic_fmt => Self::Format(arg), // Since Rust 1.52, `assert_{eq,ne}` macros expand to use: // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));` - "assert_failed" => { + sym::assert_failed => { // It should have 4 arguments in total (we already matched with the first argument, // so we're just checking for 3) if rest.len() != 3 { diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 599c6f3a4ff33..a5e66ad463bbb 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -25,7 +25,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,88,0 { LET_CHAINS } 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT } - 1,85,0 { UINT_FLOAT_MIDPOINT } + 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index e5179e479ccd9..9d7f3086b05fa 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -129,6 +129,7 @@ path_macros! { // Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of); pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit); +pub static CONCAT: PathLookup = macro_path!(core::concat); pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new); pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other); pub static ITER_STEP: PathLookup = type_path!(core::iter::Step); diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs index 360c6251a57c2..5847e916e340f 100644 --- a/src/tools/clippy/clippy_utils/src/ptr.rs +++ b/src/tools/clippy/clippy_utils/src/ptr.rs @@ -1,17 +1,17 @@ use crate::source::snippet; use crate::visitors::{Descend, for_each_expr_without_closures}; -use crate::{path_to_local_id, strip_pat_refs}; +use crate::{path_to_local_id, strip_pat_refs, sym}; use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; pub fn get_spans( cx: &LateContext<'_>, opt_body_id: Option, idx: usize, - replacements: &[(&'static str, &'static str)], + replacements: &[(Symbol, &'static str)], ) -> Option)>> { if let Some(body) = opt_body_id.map(|id| cx.tcx.hir_body(id)) { if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { @@ -27,7 +27,7 @@ pub fn get_spans( fn extract_clone_suggestions<'tcx>( cx: &LateContext<'tcx>, id: HirId, - replace: &[(&'static str, &'static str)], + replace: &[(Symbol, &'static str)], body: &'tcx Body<'_>, ) -> Option)>> { let mut spans = Vec::new(); @@ -35,11 +35,11 @@ fn extract_clone_suggestions<'tcx>( if let ExprKind::MethodCall(seg, recv, [], _) = e.kind && path_to_local_id(recv, id) { - if seg.ident.as_str() == "capacity" { + if seg.ident.name == sym::capacity { return ControlFlow::Break(()); } for &(fn_name, suffix) in replace { - if seg.ident.as_str() == fn_name { + if seg.ident.name == fn_name { spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); return ControlFlow::Continue(Descend::No); } diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 8645d5730fedb..7f2bf99daff20 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -7,13 +7,14 @@ use std::sync::Arc; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; +use rustc_lexer::{LiteralKind, TokenKind, tokenize}; use rustc_lint::{EarlyContext, LateContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, - hygiene, + BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, Span, SpanData, + SyntaxContext, hygiene, }; use std::borrow::Cow; use std::fmt; @@ -137,25 +138,25 @@ pub trait SpanRangeExt: SpanRange { fn map_range( self, cx: &impl HasSession, - f: impl for<'a> FnOnce(&'a str, Range) -> Option>, + f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { map_range(cx.sess().source_map(), self.into_range(), f) } #[allow(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] - /// Extends the range to include all preceding whitespace characters, unless there - /// are non-whitespace characters left on the same line after `self`. + /// Extends the range to include all preceding whitespace characters. + /// + /// The range will not be expanded if it would cross a line boundary, the line the range would + /// be extended to ends with a line comment and the text after the range contains a + /// non-whitespace character on the same line. e.g. /// - /// This extra condition prevents a problem when removing the '}' in: /// ```ignore - /// ( // There was an opening bracket after the parenthesis, which has been removed - /// // This is a comment - /// }) + /// ( // Some comment + /// foo) /// ``` - /// Removing the whitespaces, including the linefeed, before the '}', would put the - /// closing parenthesis at the end of the `// This is a comment` line, which would - /// make it part of the comment as well. In this case, it is best to keep the span - /// on the '}' alone. + /// + /// When the range points to `foo`, suggesting to remove the range after it's been extended will + /// cause the `)` to be placed inside the line comment as `( // Some comment)`. fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { with_leading_whitespace(cx.sess().source_map(), self.into_range()) } @@ -254,11 +255,11 @@ fn with_source_text_and_range( fn map_range( sm: &SourceMap, sp: Range, - f: impl for<'a> FnOnce(&'a str, Range) -> Option>, + f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { if let Some(src) = get_source_range(sm, sp.clone()) && let Some(text) = &src.sf.src - && let Some(range) = f(text, src.range.clone()) + && let Some(range) = f(&src.sf, text, src.range.clone()) { debug_assert!( range.start <= text.len() && range.end <= text.len(), @@ -275,20 +276,57 @@ fn map_range( } } +fn ends_with_line_comment_or_broken(text: &str) -> bool { + let Some(last) = tokenize(text).last() else { + return false; + }; + match last.kind { + // Will give the wrong result on text like `" // "` where the first quote ends a string + // started earlier. The only workaround is to lex the whole file which we don't really want + // to do. + TokenKind::LineComment { .. } | TokenKind::BlockComment { terminated: false, .. } => true, + TokenKind::Literal { kind, .. } => matches!( + kind, + LiteralKind::Byte { terminated: false } + | LiteralKind::ByteStr { terminated: false } + | LiteralKind::CStr { terminated: false } + | LiteralKind::Char { terminated: false } + | LiteralKind::RawByteStr { n_hashes: None } + | LiteralKind::RawCStr { n_hashes: None } + | LiteralKind::RawStr { n_hashes: None } + ), + _ => false, + } +} + +fn with_leading_whitespace_inner(lines: &[RelativeBytePos], src: &str, range: Range) -> Option { + debug_assert!(lines.is_empty() || lines[0].to_u32() == 0); + + let start = src.get(..range.start)?.trim_end(); + let next_line = lines.partition_point(|&pos| pos.to_usize() <= start.len()); + if let Some(line_end) = lines.get(next_line) + && line_end.to_usize() <= range.start + && let prev_start = lines.get(next_line - 1).map_or(0, |&x| x.to_usize()) + && ends_with_line_comment_or_broken(&start[prev_start..]) + && let next_line = lines.partition_point(|&pos| pos.to_usize() < range.end) + && let next_start = lines.get(next_line).map_or(src.len(), |&x| x.to_usize()) + && tokenize(src.get(range.end..next_start)?).any(|t| !matches!(t.kind, TokenKind::Whitespace)) + { + Some(range.start) + } else { + Some(start.len()) + } +} + fn with_leading_whitespace(sm: &SourceMap, sp: Range) -> Range { - map_range(sm, sp, |src, range| { - let non_blank_after = src.len() - src.get(range.end..)?.trim_start().len(); - if src.get(range.end..non_blank_after)?.contains(['\r', '\n']) { - Some(src.get(..range.start)?.trim_end().len()..range.end) - } else { - Some(range) - } + map_range(sm, sp.clone(), |sf, src, range| { + Some(with_leading_whitespace_inner(sf.lines(), src, range.clone())?..range.end) }) - .unwrap() + .unwrap_or(sp) } fn trim_start(sm: &SourceMap, sp: Range) -> Range { - map_range(sm, sp.clone(), |src, range| { + map_range(sm, sp.clone(), |_, src, range| { let src = src.get(range.clone())?; Some(range.start + (src.len() - src.trim_start().len())..range.end) }) diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 9428262b99aa7..f417530be3672 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -29,172 +29,330 @@ macro_rules! generate { }; } +// List of extra symbols to be included in Clippy (for example, as `sym::ambiguous_glob_reexports`). +// An alternative content can be specified using a colon after the symbol name. +// +// `cargo dev fmt` ensures that the content of the `generate!()` macro call stays sorted. generate! { + AsyncReadExt, + AsyncWriteExt, + BACKSLASH_SINGLE_QUOTE: r"\'", + Binary, + CLIPPY_ARGS, + CLIPPY_CONF_DIR, + CRLF: "\r\n", + Cargo_toml: "Cargo.toml", + Current, + DOUBLE_QUOTE: "\"", + Deserialize, + EarlyLintPass, + ErrorKind, + IntoIter, + Itertools, + LF: "\n", + Lazy, + Lint, + LowerExp, + LowerHex, + MAX, + MIN, + MsrvStack, + Octal, + OpenOptions, + Other, + PathLookup, + Regex, + RegexBuilder, + RegexSet, + Start, + Step, + Symbol, + SyntaxContext, + TBD, + UpperExp, + UpperHex, + V4, + V6, + Visitor, + Weak, abs, align_of, ambiguous_glob_reexports, + append, + arg, as_bytes, - as_deref_mut, as_deref, + as_deref_mut, as_mut, - AsyncReadExt, - AsyncWriteExt, - BACKSLASH_SINGLE_QUOTE: r"\'", - Binary, + assert_failed, + author, + borrow, + borrow_mut, build_hasher, + by_ref, bytes, + capacity, cargo_clippy: "cargo-clippy", - Cargo_toml: "Cargo.toml", cast, + cast_const, + cast_mut, + ceil, + ceil_char_boundary, + chain, chars, - CLIPPY_ARGS, - CLIPPY_CONF_DIR, + checked_abs, + checked_add, + checked_isqrt, + checked_mul, + checked_pow, + checked_rem_euclid, + checked_sub, + clamp, clippy_utils, clone_into, cloned, + cognitive_complexity, collect, const_ptr, contains, copied, - CRLF: "\r\n", - Current, + copy_from, + copy_from_nonoverlapping, + copy_to, + copy_to_nonoverlapping, + count_ones, + cycle, + cyclomatic_complexity, de, - Deserialize, diagnostics, disallowed_types, - DOUBLE_QUOTE: "\"", - EarlyLintPass, + drain, + dump, ends_with, enum_glob_use, + enumerate, + err, error, - ErrorKind, exp, + expect_err, + expn_data, extend, - finish_non_exhaustive, + filter, + filter_map, + find, + find_map, finish, + finish_non_exhaustive, + first, flat_map, + flatten, + floor, + floor_char_boundary, + fold, for_each, - from_bytes_with_nul_unchecked, from_bytes_with_nul, + from_bytes_with_nul_unchecked, from_ptr, from_raw, from_ref, + from_str, from_str_radix, fs, + fuse, futures_util, get, + get_mut, + get_or_insert_with, + get_unchecked, + get_unchecked_mut, + has_significant_drop, hidden_glob_reexports, hygiene, + if_chain, insert, + inspect, int_roundings, + into, into_bytes, + into_ok, into_owned, - IntoIter, io, is_ascii, + is_char_boundary, + is_digit, is_empty, is_err, + is_file, is_none, is_ok, is_some, + isqrt, itertools, - Itertools, + join, kw, last, lazy_static, - Lazy, - LF: "\n", - Lint, ln, + lock, lock_api, log, - LowerExp, - LowerHex, + log10, + log2, macro_use_imports, - map_or_else, + map_break, + map_continue, map_or, + map_or_else, + match_indices, + matches, max, - MAX, + max_by, + max_by_key, + max_value, + maximum, mem, min, - MIN, + min_by, + min_by_key, + min_value, + minimum, mode, module_name_repetitions, msrv, msrvs, - MsrvStack, mut_ptr, mutex, needless_return, + next_back, + next_if, + next_if_eq, next_tuple, - Octal, + nth, + ok, + ok_or, once_cell, - OpenOptions, + open, or_default, - Other, + or_else, + or_insert, + or_insert_with, + outer_expn, + panic_cold_display, + panic_cold_explicit, + panic_display, + panic_str, parse, - PathLookup, + partition, paths, + peek, + peek_mut, + peekable, + pow, powf, powi, + product, push, + read_line, + read_to_end, + read_to_string, redundant_pub_crate, regex, - Regex, - RegexBuilder, - RegexSet, + rem_euclid, + repeat, + replace, + replacen, reserve, resize, restriction, - rustc_lint_defs, + rev, + rfind, + rmatch_indices, + rmatches, + round, + rposition, + rsplit, + rsplit_once, + rsplit_terminator, + rsplitn, + rsplitn_mut, rustc_lint, + rustc_lint_defs, rustc_span, rustfmt_skip, rwlock, + saturating_abs, + saturating_pow, + scan, + seek, serde, set_len, set_mode, set_readonly, signum, single_component_path_imports, + skip_while, + slice_mut_unchecked, + slice_unchecked, + sort, + sort_by, + sort_unstable_by, span_lint_and_then, - split_whitespace, split, + split_at, + split_at_checked, + split_at_mut, + split_at_mut_checked, + split_inclusive, + split_once, + split_terminator, + split_whitespace, + splitn, + splitn_mut, sqrt, - Start, - Step, + starts_with, + step_by, + strlen, style, + subsec_micros, + subsec_nanos, + sum, symbol, - Symbol, - SyntaxContext, take, - TBD, + take_while, + then, then_some, to_ascii_lowercase, to_ascii_uppercase, to_digit, to_lowercase, + to_os_string, to_owned, + to_path_buf, to_uppercase, tokio, + trim, + trim_end_matches, + trim_start_matches, unreachable_pub, unsafe_removed_from_name, + unused, unused_braces, unused_extern_crates, unused_import_braces, unused_trait_names, - unused, unwrap_err, + unwrap_err_unchecked, unwrap_or_default, unwrap_or_else, - UpperExp, - UpperHex, - V4, - V6, - Visitor, + unwrap_unchecked, + unzip, + utils, + wake, warnings, - Weak, wildcard_imports, with_capacity, wrapping_offset, + write, + writeln, + zip, } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 26d41cfb4977b..c50ad17bfad1e 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -1361,3 +1361,14 @@ pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { || ty.is_array() || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) } + +/// Gets the index of a field by name. +pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { + match *ty.kind() { + ty::Adt(def, _) if def.is_union() || def.is_struct() => { + def.non_enum_variant().fields.iter().position(|f| f.name == name) + }, + ty::Tuple(_) => name.as_str().parse::().ok(), + _ => None, + } +} diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index da41bdd27bcb6..0b9ebe554f5b4 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-05-14" +channel = "nightly-2025-05-21" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index f8acf88cf81c6..37adb14169a3f 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -96,16 +96,11 @@ fn track_files(psess: &mut ParseSess) { // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever // it is rebuilt - #[expect( - clippy::collapsible_if, - reason = "Due to a bug in let_chains this if statement can't be collapsed" - )] - if cfg!(debug_assertions) { - if let Ok(current_exe) = env::current_exe() - && let Some(current_exe) = current_exe.to_str() - { - file_depinfo.insert(Symbol::intern(current_exe)); - } + if cfg!(debug_assertions) + && let Ok(current_exe) = env::current_exe() + && let Some(current_exe) = current_exe.to_str() + { + file_depinfo.insert(Symbol::intern(current_exe)); } } diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr index 50567e32b1bb9..a3c35a31c3314 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr @@ -13,37 +13,37 @@ LL | const SNAKE_CASE: &str = "zzzzzzzz"; = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:7 | LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `TraitUnorderedItemKinds` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:7 | LL | trait TraitUnorderedItemKinds { | ^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:204:5 | LL | mod this_is_in_the_wrong_position { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:199:4 | LL | fn main() { | ^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:214:7 | LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `ZisShouldBeBeforeZeMainFn` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:212:8 | LL | struct ZisShouldBeBeforeZeMainFn; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,100 +61,124 @@ LL | C, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + | +note: should be placed before `r` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:56:5 + | +LL | r: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:59:5 + | +LL | b: u8, + | ^ + | +note: should be placed before `g` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:112:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:111:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 | LL | const B: bool; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 | LL | const C: bool; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:8 | LL | fn b(); | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 | LL | fn c(); | ^ error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5 | LL | const A: bool; | ^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5 | LL | type SomeType; | ^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 | LL | const B: bool = false; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:11 | LL | const C: bool = false; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:8 | LL | fn b() {} | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:177:8 | LL | fn c() {} | ^ error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5 | LL | const A: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5 | LL | type SomeType = (); | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr index 50567e32b1bb9..a3c35a31c3314 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr @@ -13,37 +13,37 @@ LL | const SNAKE_CASE: &str = "zzzzzzzz"; = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:7 | LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `TraitUnorderedItemKinds` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:7 | LL | trait TraitUnorderedItemKinds { | ^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:204:5 | LL | mod this_is_in_the_wrong_position { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:199:4 | LL | fn main() { | ^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:214:7 | LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `ZisShouldBeBeforeZeMainFn` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:212:8 | LL | struct ZisShouldBeBeforeZeMainFn; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,100 +61,124 @@ LL | C, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + | +note: should be placed before `r` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:56:5 + | +LL | r: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:59:5 + | +LL | b: u8, + | ^ + | +note: should be placed before `g` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:112:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:111:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 | LL | const B: bool; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 | LL | const C: bool; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:8 | LL | fn b(); | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 | LL | fn c(); | ^ error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5 | LL | const A: bool; | ^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5 | LL | type SomeType; | ^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 | LL | const B: bool = false; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:11 | LL | const C: bool = false; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:8 | LL | fn b() {} | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:177:8 | LL | fn c() {} | ^ error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5 | LL | const A: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5 | LL | type SomeType = (); | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr index ae5261dcc6df8..3fdd706fc627f 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr @@ -25,7 +25,19 @@ LL | const SNAKE_CASE: &str = "zzzzzzzz"; | ^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:71:1 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:64:8 + | +LL | struct EnumWithExternButAtWrongPosition { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `EnumWithoutExtern` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:55:8 + | +LL | struct EnumWithoutExtern { + | ^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:87:1 | LL | / impl CloneSelf for StructOrdered { LL | | @@ -36,7 +48,7 @@ LL | | } | |_^ | note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:61:1 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:77:1 | LL | / impl Default for StructOrdered { LL | | fn default() -> Self { @@ -47,25 +59,25 @@ LL | | } | |_^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:7 | LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `TraitUnorderedItemKinds` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:7 | LL | trait TraitUnorderedItemKinds { | ^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:167:1 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:1 | LL | impl BasicEmptyTrait for StructOrdered {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:1 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:168:1 | LL | / impl TraitUnordered for StructUnordered { LL | | const A: bool = false; @@ -76,25 +88,25 @@ LL | | } | |_^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:204:5 | LL | mod this_is_in_the_wrong_position { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:199:4 | LL | fn main() { | ^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:214:7 | LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `ZisShouldBeBeforeZeMainFn` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:212:8 | LL | struct ZisShouldBeBeforeZeMainFn; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,112 +136,136 @@ LL | C, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + | +note: should be placed before `r` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:56:5 + | +LL | r: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:59:5 + | +LL | b: u8, + | ^ + | +note: should be placed before `g` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:112:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:111:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 | LL | const B: bool; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 | LL | const C: bool; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:8 | LL | fn b(); | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 | LL | fn c(); | ^ error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5 | LL | const A: bool; | ^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5 | LL | type SomeType; | ^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 | LL | const B: bool = false; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:11 | LL | const C: bool = false; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:8 | LL | fn b() {} | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:177:8 | LL | fn c() {} | ^ error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5 | LL | const A: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5 | LL | type SomeType = (); | ^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:191:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:207:11 | LL | const A: i8 = 1; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:190:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:206:11 | LL | const C: i8 = 0; | ^ -error: aborting due to 18 previous errors +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs index 90399470d4c09..1cfed9790c120 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs @@ -52,6 +52,22 @@ enum EnumUnorderedAllowed { B, } +struct EnumWithoutExtern { + r: u8, + g: u8, + //~^ arbitrary_source_item_ordering + b: u8, + //~^ arbitrary_source_item_ordering +} + +#[repr(C)] +struct EnumWithExternButAtWrongPosition { + //~[ord_within]^ arbitrary_source_item_ordering + r: u8, + g: u8, + b: u8, +} + struct StructOrdered { a: bool, b: bool, diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed index 6f5cc47ba6c75..f695f9804d59c 100644 --- a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed @@ -13,7 +13,7 @@ fn main() { //~^^^^^^ collapsible_if // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 - if x == "hello" // Inner comment + if x == "hello" // Inner comment && y == "world" { println!("Hello world!"); } @@ -26,7 +26,7 @@ fn main() { } //~^^^^^^ collapsible_if - if x == "hello" /* Inner comment */ + if x == "hello" /* Inner comment */ && y == "world" { println!("Hello world!"); } diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr index 357ce4ad32deb..a12c2112f5877 100644 --- a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr @@ -32,7 +32,7 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" // Inner comment +LL ~ if x == "hello" // Inner comment LL ~ && y == "world" { LL | println!("Hello world!"); LL ~ } @@ -70,7 +70,7 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" /* Inner comment */ +LL ~ if x == "hello" /* Inner comment */ LL ~ && y == "world" { LL | println!("Hello world!"); LL ~ } diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs index e953a2a4e90bc..2a6097fb57953 100644 --- a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs +++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs @@ -11,9 +11,9 @@ fn issue10272() { // should trigger warning let x = Cell::new(true); if x.get() { + //~^ ifs_same_cond } else if !x.take() { } else if x.get() { - //~^ ifs_same_cond } else { } } diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr index d67e7fca6565b..adc44358c4c7b 100644 --- a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr +++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr @@ -1,14 +1,12 @@ -error: this `if` has the same condition as a previous `if` - --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:15:15 - | -LL | } else if x.get() { - | ^^^^^^^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:13:8 | LL | if x.get() { | ^^^^^^^ +... +LL | } else if x.get() { + | ^^^^^^^ + | = note: `-D clippy::ifs-same-cond` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed index 18f0e04a8807e..429c20f95e919 100644 --- a/src/tools/clippy/tests/ui/assign_ops.fixed +++ b/src/tools/clippy/tests/ui/assign_ops.fixed @@ -1,7 +1,9 @@ +#![allow(clippy::useless_vec)] +#![warn(clippy::assign_op_pattern)] + use core::num::Wrapping; +use std::ops::{Mul, MulAssign}; -#[allow(dead_code, unused_assignments, clippy::useless_vec)] -#[warn(clippy::assign_op_pattern)] fn main() { let mut a = 5; a += 1; @@ -39,3 +41,35 @@ fn main() { v[0] = v[0] + v[1]; let _ = || v[0] = v[0] + v[1]; } + +fn cow_add_assign() { + use std::borrow::Cow; + let mut buf = Cow::Owned(String::from("bar")); + let cows = Cow::Borrowed("foo"); + + // this can be linted + buf += cows.clone(); + //~^ assign_op_pattern + + // this should not as cow Add is not commutative + buf = cows + buf; +} + +// check that we don't lint on op assign impls, because that's just the way to impl them + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Wrap(i64); + +impl Mul for Wrap { + type Output = Self; + + fn mul(self, rhs: i64) -> Self { + Wrap(self.0 * rhs) + } +} + +impl MulAssign for Wrap { + fn mul_assign(&mut self, rhs: i64) { + *self = *self * rhs + } +} diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs index 8b05c74d86040..480ff07f150ea 100644 --- a/src/tools/clippy/tests/ui/assign_ops.rs +++ b/src/tools/clippy/tests/ui/assign_ops.rs @@ -1,7 +1,9 @@ +#![allow(clippy::useless_vec)] +#![warn(clippy::assign_op_pattern)] + use core::num::Wrapping; +use std::ops::{Mul, MulAssign}; -#[allow(dead_code, unused_assignments, clippy::useless_vec)] -#[warn(clippy::assign_op_pattern)] fn main() { let mut a = 5; a = a + 1; @@ -39,3 +41,35 @@ fn main() { v[0] = v[0] + v[1]; let _ = || v[0] = v[0] + v[1]; } + +fn cow_add_assign() { + use std::borrow::Cow; + let mut buf = Cow::Owned(String::from("bar")); + let cows = Cow::Borrowed("foo"); + + // this can be linted + buf = buf + cows.clone(); + //~^ assign_op_pattern + + // this should not as cow Add is not commutative + buf = cows + buf; +} + +// check that we don't lint on op assign impls, because that's just the way to impl them + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Wrap(i64); + +impl Mul for Wrap { + type Output = Self; + + fn mul(self, rhs: i64) -> Self { + Wrap(self.0 * rhs) + } +} + +impl MulAssign for Wrap { + fn mul_assign(&mut self, rhs: i64) { + *self = *self * rhs + } +} diff --git a/src/tools/clippy/tests/ui/assign_ops.stderr b/src/tools/clippy/tests/ui/assign_ops.stderr index 17f216ee4a071..881a333fbe4b9 100644 --- a/src/tools/clippy/tests/ui/assign_ops.stderr +++ b/src/tools/clippy/tests/ui/assign_ops.stderr @@ -1,5 +1,5 @@ error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:7:5 + --> tests/ui/assign_ops.rs:9:5 | LL | a = a + 1; | ^^^^^^^^^ help: replace it with: `a += 1` @@ -8,64 +8,70 @@ LL | a = a + 1; = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:9:5 + --> tests/ui/assign_ops.rs:11:5 | LL | a = 1 + a; | ^^^^^^^^^ help: replace it with: `a += 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:11:5 + --> tests/ui/assign_ops.rs:13:5 | LL | a = a - 1; | ^^^^^^^^^ help: replace it with: `a -= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:13:5 + --> tests/ui/assign_ops.rs:15:5 | LL | a = a * 99; | ^^^^^^^^^^ help: replace it with: `a *= 99` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:15:5 + --> tests/ui/assign_ops.rs:17:5 | LL | a = 42 * a; | ^^^^^^^^^^ help: replace it with: `a *= 42` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:17:5 + --> tests/ui/assign_ops.rs:19:5 | LL | a = a / 2; | ^^^^^^^^^ help: replace it with: `a /= 2` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:19:5 + --> tests/ui/assign_ops.rs:21:5 | LL | a = a % 5; | ^^^^^^^^^ help: replace it with: `a %= 5` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:21:5 + --> tests/ui/assign_ops.rs:23:5 | LL | a = a & 1; | ^^^^^^^^^ help: replace it with: `a &= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:28:5 + --> tests/ui/assign_ops.rs:30:5 | LL | s = s + "bla"; | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:33:5 + --> tests/ui/assign_ops.rs:35:5 | LL | a = a + Wrapping(1u32); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:36:5 + --> tests/ui/assign_ops.rs:38:5 | LL | v[0] = v[0] + v[1]; | ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]` -error: aborting due to 11 previous errors +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:51:5 + | +LL | buf = buf + cows.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/assign_ops2.rs b/src/tools/clippy/tests/ui/assign_ops2.rs deleted file mode 100644 index 51867fa6962cf..0000000000000 --- a/src/tools/clippy/tests/ui/assign_ops2.rs +++ /dev/null @@ -1,77 +0,0 @@ -//@no-rustfix: overlapping suggestions -#![allow(clippy::uninlined_format_args)] - -#[allow(unused_assignments)] -#[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] -fn main() { - let mut a = 5; - a += a + 1; - //~^ misrefactored_assign_op - - a += 1 + a; - //~^ misrefactored_assign_op - - a -= a - 1; - //~^ misrefactored_assign_op - - a *= a * 99; - //~^ misrefactored_assign_op - - a *= 42 * a; - //~^ misrefactored_assign_op - - a /= a / 2; - //~^ misrefactored_assign_op - - a %= a % 5; - //~^ misrefactored_assign_op - - a &= a & 1; - //~^ misrefactored_assign_op - - a *= a * a; - //~^ misrefactored_assign_op - - a = a * a * a; - a = a * 42 * a; - a = a * 2 + a; - a -= 1 - a; - a /= 5 / a; - a %= 42 % a; - a <<= 6 << a; -} - -// check that we don't lint on op assign impls, because that's just the way to impl them - -use std::ops::{Mul, MulAssign}; - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Wrap(i64); - -impl Mul for Wrap { - type Output = Self; - - fn mul(self, rhs: i64) -> Self { - Wrap(self.0 * rhs) - } -} - -impl MulAssign for Wrap { - fn mul_assign(&mut self, rhs: i64) { - *self = *self * rhs - } -} - -fn cow_add_assign() { - use std::borrow::Cow; - let mut buf = Cow::Owned(String::from("bar")); - let cows = Cow::Borrowed("foo"); - - // this can be linted - buf = buf + cows.clone(); - //~^ assign_op_pattern - - // this should not as cow Add is not commutative - buf = cows + buf; - println!("{}", buf); -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/src/tools/clippy/tests/ui/auxiliary/interior_mutable_const.rs similarity index 100% rename from src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs rename to src/tools/clippy/tests/ui/auxiliary/interior_mutable_const.rs diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs new file mode 100644 index 0000000000000..0f439f7891507 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs @@ -0,0 +1,221 @@ +//@aux-build:interior_mutable_const.rs + +#![deny(clippy::borrow_interior_mutable_const)] +#![allow( + clippy::declare_interior_mutable_const, + clippy::out_of_bounds_indexing, + const_item_mutation, + unconditional_panic +)] + +use core::cell::{Cell, UnsafeCell}; +use core::ops::{Deref, Index}; + +trait ConstDefault { + const DEFAULT: Self; +} +impl ConstDefault for u32 { + const DEFAULT: Self = 0; +} +impl ConstDefault for Cell { + const DEFAULT: Self = Cell::new(T::DEFAULT); +} + +fn main() { + { + const C: String = String::new(); + let _ = C; + let _ = &C; + let _ = C.len(); + let _ = &*C; + } + { + const C: UnsafeCell = UnsafeCell::new(0); + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = C.into_inner(); + let _ = C.get(); //~ borrow_interior_mutable_const + } + { + const C: Cell = Cell::new(0); + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = &mut C; //~ borrow_interior_mutable_const + let _ = C.into_inner(); + + let local = C; + C.swap(&local) //~ borrow_interior_mutable_const + } + { + const C: [(Cell,); 1] = [(Cell::new(0),)]; + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = &C[0]; //~ borrow_interior_mutable_const + let _ = &C[0].0; //~ borrow_interior_mutable_const + C[0].0.set(1); //~ borrow_interior_mutable_const + } + { + struct S(Cell); + impl S { + const C: Self = Self(Cell::new(0)); + } + impl Deref for S { + type Target = Cell; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + let _ = S::C; + let _ = S::C.0; + let _ = &S::C; //~ borrow_interior_mutable_const + let _ = &S::C.0; //~ borrow_interior_mutable_const + S::C.set(1); //~ borrow_interior_mutable_const + let _ = &*S::C; //~ borrow_interior_mutable_const + (*S::C).set(1); //~ borrow_interior_mutable_const + } + { + enum E { + Cell(Cell), + Other, + } + const CELL: E = E::Cell(Cell::new(0)); + const OTHER: E = E::Other; + + let _ = CELL; + let _ = &CELL; //~ borrow_interior_mutable_const + let E::Cell(_) = CELL else { + return; + }; + + let _ = OTHER; + let _ = &OTHER; + let E::Cell(ref _x) = OTHER else { + return; + }; + } + { + struct S { + cell: (Cell, u32), + other: Option, + } + impl S { + const C: Self = Self { + cell: (Cell::::DEFAULT, 0), + other: Some(T::DEFAULT), + }; + + fn f() { + let _ = Self::C; + let _ = &Self::C; //~ borrow_interior_mutable_const + let _ = Self::C.other; + let _ = &Self::C.other; + let _ = &Self::C.cell; //~ borrow_interior_mutable_const + let _ = &Self::C.cell.0; //~ borrow_interior_mutable_const + Self::C.cell.0.set(T::DEFAULT); //~ borrow_interior_mutable_const + let _ = &Self::C.cell.1; + } + } + } + { + trait T { + const VALUE: Option> = Some(Cell::new(0)); + } + impl T for u32 {} + impl T for i32 { + const VALUE: Option> = None; + } + + let _ = &u32::VALUE; //~ borrow_interior_mutable_const + let _ = &i32::VALUE; + } + { + trait Trait { + type T: ConstDefault; + const VALUE: Option> = Some(Self::T::::DEFAULT); + } + impl Trait for u32 { + type T = Cell; + } + impl Trait for i32 { + type T = Cell; + const VALUE: Option> = None; + } + + fn f() { + let _ = &>::VALUE; //~ borrow_interior_mutable_const + let _ = &>::VALUE; + } + } + { + trait Trait { + const UNFROZEN: Option> = Some(Cell::new(0)); + const FROZEN: Option> = None; + const NON_FREEZE: u32 = 0; + } + fn f() { + // None of these are guaranteed to be frozen, so don't lint. + let _ = &T::UNFROZEN; + let _ = &T::FROZEN; + let _ = &T::NON_FREEZE; + } + } + { + struct S([Option>; 2]); + impl Index for S { + type Output = Option>; + fn index(&self, idx: usize) -> &Self::Output { + &self.0[idx] + } + } + + const C: S = S([Some(Cell::new(0)), None]); + let _ = &C; //~ borrow_interior_mutable_const + let _ = &C[0]; //~ borrow_interior_mutable_const + let _ = &C.0[0]; //~ borrow_interior_mutable_const + let _ = &C.0[1]; + } + { + const C: [Option>; 2] = [None, None]; + let _ = &C[0]; + let _ = &C[1]; + let _ = &C[2]; + + fn f(i: usize) { + let _ = &C[i]; + } + } + { + const C: [Option>; 2] = [None, Some(Cell::new(0))]; + let _ = &C[0]; + let _ = &C[1]; //~ borrow_interior_mutable_const + let _ = &C[2]; + + fn f(i: usize) { + let _ = &C[i]; //~ borrow_interior_mutable_const + } + } + { + let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const + let _ = &interior_mutable_const::WRAPPED_PRIVATE_FROZEN_VARIANT; + } + { + type Cell2 = Cell; + type MyCell = Cell2; + struct S(Option); + trait T { + type Assoc; + } + struct S2(T, T, u32); + impl T for S { + type Assoc = S2; + } + type Assoc = ::Assoc; + impl S { + const VALUE: Assoc = S2(Self(None), Self(Some(Cell::new(0))), 0); + } + let _ = &S::VALUE; //~ borrow_interior_mutable_const + let _ = &S::VALUE.0; + let _ = &S::VALUE.1; //~ borrow_interior_mutable_const + let _ = &S::VALUE.2; + } +} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr new file mode 100644 index 0000000000000..e7c3f879b05b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr @@ -0,0 +1,247 @@ +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:35:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing +note: the lint level is defined here + --> tests/ui/borrow_interior_mutable_const.rs:3:9 + | +LL | #![deny(clippy::borrow_interior_mutable_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:37:17 + | +LL | let _ = C.get(); + | ^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:42:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:43:17 + | +LL | let _ = &mut C; + | ^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:47:9 + | +LL | C.swap(&local) + | ^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:52:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:53:17 + | +LL | let _ = &C[0]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:54:17 + | +LL | let _ = &C[0].0; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:55:9 + | +LL | C[0].0.set(1); + | ^^^^^^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:70:17 + | +LL | let _ = &S::C; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:71:17 + | +LL | let _ = &S::C.0; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:72:9 + | +LL | S::C.set(1); + | ^^^^ + | + = note: there is a compiler inserted call to `Deref::deref` here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:73:18 + | +LL | let _ = &*S::C; + | ^^^^^ + | + = note: this deref expression is a call to `Deref::deref` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:74:9 + | +LL | (*S::C).set(1); + | ^^^^^^^ + | + = note: this deref expression is a call to `Deref::deref` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:85:17 + | +LL | let _ = &CELL; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:109:25 + | +LL | let _ = &Self::C; + | ^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:112:25 + | +LL | let _ = &Self::C.cell; + | ^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:113:25 + | +LL | let _ = &Self::C.cell.0; + | ^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:114:17 + | +LL | Self::C.cell.0.set(T::DEFAULT); + | ^^^^^^^^^^^^^^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:128:17 + | +LL | let _ = &u32::VALUE; + | ^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:145:21 + | +LL | let _ = &>::VALUE; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:172:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:173:18 + | +LL | let _ = &C[0]; + | ^^^^ + | + = note: this index expression is a call to `Index::index` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:174:17 + | +LL | let _ = &C.0[0]; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:190:17 + | +LL | let _ = &C[1]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:194:21 + | +LL | let _ = &C[i]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:198:17 + | +LL | let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:216:17 + | +LL | let _ = &S::VALUE; + | ^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:218:17 + | +LL | let _ = &S::VALUE.1; + | ^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: aborting due to 29 previous errors + diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs deleted file mode 100644 index da940a4cfb50b..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs +++ /dev/null @@ -1,101 +0,0 @@ -//@aux-build:helper.rs - -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const)] - -// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions. - -extern crate helper; - -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -enum OptionalCell { - Unfrozen(Cell), - Frozen, -} - -const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); -const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - -fn borrow_optional_cell() { - let _ = &UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &FROZEN_VARIANT; -} - -trait AssocConsts { - const TO_BE_UNFROZEN_VARIANT: OptionalCell; - const TO_BE_FROZEN_VARIANT: OptionalCell; - - const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - fn function() { - // This is the "suboptimal behavior" mentioned in `is_value_unfrozen` - // caused by a similar reason to unfrozen types without any default values - // get linted even if it has frozen variants'. - let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR: interior mutability - - // The lint ignores default values because an impl of this trait can set - // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`. - let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR: interior mutability - } -} - -impl AssocConsts for u64 { - const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - fn function() { - let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &::TO_BE_FROZEN_VARIANT; - let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; - } -} - -trait AssocTypes { - type ToBeUnfrozen; - - const TO_BE_UNFROZEN_VARIANT: Option; - const TO_BE_FROZEN_VARIANT: Option; - - // there's no need to test here because it's the exactly same as `trait::AssocTypes` - fn function(); -} - -impl AssocTypes for u64 { - type ToBeUnfrozen = AtomicUsize; - - const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); - const TO_BE_FROZEN_VARIANT: Option = None; - - fn function() { - let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &::TO_BE_FROZEN_VARIANT; - } -} - -enum BothOfCellAndGeneric { - Unfrozen(Cell<*const T>), - Generic(*const T), - Frozen(usize), -} - -impl BothOfCellAndGeneric { - const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); - - fn function() { - let _ = &Self::UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &Self::GENERIC_VARIANT; //~ ERROR: interior mutability - let _ = &Self::FROZEN_VARIANT; - } -} - -fn main() { - // constants defined in foreign crates - let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT; -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr deleted file mode 100644 index 43850384b9036..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr +++ /dev/null @@ -1,79 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:22:14 - | -LL | let _ = &UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/enums.rs:3:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:37:18 - | -LL | let _ = &Self::TO_BE_FROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:41:18 - | -LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:50:18 - | -LL | let _ = &::TO_BE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:52:18 - | -LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:74:18 - | -LL | let _ = &::TO_BE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:91:18 - | -LL | let _ = &Self::UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:92:18 - | -LL | let _ = &Self::GENERIC_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:99:14 - | -LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 9 previous errors - diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs deleted file mode 100644 index fa729b62d7f51..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs +++ /dev/null @@ -1,128 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)] -#![allow(const_item_mutation)] - -use std::borrow::Cow; -use std::cell::{Cell, UnsafeCell}; -use std::fmt::Display; -use std::sync::Once; -use std::sync::atomic::{AtomicUsize, Ordering}; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); -const CELL: Cell = Cell::new(6); -const ATOMIC_TUPLE: ([AtomicUsize; 1], Option>, u8) = ([ATOMIC], None, 7); -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow = Cow::Borrowed("abcdef"); -const NO_ANN: &dyn Display = &70; -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -const ONCE_INIT: Once = Once::new(); - -// This is just a pointer that can be safely dereferenced, -// it's semantically the same as `&'static T`; -// but it isn't allowed to make a static reference from an arbitrary integer value at the moment. -// For more information, please see the issue #5918. -pub struct StaticRef { - ptr: *const T, -} - -impl StaticRef { - /// Create a new `StaticRef` from a raw pointer - /// - /// ## Safety - /// - /// Callers must pass in a reference to statically allocated memory which - /// does not overlap with other values. - pub const unsafe fn new(ptr: *const T) -> StaticRef { - StaticRef { ptr } - } -} - -impl std::ops::Deref for StaticRef { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.ptr } - } -} - -// ICE regression test -mod issue12979 { - use std::cell::UnsafeCell; - - const ATOMIC_TUPLE: (Vec>, ()) = (Vec::new(), ()); - - fn main() { - let _x = &ATOMIC_TUPLE.0; - } -} - -// use a tuple to make sure referencing a field behind a pointer isn't linted. -const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; - -fn main() { - ATOMIC.store(1, Ordering::SeqCst); - //~^ borrow_interior_mutable_const - assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); - //~^ borrow_interior_mutable_const - - let _once = ONCE_INIT; - let _once_ref = &ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_ref_2 = &&ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_ref_4 = &&&&ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_mut = &mut ONCE_INIT; - //~^ borrow_interior_mutable_const - let _atomic_into_inner = ATOMIC.into_inner(); - // these should be all fine. - let _twice = (ONCE_INIT, ONCE_INIT); - let _ref_twice = &(ONCE_INIT, ONCE_INIT); - let _ref_once = &(ONCE_INIT, ONCE_INIT).0; - let _array_twice = [ONCE_INIT, ONCE_INIT]; - let _ref_array_twice = &[ONCE_INIT, ONCE_INIT]; - let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0]; - - // referencing projection is still bad. - let _ = &ATOMIC_TUPLE; - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.0; - //~^ borrow_interior_mutable_const - let _ = &(&&&&ATOMIC_TUPLE).0; - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.0[0]; - //~^ borrow_interior_mutable_const - let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.2; - let _ = (&&&&ATOMIC_TUPLE).0; - let _ = (&&&&ATOMIC_TUPLE).2; - let _ = ATOMIC_TUPLE.0; - let _ = ATOMIC_TUPLE.0[0]; - //~^ borrow_interior_mutable_const - let _ = ATOMIC_TUPLE.1.into_iter(); - let _ = ATOMIC_TUPLE.2; - let _ = &{ ATOMIC_TUPLE }; - - CELL.set(2); - //~^ borrow_interior_mutable_const - assert_eq!(CELL.get(), 6); - //~^ borrow_interior_mutable_const - - assert_eq!(INTEGER, 8); - assert!(STRING.is_empty()); - - let a = ATOMIC; - a.store(4, Ordering::SeqCst); - assert_eq!(a.load(Ordering::SeqCst), 4); - - STATIC_TUPLE.0.store(3, Ordering::SeqCst); - assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3); - assert!(STATIC_TUPLE.1.is_empty()); - - assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. - - let _ = &CELL_REF.0; -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr deleted file mode 100644 index decea153f7173..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr +++ /dev/null @@ -1,119 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:65:5 - | -LL | ATOMIC.store(1, Ordering::SeqCst); - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/others.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:67:16 - | -LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:71:22 - | -LL | let _once_ref = &ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:73:25 - | -LL | let _once_ref_2 = &&ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:75:27 - | -LL | let _once_ref_4 = &&&&ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:77:26 - | -LL | let _once_mut = &mut ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:89:14 - | -LL | let _ = &ATOMIC_TUPLE; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:91:14 - | -LL | let _ = &ATOMIC_TUPLE.0; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:93:19 - | -LL | let _ = &(&&&&ATOMIC_TUPLE).0; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:95:14 - | -LL | let _ = &ATOMIC_TUPLE.0[0]; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:97:13 - | -LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:103:13 - | -LL | let _ = ATOMIC_TUPLE.0[0]; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:109:5 - | -LL | CELL.set(2); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:111:16 - | -LL | assert_eq!(CELL.get(), 6); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 14 previous errors - diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.rs deleted file mode 100644 index bbe5538fbe123..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![deny(clippy::declare_interior_mutable_const)] - -// Inspired by https://github.com/rust-lang/rust/pull/130543#issuecomment-2364828139 - -use std::cell::UnsafeCell; - -trait Trait { - type Assoc; -} - -type Assoc = ::Assoc; - -impl Trait for u8 { - type Assoc = UnsafeCell; -} - -impl Trait for () { - type Assoc = (); -} - -enum MaybeMutable { - Mutable(Assoc), - Immutable(Assoc<()>), -} - -const CELL: Assoc = UnsafeCell::new(0); //~ ERROR: interior mutable -const UNIT: Assoc<()> = (); -const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); //~ ERROR: interior mutable -const IMMUTABLE: MaybeMutable = MaybeMutable::Immutable(UNIT); - -fn print_ref(t: &T) { - let p: *const T = t; - println!("{p:p}") -} - -fn main() { - print_ref(&CELL); //~ ERROR: interior mutability - print_ref(&UNIT); - print_ref(&MUTABLE); //~ ERROR: interior mutability - print_ref(&IMMUTABLE); -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.stderr deleted file mode 100644 index eabaf66560ada..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/borrow_interior_mutable_const/projections.rs:27:1 - | -LL | const CELL: Assoc = UnsafeCell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/projections.rs:2:9 - | -LL | #![deny(clippy::declare_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/borrow_interior_mutable_const/projections.rs:29:1 - | -LL | const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/projections.rs:38:16 - | -LL | print_ref(&CELL); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/projections.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/projections.rs:40:16 - | -LL | print_ref(&MUTABLE); - | ^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 4 previous errors - diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs deleted file mode 100644 index c4878dbe57b29..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs +++ /dev/null @@ -1,219 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const)] - -// this file replicates its `declare` counterpart. Please see it for more discussions. - -use std::borrow::Cow; -use std::cell::Cell; -use std::sync::atomic::{AtomicUsize, Ordering}; - -trait ConcreteTypes { - const ATOMIC: AtomicUsize; - const STRING: String; - - fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::STRING; - } -} - -impl ConcreteTypes for u64 { - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const STRING: String = String::new(); - - fn function() { - // Lint this again since implementers can choose not to borrow it. - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::STRING; - } -} - -// a helper trait used below -trait ConstDefault { - const DEFAULT: Self; -} - -trait GenericTypes { - const TO_REMAIN_GENERIC: T; - const TO_BE_CONCRETE: U; - - fn function() { - let _ = &Self::TO_REMAIN_GENERIC; - } -} - -impl GenericTypes for Vec { - const TO_REMAIN_GENERIC: T = T::DEFAULT; - const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - - fn function() { - let _ = &Self::TO_REMAIN_GENERIC; - let _ = &Self::TO_BE_CONCRETE; - //~^ borrow_interior_mutable_const - } -} - -// a helper type used below -pub struct Wrapper(T); - -trait AssocTypes { - type ToBeFrozen; - type ToBeUnfrozen; - type ToBeGenericParam; - - const TO_BE_FROZEN: Self::ToBeFrozen; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen; - const WRAPPED_TO_BE_UNFROZEN: Wrapper; - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; - - fn function() { - let _ = &Self::TO_BE_FROZEN; - let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - } -} - -impl AssocTypes for Vec { - type ToBeFrozen = u16; - type ToBeUnfrozen = AtomicUsize; - type ToBeGenericParam = T; - - const TO_BE_FROZEN: Self::ToBeFrozen = 12; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); - - fn function() { - let _ = &Self::TO_BE_FROZEN; - let _ = &Self::TO_BE_UNFROZEN; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM; - } -} - -// a helper trait used below -trait AssocTypesHelper { - type NotToBeBounded; - type ToBeBounded; - - const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; -} - -trait AssocTypesFromGenericParam -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; - - fn function() { - let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const - } -} - -impl AssocTypesFromGenericParam for Vec -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); - - fn function() { - let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const - } -} - -trait SelfType: Sized { - const SELF: Self; - const WRAPPED_SELF: Option; - - fn function() { - let _ = &Self::SELF; - let _ = &Self::WRAPPED_SELF; - } -} - -impl SelfType for u64 { - const SELF: Self = 16; - const WRAPPED_SELF: Option = Some(20); - - fn function() { - let _ = &Self::SELF; - let _ = &Self::WRAPPED_SELF; - } -} - -impl SelfType for AtomicUsize { - const SELF: Self = AtomicUsize::new(17); - const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); - - fn function() { - let _ = &Self::SELF; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_SELF; - //~^ borrow_interior_mutable_const - } -} - -trait BothOfCellAndGeneric { - const DIRECT: Cell; - const INDIRECT: Cell<*const T>; - - fn function() { - let _ = &Self::DIRECT; - //~^ borrow_interior_mutable_const - let _ = &Self::INDIRECT; - //~^ borrow_interior_mutable_const - } -} - -impl BothOfCellAndGeneric for Vec { - const DIRECT: Cell = Cell::new(T::DEFAULT); - const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); - - fn function() { - let _ = &Self::DIRECT; - //~^ borrow_interior_mutable_const - let _ = &Self::INDIRECT; - //~^ borrow_interior_mutable_const - } -} - -struct Local(T); - -impl Local -where - T: ConstDefault + AssocTypesHelper, -{ - const ATOMIC: AtomicUsize = AtomicUsize::new(18); - const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - - const GENERIC_TYPE: T = T::DEFAULT; - - const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - - fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::COW; - let _ = &Self::GENERIC_TYPE; - let _ = &Self::ASSOC_TYPE; - let _ = &Self::BOUNDED_ASSOC_TYPE; - //~^ borrow_interior_mutable_const - } -} - -fn main() { - u64::ATOMIC.store(5, Ordering::SeqCst); - //~^ borrow_interior_mutable_const - assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); - //~^ borrow_interior_mutable_const -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr deleted file mode 100644 index cad68ca9260cb..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr +++ /dev/null @@ -1,143 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:15:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/traits.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:27:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:53:18 - | -LL | let _ = &Self::TO_BE_CONCRETE; - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:89:18 - | -LL | let _ = &Self::TO_BE_UNFROZEN; - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:91:18 - | -LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:114:18 - | -LL | let _ = &Self::BOUNDED; - | ^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:128:18 - | -LL | let _ = &Self::BOUNDED; - | ^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:158:18 - | -LL | let _ = &Self::SELF; - | ^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:160:18 - | -LL | let _ = &Self::WRAPPED_SELF; - | ^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:170:18 - | -LL | let _ = &Self::DIRECT; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:172:18 - | -LL | let _ = &Self::INDIRECT; - | ^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:182:18 - | -LL | let _ = &Self::DIRECT; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:184:18 - | -LL | let _ = &Self::INDIRECT; - | ^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:204:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:209:18 - | -LL | let _ = &Self::BOUNDED_ASSOC_TYPE; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:215:5 - | -LL | u64::ATOMIC.store(5, Ordering::SeqCst); - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:217:16 - | -LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 17 previous errors - diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs index 88c2549f4dc50..77329cf5455de 100644 --- a/src/tools/clippy/tests/ui/cast.rs +++ b/src/tools/clippy/tests/ui/cast.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: only some diagnostics have suggestions #![feature(repr128)] #![allow(incomplete_features)] diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs index e924018088674..e5bef2a99d59e 100644 --- a/src/tools/clippy/tests/ui/cast_size.rs +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -1,7 +1,7 @@ //@revisions: 32bit 64bit //@[32bit]ignore-bitwidth: 64 //@[64bit]ignore-bitwidth: 32 -//@no-rustfix +//@no-rustfix: only some diagnostics have suggestions #![warn( clippy::cast_precision_loss, diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs index 145885702a9bc..7635f848cb349 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -4,7 +4,7 @@ clippy::branches_sharing_code, clippy::unnecessary_literal_unwrap )] -//@no-rustfix +//@no-rustfix: has placeholders fn test_nested() { fn nested() { let x = Some(()); diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs index ba0d36d85fe54..785b2473c0530 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs @@ -1,4 +1,4 @@ -//@no-rustfix: overlapping suggestions +//@no-rustfix: has placeholders #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] #![allow( clippy::if_same_then_else, diff --git a/src/tools/clippy/tests/ui/comparison_chain.rs b/src/tools/clippy/tests/ui/comparison_chain.rs index 669690a4d42ce..6db75a4f364d7 100644 --- a/src/tools/clippy/tests/ui/comparison_chain.rs +++ b/src/tools/clippy/tests/ui/comparison_chain.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: has placeholders #![allow(dead_code)] #![warn(clippy::comparison_chain)] @@ -12,11 +12,10 @@ fn f(x: u8, y: u8, z: u8) { a() } - if x > y { - //~^ comparison_chain - + // Ignored: Not all cases are covered + if x < y { a() - } else if x < y { + } else if x > y { b() } @@ -123,9 +122,8 @@ fn g(x: f64, y: f64, z: f64) { } fn h(x: T, y: T, z: T) { + // Ignored: Not all cases are covered if x > y { - //~^ comparison_chain - a() } else if x < y { b() diff --git a/src/tools/clippy/tests/ui/comparison_chain.stderr b/src/tools/clippy/tests/ui/comparison_chain.stderr index 0256573d0d906..ce580bd54a16a 100644 --- a/src/tools/clippy/tests/ui/comparison_chain.stderr +++ b/src/tools/clippy/tests/ui/comparison_chain.stderr @@ -1,12 +1,12 @@ error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:15:5 + --> tests/ui/comparison_chain.rs:29:5 | LL | / if x > y { LL | | LL | | LL | | a() -LL | | } else if x < y { -LL | | b() +... | +LL | | c() LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` | @@ -14,19 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::comparison_chain)]` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:30:5 - | -LL | / if x > y { -LL | | -LL | | -LL | | a() -... | -LL | | c() -LL | | } - | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` - -error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:40:5 + --> tests/ui/comparison_chain.rs:39:5 | LL | / if x > y { LL | | @@ -38,7 +26,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:50:5 + --> tests/ui/comparison_chain.rs:49:5 | LL | / if x > 1 { LL | | @@ -50,19 +38,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&1) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:126:5 - | -LL | / if x > y { -LL | | -LL | | -LL | | a() -LL | | } else if x < y { -LL | | b() -LL | | } - | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` - -error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:134:5 + --> tests/ui/comparison_chain.rs:132:5 | LL | / if x > y { LL | | @@ -74,7 +50,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:144:5 + --> tests/ui/comparison_chain.rs:142:5 | LL | / if x > y { LL | | @@ -86,7 +62,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:251:5 + --> tests/ui/comparison_chain.rs:249:5 | LL | / if x + 1 > y * 2 { LL | | @@ -97,5 +73,5 @@ LL | | "cc" LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match (x + 1).cmp(&(y * 2)) {...}` -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.fixed b/src/tools/clippy/tests/ui/crashes/ice-12491.fixed index ab9c61463e92d..8a31eb9550b23 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-12491.fixed +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.fixed @@ -3,6 +3,6 @@ fn main() { if (true) { // anything一些中文 - //~^^ needless_return + //~^ needless_return } } diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.rs b/src/tools/clippy/tests/ui/crashes/ice-12491.rs index b774bd3788a77..013aadadce516 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-12491.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.rs @@ -4,6 +4,6 @@ fn main() { if (true) { // anything一些中文 return; - //~^^ needless_return + //~^ needless_return } } diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.stderr b/src/tools/clippy/tests/ui/crashes/ice-12491.stderr index 7cc418898e881..4b77299dd5e6c 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-12491.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.stderr @@ -1,10 +1,8 @@ error: unneeded `return` statement - --> tests/ui/crashes/ice-12491.rs:5:24 + --> tests/ui/crashes/ice-12491.rs:6:9 | -LL | // anything一些中文 - | ____________________________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | = note: `-D clippy::needless-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_return)]` diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.1.fixed b/src/tools/clippy/tests/ui/crashes/ice-12979.1.fixed new file mode 100644 index 0000000000000..e68f1c20a8e7c --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.1.fixed @@ -0,0 +1,2 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr +const FOO: u8 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.2.fixed b/src/tools/clippy/tests/ui/crashes/ice-12979.2.fixed new file mode 100644 index 0000000000000..e89fa636d4b24 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.2.fixed @@ -0,0 +1,3 @@ +#![deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.rs b/src/tools/clippy/tests/ui/crashes/ice-12979.rs new file mode 100644 index 0000000000000..a2787291d9e15 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.rs @@ -0,0 +1,3 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.stderr b/src/tools/clippy/tests/ui/crashes/ice-12979.stderr new file mode 100644 index 0000000000000..5e76081616461 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.stderr @@ -0,0 +1,19 @@ +error: empty line after outer attribute + --> tests/ui/crashes/ice-12979.rs:1:1 + | +LL | / #[deny(clippy::declare_interior_mutable_const)] +LL | | + | |_^ +LL | const FOO: u8 = 0; + | --------- the attribute applies to this constant item + | + = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]` + = help: if the empty line is unintentional, remove it +help: if the attribute should apply to the crate use an inner attribute + | +LL | #![deny(clippy::declare_interior_mutable_const)] + | + + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-6840.rs b/src/tools/clippy/tests/ui/crashes/ice-6840.rs index 94481f2489987..f30d83e1eeecd 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6840.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6840.rs @@ -2,6 +2,7 @@ //! This is a reproducer for the ICE 6840: https://github.com/rust-lang/rust-clippy/issues/6840. //! The ICE is caused by `TyCtxt::layout_of` and `is_normalizable` not being strict enough #![allow(dead_code)] +#![deny(clippy::zero_sized_map_values)] // For ICE 14822 use std::collections::HashMap; pub trait Rule { diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.rs b/src/tools/clippy/tests/ui/crashes/ice-9445.rs deleted file mode 100644 index 232b8e4a7959d..0000000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.rs +++ /dev/null @@ -1,4 +0,0 @@ -const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); -//~^ declare_interior_mutable_const - -fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr b/src/tools/clippy/tests/ui/crashes/ice-9445.stderr deleted file mode 100644 index 76689cd6f5c22..0000000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/crashes/ice-9445.rs:1:1 - | -LL | const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: aborting due to 1 previous error - diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs index 1a5119651b539..96b35c9a20c4c 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: overlapping suggestions //@error-in-other-file: #![warn(clippy::dbg_macro)] diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs new file mode 100644 index 0000000000000..c65df275038dc --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs @@ -0,0 +1,200 @@ +#![deny(clippy::declare_interior_mutable_const)] +#![allow(clippy::missing_const_for_thread_local)] + +use core::cell::{Cell, RefCell, UnsafeCell}; +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::ptr; +use core::sync::atomic::AtomicUsize; + +fn main() {} + +const _: Cell = Cell::new(0); +const UNSAFE_CELL: UnsafeCell = UnsafeCell::new(0); //~ declare_interior_mutable_const +const REF_CELL: RefCell = RefCell::new(0); //~ declare_interior_mutable_const +const CELL: Cell = Cell::new(0); //~ declare_interior_mutable_const + +// Constants can't contain pointers or references to type with interior mutability. +const fn make_ptr() -> *const Cell { + ptr::null() +} +const PTR: *const Cell = make_ptr(); + +const fn casted_to_cell_ptr() -> *const Cell { + const VALUE: u32 = 0; + &VALUE as *const _ as *const Cell +} +const TRANSMUTED_PTR: *const Cell = casted_to_cell_ptr(); + +const CELL_TUPLE: (bool, Cell) = (true, Cell::new(0)); //~ declare_interior_mutable_const +const CELL_ARRAY: [Cell; 2] = [Cell::new(0), Cell::new(0)]; //~ declare_interior_mutable_const + +const UNINIT_CELL: MaybeUninit> = MaybeUninit::uninit(); + +struct CellStruct { + x: u32, + cell: Cell, +} +//~v declare_interior_mutable_const +const CELL_STRUCT: CellStruct = CellStruct { + x: 0, + cell: Cell::new(0), +}; + +enum CellEnum { + Cell(Cell), +} +const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); //~ declare_interior_mutable_const + +const NONE_CELL: Option> = None; +const SOME_CELL: Option> = Some(Cell::new(0)); //~ declare_interior_mutable_const + +struct NestedCell([(Option>,); 1]); +const NONE_NESTED_CELL: NestedCell = NestedCell([(None,)]); +const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); //~ declare_interior_mutable_const + +union UnionCell { + cell: ManuallyDrop>, + x: u32, +} +//~v declare_interior_mutable_const +const UNION_CELL: UnionCell = UnionCell { + cell: ManuallyDrop::new(Cell::new(0)), +}; +// Access to either union field is valid so we have to be conservative here. +const UNION_U32: UnionCell = UnionCell { x: 0 }; //~ declare_interior_mutable_const + +struct Assoc; +impl Assoc { + const SELF: Self = Self; + const CELL: Cell = Cell::new(0); //~ declare_interior_mutable_const +} + +struct AssocCell(Cell); +impl AssocCell { + const SELF: Self = Self(Cell::new(0)); //~ declare_interior_mutable_const + const NONE_SELF: Option = None; + const SOME_SELF: Option = Some(Self(Cell::new(0))); //~ declare_interior_mutable_const +} + +trait ConstDefault { + // May or may not be `Freeze` + const DEFAULT: Self; +} +impl ConstDefault for u32 { + const DEFAULT: Self = 0; +} +impl ConstDefault for Cell { + // Interior mutability is forced by the trait. + const DEFAULT: Self = Cell::new(T::DEFAULT); +} +impl ConstDefault for Option> { + // Could have been `None` + const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const +} + +enum GenericEnumCell { + Cell(Cell), + Other(T), +} +impl ConstDefault for GenericEnumCell { + const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const +} +impl GenericEnumCell { + const CELL: Self = Self::DEFAULT; //~ declare_interior_mutable_const + const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); //~ declare_interior_mutable_const + const OTHER: Self = Self::Other(T::DEFAULT); + const FROM_OTHER: Self = Self::OTHER; +} + +enum GenericNestedEnumCell { + GenericEnumCell(GenericEnumCell), + EnumCell(GenericEnumCell), + Other(T), +} +impl GenericNestedEnumCell { + const GENERIC_OTHER: Self = Self::GenericEnumCell(GenericEnumCell::::FROM_OTHER); + const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::::CELL); //~ declare_interior_mutable_const + const ENUM_OTHER: Self = Self::EnumCell(GenericEnumCell::::FROM_OTHER); + const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::::CELL); //~ declare_interior_mutable_const +} + +trait CellTrait: ConstDefault + Sized { + // Must be non-`Freeze` due to the type + const CELL: Cell; //~ declare_interior_mutable_const + // May be non-`Freeze`, but may not be + const OPTION_CELL: Option>; + // May get redefined by the impl, but the default is non-`Freeze`. + const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const + // May get redefined by the impl, but the default is `Freeze`. + const NONE_CELL: Option> = None; +} + +trait CellWithAssoc { + type T; + const DEFAULT: Self::T; + // Must be non-`Freeze` due to the type + const CELL: Cell; //~ declare_interior_mutable_const + // May be non-`Freeze`, but may not be + const OPTION_CELL: Option>; + // May get redefined by the impl, but the default is non-`Freeze`. + const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const + // May get redefined by the impl, but the default is `Freeze`. + const NONE_CELL: Option> = None; +} + +impl CellWithAssoc for () { + type T = u32; + const DEFAULT: Self::T = 0; + const CELL: Cell = Cell::new(0); + const OPTION_CELL: Option> = None; +} + +trait WithAssoc { + type T; + const VALUE: Self::T; +} + +impl WithAssoc for u32 { + type T = Cell; + // The cell comes from the impl block, not the trait. + const VALUE: Self::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithLayeredAssoc { + type T: WithAssoc; + const VALUE: ::T; +} + +impl WithLayeredAssoc for u32 { + type T = u32; + // The cell comes from the impl block, not the trait. + const VALUE: ::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithGenericAssoc { + type T; + const VALUE: Self::T; +} + +impl WithGenericAssoc for u32 { + type T = Cell; + const VALUE: Self::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithGenericAssocCell { + type T; + const VALUE: Self::T>; +} + +impl WithGenericAssocCell for u32 { + type T = Option; + const VALUE: Self::T> = None; +} + +impl WithGenericAssocCell for i32 { + type T = Option; + const VALUE: Self::T> = Some(Cell::new(0)); //~ declare_interior_mutable_const +} + +thread_local!(static THREAD_LOCAL_CELL: Cell = const { Cell::new(0) }); +thread_local!(static THREAD_LOCAL_CELL2: Cell = Cell::new(0)); diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr new file mode 100644 index 0000000000000..9742c17486c5f --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr @@ -0,0 +1,197 @@ +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:12:7 + | +LL | const UNSAFE_CELL: UnsafeCell = UnsafeCell::new(0); + | ^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item +note: the lint level is defined here + --> tests/ui/declare_interior_mutable_const.rs:1:9 + | +LL | #![deny(clippy::declare_interior_mutable_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:13:7 + | +LL | const REF_CELL: RefCell = RefCell::new(0); + | ^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:14:7 + | +LL | const CELL: Cell = Cell::new(0); + | ^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:28:7 + | +LL | const CELL_TUPLE: (bool, Cell) = (true, Cell::new(0)); + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:29:7 + | +LL | const CELL_ARRAY: [Cell; 2] = [Cell::new(0), Cell::new(0)]; + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:38:7 + | +LL | const CELL_STRUCT: CellStruct = CellStruct { + | ^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:46:7 + | +LL | const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:49:7 + | +LL | const SOME_CELL: Option> = Some(Cell::new(0)); + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:53:7 + | +LL | const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); + | ^^^^^^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:60:7 + | +LL | const UNION_CELL: UnionCell = UnionCell { + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:64:7 + | +LL | const UNION_U32: UnionCell = UnionCell { x: 0 }; + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:69:11 + | +LL | const CELL: Cell = Cell::new(0); + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:74:11 + | +LL | const SELF: Self = Self(Cell::new(0)); + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:76:11 + | +LL | const SOME_SELF: Option = Some(Self(Cell::new(0))); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:92:11 + | +LL | const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); + | ^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:100:11 + | +LL | const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); + | ^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:103:11 + | +LL | const CELL: Self = Self::DEFAULT; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:104:11 + | +LL | const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); + | ^^^^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:116:11 + | +LL | const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::::CELL); + | ^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:118:11 + | +LL | const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::::CELL); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:123:11 + | +LL | const CELL: Cell; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:127:11 + | +LL | const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:136:11 + | +LL | const CELL: Cell; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:140:11 + | +LL | const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:160:11 + | +LL | const VALUE: Self::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:171:11 + | +LL | const VALUE: ::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:181:11 + | +LL | const VALUE: Self::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:196:11 + | +LL | const VALUE: Self::T> = Some(Cell::new(0)); + | ^^^^^ + +error: aborting due to 28 previous errors + diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs deleted file mode 100644 index c87468277fb31..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs +++ /dev/null @@ -1,135 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -enum OptionalCell { - Unfrozen(Cell), - Frozen, -} - -// a constant with enums should be linted only when the used variant is unfrozen (#3962). -const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); -//~^ declare_interior_mutable_const -const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - -const fn unfrozen_variant() -> OptionalCell { - OptionalCell::Unfrozen(Cell::new(false)) -} - -const fn frozen_variant() -> OptionalCell { - OptionalCell::Frozen -} - -const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); -//~^ declare_interior_mutable_const -const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant(); - -enum NestedInnermost { - Unfrozen(AtomicUsize), - Frozen, -} - -struct NestedInner { - inner: NestedInnermost, -} - -enum NestedOuter { - NestedInner(NestedInner), - NotNested(usize), -} - -struct NestedOutermost { - outer: NestedOuter, -} - -// a constant with enums should be linted according to its value, no matter how structs involve. -const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { - //~^ declare_interior_mutable_const - outer: NestedOuter::NestedInner(NestedInner { - inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), - }), -}; -const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost { - outer: NestedOuter::NestedInner(NestedInner { - inner: NestedInnermost::Frozen, - }), -}; - -trait AssocConsts { - // When there's no default value, lint it only according to its type. - // Further details are on the corresponding code (`NonCopyConst::check_trait_item`). - const TO_BE_UNFROZEN_VARIANT: OptionalCell; - //~^ declare_interior_mutable_const - const TO_BE_FROZEN_VARIANT: OptionalCell; - //~^ declare_interior_mutable_const - - // Lint default values accordingly. - const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - //~^ declare_interior_mutable_const - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; -} - -// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it -// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'. -impl AssocConsts for u64 { - const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - // even if this sets an unfrozen variant, the lint ignores it. - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); -} - -// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters -// here are values; and I think substituted generics at definitions won't appear in MIR. -trait AssocTypes { - type ToBeUnfrozen; - - const TO_BE_UNFROZEN_VARIANT: Option; - const TO_BE_FROZEN_VARIANT: Option; -} - -impl AssocTypes for u64 { - type ToBeUnfrozen = AtomicUsize; - - const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); - //~^ declare_interior_mutable_const - const TO_BE_FROZEN_VARIANT: Option = None; -} - -// Use raw pointers since direct generics have a false negative at the type level. -enum BothOfCellAndGeneric { - Unfrozen(Cell<*const T>), - Generic(*const T), - Frozen(usize), -} - -impl BothOfCellAndGeneric { - const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - //~^ declare_interior_mutable_const - - // This is a false positive. The argument about this is on `is_value_unfrozen_raw` - const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - //~^ declare_interior_mutable_const - - const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); - - // This is what is likely to be a false negative when one tries to fix - // the `GENERIC_VARIANT` false positive. - const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); - //~^ declare_interior_mutable_const -} - -// associated types here is basically the same as the one above. -trait BothOfCellAndGenericWithAssocType { - type AssocType; - - const UNFROZEN_VARIANT: BothOfCellAndGeneric = - //~^ declare_interior_mutable_const - BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - //~^ declare_interior_mutable_const - const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr deleted file mode 100644 index 32839d14f0ed3..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:12:1 - | -LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:24:1 - | -LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:47:1 - | -LL | / const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { -LL | | -LL | | outer: NestedOuter::NestedInner(NestedInner { -LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), -LL | | }), -LL | | }; - | |__^ - | - = help: consider making this a static item - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:62:5 - | -LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:64:5 - | -LL | const TO_BE_FROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:68:5 - | -LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:95:5 - | -LL | const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:108:5 - | -LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:112:5 - | -LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:119:5 - | -LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:127:5 - | -LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric = -LL | | -LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - | |____________________________________________________________________^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:130:5 - | -LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 12 previous errors - diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs deleted file mode 100644 index 7ce04a3f2c340..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs +++ /dev/null @@ -1,76 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::borrow::Cow; -use std::cell::Cell; -use std::fmt::Display; -use std::ptr; -use std::sync::Once; -use std::sync::atomic::AtomicUsize; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); -//~^ declare_interior_mutable_const -const CELL: Cell = Cell::new(6); -//~^ declare_interior_mutable_const -const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); -//~^ declare_interior_mutable_const - -macro_rules! declare_const { - ($name:ident: $ty:ty = $e:expr) => { - const $name: $ty = $e; - //~^ declare_interior_mutable_const - }; -} -declare_const!(_ONCE: Once = Once::new()); - -// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. - -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow = Cow::Borrowed("abcdef"); -// note: a const item of Cow is used in the `postgres` package. - -const NO_ANN: &dyn Display = &70; - -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -// there should be no lints on the line above line - -mod issue_8493 { - use std::cell::Cell; - - thread_local! { - static _BAR: Cell = const { Cell::new(0) }; - } - - macro_rules! issue_8493 { - () => { - const _BAZ: Cell = Cell::new(0); - //~^ declare_interior_mutable_const - static _FOOBAR: () = { - thread_local! { - static _VAR: Cell = const { Cell::new(0) }; - } - }; - }; - } - - issue_8493!(); -} - -#[repr(C, align(8))] -struct NoAtomic(usize); -#[repr(C, align(8))] -struct WithAtomic(AtomicUsize); - -const fn with_non_null() -> *const WithAtomic { - const NO_ATOMIC: NoAtomic = NoAtomic(0); - (&NO_ATOMIC as *const NoAtomic).cast() -} -const WITH_ATOMIC: *const WithAtomic = with_non_null(); - -struct Generic(T); -impl Generic { - const RAW_POINTER: *const Cell = ptr::null(); -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr deleted file mode 100644 index 09299b290416d..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:10:1 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this a static item - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:12:1 - | -LL | const CELL: Cell = Cell::new(6); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:14:1 - | -LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this a static item - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:19:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(_ONCE: Once = Once::new()); - | ----------------------------------------- in this macro invocation - | - = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:47:13 - | -LL | const _BAZ: Cell = Cell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | issue_8493!(); - | ------------- in this macro invocation - | - = note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 5 previous errors - diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs deleted file mode 100644 index d3139be6859f3..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs +++ /dev/null @@ -1,162 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::borrow::Cow; -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -macro_rules! declare_const { - ($name:ident: $ty:ty = $e:expr) => { - const $name: $ty = $e; - //~^ declare_interior_mutable_const - }; -} - -// a constant whose type is a concrete type should be linted at the definition site. -trait ConcreteTypes { - const ATOMIC: AtomicUsize; - //~^ declare_interior_mutable_const - const INTEGER: u64; - const STRING: String; - declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); -} - -impl ConcreteTypes for u64 { - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const INTEGER: u64 = 10; - const STRING: String = String::new(); -} - -// a helper trait used below -trait ConstDefault { - const DEFAULT: Self; -} - -// a constant whose type is a generic type should be linted at the implementation site. -trait GenericTypes { - const TO_REMAIN_GENERIC: T; - const TO_BE_CONCRETE: U; - - const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC; - declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC); -} - -impl GenericTypes for u64 { - const TO_REMAIN_GENERIC: T = T::DEFAULT; - const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - //~^ declare_interior_mutable_const -} - -// a helper type used below -struct Wrapper(T); - -// a constant whose type is an associated type should be linted at the implementation site, too. -trait AssocTypes { - type ToBeFrozen; - type ToBeUnfrozen; - type ToBeGenericParam; - - const TO_BE_FROZEN: Self::ToBeFrozen; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen; - const WRAPPED_TO_BE_UNFROZEN: Wrapper; - // to ensure it can handle things when a generic type remains after normalization. - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; -} - -impl AssocTypes for Vec { - type ToBeFrozen = u16; - type ToBeUnfrozen = AtomicUsize; - type ToBeGenericParam = T; - - const TO_BE_FROZEN: Self::ToBeFrozen = 12; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - //~^ declare_interior_mutable_const - const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); - //~^ declare_interior_mutable_const - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); -} - -// a helper trait used below -trait AssocTypesHelper { - type NotToBeBounded; - type ToBeBounded; - - const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; -} - -// a constant whose type is an assoc type originated from a generic param bounded at the definition -// site should be linted at there. -trait AssocTypesFromGenericParam -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; - //~^ declare_interior_mutable_const -} - -impl AssocTypesFromGenericParam for u64 -where - T: AssocTypesHelper, -{ - // an associated type could remain unknown in a trait impl. - const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); -} - -// a constant whose type is `Self` should be linted at the implementation site as well. -// (`Option` requires `Sized` bound.) -trait SelfType: Sized { - const SELF: Self; - // this was the one in the original issue (#5050). - const WRAPPED_SELF: Option; -} - -impl SelfType for u64 { - const SELF: Self = 16; - const WRAPPED_SELF: Option = Some(20); -} - -impl SelfType for AtomicUsize { - // this (interior mutable `Self` const) exists in `parking_lot`. - // `const_trait_impl` will replace it in the future, hopefully. - const SELF: Self = AtomicUsize::new(17); - //~^ declare_interior_mutable_const - const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); - //~^ declare_interior_mutable_const -} - -// Even though a constant contains a generic type, if it also have an interior mutable type, -// it should be linted at the definition site. -trait BothOfCellAndGeneric { - const DIRECT: Cell; - //~^ declare_interior_mutable_const - const INDIRECT: Cell<*const T>; - //~^ declare_interior_mutable_const -} - -impl BothOfCellAndGeneric for u64 { - const DIRECT: Cell = Cell::new(T::DEFAULT); - //~^ declare_interior_mutable_const - const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); -} - -struct Local(T); - -// a constant in an inherent impl are essentially the same as a normal const item -// except there can be a generic or associated type. -impl Local -where - T: ConstDefault + AssocTypesHelper, -{ - const ATOMIC: AtomicUsize = AtomicUsize::new(18); - //~^ declare_interior_mutable_const - const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - - const GENERIC_TYPE: T = T::DEFAULT; - - const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - //~^ declare_interior_mutable_const -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr deleted file mode 100644 index b03dd7a084033..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr +++ /dev/null @@ -1,88 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:16:5 - | -LL | const ATOMIC: AtomicUsize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:9:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); - | ---------------------------------------------------------- in this macro invocation - | - = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:45:5 - | -LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:71:5 - | -LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:73:5 - | -LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:93:5 - | -LL | const BOUNDED: T::ToBeBounded; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:122:5 - | -LL | const SELF: Self = AtomicUsize::new(17); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:124:5 - | -LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:131:5 - | -LL | const DIRECT: Cell; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:133:5 - | -LL | const INDIRECT: Cell<*const T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:138:5 - | -LL | const DIRECT: Cell = Cell::new(T::DEFAULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:151:5 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:158:5 - | -LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 13 previous errors - diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs index 2787f6406fe39..6b69bdd29ceac 100644 --- a/src/tools/clippy/tests/ui/deprecated.rs +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -2,20 +2,20 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. -#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` +#![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops` #![warn(clippy::extend_from_slice)] //~ ERROR: lint `clippy::extend_from_slice` -#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` -#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` -#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` +#![warn(clippy::match_on_vec_items)] //~ ERROR: lint `clippy::match_on_vec_items` #![warn(clippy::misaligned_transmute)] //~ ERROR: lint `clippy::misaligned_transmute` -#![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops` +#![warn(clippy::option_map_or_err_ok)] //~ ERROR: lint `clippy::option_map_or_err_ok` +#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` +#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` +#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` +#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` +#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` #![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization` +#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` +#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` #![warn(clippy::unused_collect)] //~ ERROR: lint `clippy::unused_collect` -#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` -#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` -#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` #![warn(clippy::wrong_pub_self_convention)] //~ ERROR: lint `clippy::wrong_pub_self_convention` -#![warn(clippy::option_map_or_err_ok)] //~ ERROR: lint `clippy::option_map_or_err_ok` -#![warn(clippy::match_on_vec_items)] //~ ERROR: lint `clippy::match_on_vec_items` fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr index 604732405c370..07e59d33d608b 100644 --- a/src/tools/clippy/tests/ui/deprecated.stderr +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -1,8 +1,8 @@ -error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can +error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy --> tests/ui/deprecated.rs:5:9 | -LL | #![warn(clippy::should_assert_eq)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::assign_ops)] + | ^^^^^^^^^^^^^^^^^^ | = note: `-D renamed-and-removed-lints` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` @@ -13,83 +13,83 @@ error: lint `clippy::extend_from_slice` has been removed: `Vec::extend_from_slic LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator +error: lint `clippy::match_on_vec_items` has been removed: `clippy::indexing_slicing` covers indexing and slicing on `Vec<_>` --> tests/ui/deprecated.rs:7:9 | -LL | #![warn(clippy::range_step_by_zero)] +LL | #![warn(clippy::match_on_vec_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable +error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` --> tests/ui/deprecated.rs:8:9 | -LL | #![warn(clippy::unstable_as_slice)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::misaligned_transmute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable +error: lint `clippy::option_map_or_err_ok` has been removed: `clippy::manual_ok_or` covers this case --> tests/ui/deprecated.rs:9:9 | -LL | #![warn(clippy::unstable_as_mut_slice)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::option_map_or_err_ok)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` +error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config --> tests/ui/deprecated.rs:10:9 | -LL | #![warn(clippy::misaligned_transmute)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::pub_enum_variant_names)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy +error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator --> tests/ui/deprecated.rs:11:9 | -LL | #![warn(clippy::assign_ops)] - | ^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::range_step_by_zero)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower +error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 --> tests/ui/deprecated.rs:12:9 | -LL | #![warn(clippy::unsafe_vector_initialization)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::regex_macro)] + | ^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` +error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated --> tests/ui/deprecated.rs:13:9 | -LL | #![warn(clippy::unused_collect)] +LL | #![warn(clippy::replace_consts)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated +error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can --> tests/ui/deprecated.rs:14:9 | -LL | #![warn(clippy::replace_consts)] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::should_assert_eq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 +error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower --> tests/ui/deprecated.rs:15:9 | -LL | #![warn(clippy::regex_macro)] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unsafe_vector_initialization)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config +error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable --> tests/ui/deprecated.rs:16:9 | -LL | #![warn(clippy::pub_enum_variant_names)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unstable_as_mut_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config +error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable --> tests/ui/deprecated.rs:17:9 | -LL | #![warn(clippy::wrong_pub_self_convention)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::option_map_or_err_ok` has been removed: `clippy::manual_ok_or` covers this case +error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` --> tests/ui/deprecated.rs:18:9 | -LL | #![warn(clippy::option_map_or_err_ok)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unused_collect)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::match_on_vec_items` has been removed: `clippy::indexing_slicing` covers indexing and slicing on `Vec<_>` +error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config --> tests/ui/deprecated.rs:19:9 | -LL | #![warn(clippy::match_on_vec_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::wrong_pub_self_convention)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs index e9218bbb40940..73f62ac124698 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: requires manual changes #![warn(clippy::double_ended_iterator_last)] // Should not be linted because applying the lint would move the original iterator. This can only be diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed index b1600862a8f60..419cf2354f89b 100644 --- a/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed +++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed @@ -23,4 +23,12 @@ struct MyTupleStruct(usize, String); // should not trigger lint struct MySingleTupleStruct(usize); // should not trigger lint struct MyUnitLikeStruct; // should not trigger lint +macro_rules! empty_struct { + ($s:ident) => { + struct $s {} + }; +} + +empty_struct!(FromMacro); + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs index 1f69c4be9ec77..90c415c122063 100644 --- a/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs +++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs @@ -23,4 +23,12 @@ struct MyTupleStruct(usize, String); // should not trigger lint struct MySingleTupleStruct(usize); // should not trigger lint struct MyUnitLikeStruct; // should not trigger lint +macro_rules! empty_struct { + ($s:ident) => { + struct $s {} + }; +} + +empty_struct!(FromMacro); + fn main() {} diff --git a/src/tools/clippy/tests/ui/entry_unfixable.rs b/src/tools/clippy/tests/ui/entry_unfixable.rs index dbdacf950569c..c4c055572086e 100644 --- a/src/tools/clippy/tests/ui/entry_unfixable.rs +++ b/src/tools/clippy/tests/ui/entry_unfixable.rs @@ -1,6 +1,5 @@ -#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![allow(clippy::needless_pass_by_value, clippy::collapsible_if)] #![warn(clippy::map_entry)] -//@no-rustfix use std::collections::HashMap; use std::hash::Hash; diff --git a/src/tools/clippy/tests/ui/entry_unfixable.stderr b/src/tools/clippy/tests/ui/entry_unfixable.stderr index 9f9956d351b29..0197d2ab4cf9c 100644 --- a/src/tools/clippy/tests/ui/entry_unfixable.stderr +++ b/src/tools/clippy/tests/ui/entry_unfixable.stderr @@ -1,5 +1,5 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` - --> tests/ui/entry_unfixable.rs:28:13 + --> tests/ui/entry_unfixable.rs:27:13 | LL | / if !self.values.contains_key(&name) { LL | | @@ -14,7 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::map_entry)]` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> tests/ui/entry_unfixable.rs:43:5 + --> tests/ui/entry_unfixable.rs:42:5 | LL | / if hm.contains_key(&key) { LL | | @@ -26,7 +26,7 @@ LL | | } | |_____^ error: usage of `contains_key` followed by `insert` on a `HashMap` - --> tests/ui/entry_unfixable.rs:81:13 + --> tests/ui/entry_unfixable.rs:80:13 | LL | / if self.globals.contains_key(&name) { LL | | diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed index 99d09774d16d3..8a8c2e1939c83 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.fixed +++ b/src/tools/clippy/tests/ui/excessive_precision.fixed @@ -79,6 +79,9 @@ fn main() { // issue #2840 let num = 0.000_000_000_01e-10f64; + // issue #6341 + let exponential: f64 = 4.886506780521244E-03; + // issue #7744 let _ = 2.225_073_858_507_201e-308_f64; //~^ excessive_precision diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs index a542fb2e7e3cd..5dcf55cb92735 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.rs +++ b/src/tools/clippy/tests/ui/excessive_precision.rs @@ -79,6 +79,9 @@ fn main() { // issue #2840 let num = 0.000_000_000_01e-10f64; + // issue #6341 + let exponential: f64 = 4.886506780521244E-03; + // issue #7744 let _ = 2.225_073_858_507_201_1e-308_f64; //~^ excessive_precision diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr index 934a367e10659..f5eeadf0c8cb0 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.stderr +++ b/src/tools/clippy/tests/ui/excessive_precision.stderr @@ -157,7 +157,7 @@ LL + let bad_bige32: f32 = 1.123_456_8E-10; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:83:13 + --> tests/ui/excessive_precision.rs:86:13 | LL | let _ = 2.225_073_858_507_201_1e-308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + let _ = 2.225_073_858_507_201e-308_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:87:13 + --> tests/ui/excessive_precision.rs:90:13 | LL | let _ = 1.000_000_000_000_001e-324_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let _ = 0_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:98:20 + --> tests/ui/excessive_precision.rs:101:20 | LL | const _: f64 = 3.0000000000000000e+00; | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.rs b/src/tools/clippy/tests/ui/explicit_counter_loop.rs index 8340d99ace226..13934785d7b15 100644 --- a/src/tools/clippy/tests/ui/explicit_counter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.rs @@ -1,6 +1,6 @@ #![warn(clippy::explicit_counter_loop)] #![allow(clippy::uninlined_format_args, clippy::useless_vec)] -//@no-rustfix +//@no-rustfix: suggestion does not remove the `+= 1` fn main() { let mut vec = vec![1, 2, 3, 4]; let mut _index = 0; diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed index 0d1a5f80f3d5a..619329a6ade0c 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed @@ -8,7 +8,8 @@ clippy::needless_borrow, clippy::no_effect, clippy::uninlined_format_args, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::deref_addrof )] use std::ops::{Deref, DerefMut}; @@ -87,9 +88,6 @@ fn main() { let b = &*opt_a.unwrap(); //~^ explicit_deref_methods - // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified - // syntax - Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); ::deref(&Aaa); @@ -139,4 +137,9 @@ fn main() { let no_lint = NoLint(42); let b = no_lint.deref(); let b = no_lint.deref_mut(); + + let _ = &*&"foo"; //~ explicit_deref_methods + let mut x = String::new(); + let _ = &&mut **&mut x; //~ explicit_deref_methods + let _ = &&mut ***(&mut &mut x); //~ explicit_deref_methods } diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.rs b/src/tools/clippy/tests/ui/explicit_deref_methods.rs index 8d4a899cd2607..9f2d513283c9b 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.rs +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.rs @@ -8,7 +8,8 @@ clippy::needless_borrow, clippy::no_effect, clippy::uninlined_format_args, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::deref_addrof )] use std::ops::{Deref, DerefMut}; @@ -87,9 +88,6 @@ fn main() { let b = opt_a.unwrap().deref(); //~^ explicit_deref_methods - // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified - // syntax - Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); ::deref(&Aaa); @@ -139,4 +137,9 @@ fn main() { let no_lint = NoLint(42); let b = no_lint.deref(); let b = no_lint.deref_mut(); + + let _ = &Deref::deref(&"foo"); //~ explicit_deref_methods + let mut x = String::new(); + let _ = &DerefMut::deref_mut(&mut x); //~ explicit_deref_methods + let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); //~ explicit_deref_methods } diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr index 2ca376cba00bd..a81e2f60317b3 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:54:19 + --> tests/ui/explicit_deref_methods.rs:55:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try: `&*a` @@ -8,70 +8,88 @@ LL | let b: &str = a.deref(); = help: to override `-D warnings` add `#[allow(clippy::explicit_deref_methods)]` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:57:23 + --> tests/ui/explicit_deref_methods.rs:58:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:61:39 + --> tests/ui/explicit_deref_methods.rs:62:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:61:50 + --> tests/ui/explicit_deref_methods.rs:62:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:20 + --> tests/ui/explicit_deref_methods.rs:66:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:69:11 + --> tests/ui/explicit_deref_methods.rs:70:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:74:28 + --> tests/ui/explicit_deref_methods.rs:75:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:77:13 + --> tests/ui/explicit_deref_methods.rs:78:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:80:28 + --> tests/ui/explicit_deref_methods.rs:81:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:83:19 + --> tests/ui/explicit_deref_methods.rs:84:19 | LL | let b: &str = a.deref().deref(); | ^^^^^^^^^^^^^^^^^ help: try: `&**a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:87:13 + --> tests/ui/explicit_deref_methods.rs:88:13 | LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*opt_a.unwrap()` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:125:31 + --> tests/ui/explicit_deref_methods.rs:123:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` -error: aborting due to 12 previous errors +error: explicit `deref` method call + --> tests/ui/explicit_deref_methods.rs:141:14 + | +LL | let _ = &Deref::deref(&"foo"); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` + +error: explicit `deref_mut` method call + --> tests/ui/explicit_deref_methods.rs:143:14 + | +LL | let _ = &DerefMut::deref_mut(&mut x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` + +error: explicit `deref_mut` method call + --> tests/ui/explicit_deref_methods.rs:144:14 + | +LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed index 2b68906ae39fa..c1b3c478eeb91 100644 --- a/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed +++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed @@ -73,3 +73,16 @@ fn main() { for _ in S.into_iter::() {} } + +fn issue14630() { + macro_rules! mac { + (into_iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in dbg!([1, 2]) {} + //~^ explicit_into_iter_loop + + for _ in mac!(into_iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs index ca335b62d9068..581e0dadcecb3 100644 --- a/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs @@ -73,3 +73,16 @@ fn main() { for _ in S.into_iter::() {} } + +fn issue14630() { + macro_rules! mac { + (into_iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in dbg!([1, 2]).into_iter() {} + //~^ explicit_into_iter_loop + + for _ in mac!(into_iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr index 1c3156755d4ee..26fb11e004822 100644 --- a/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr +++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr @@ -37,5 +37,11 @@ error: it is more concise to loop over containers instead of using explicit iter LL | for _ in mr.into_iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *mr` -error: aborting due to 6 previous errors +error: it is more concise to loop over containers instead of using explicit iteration methods + --> tests/ui/explicit_into_iter_loop.rs:84:14 + | +LL | for _ in dbg!([1, 2]).into_iter() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `dbg!([1, 2])` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed index cd0898dfc3672..f246ec61800e1 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed @@ -183,3 +183,16 @@ pub fn issue_13184() { let rvalues = &values; for _ in rvalues.iter() {} } + +fn issue14630() { + macro_rules! mac { + (iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in &dbg!([1, 2]) {} + //~^ explicit_iter_loop + + for _ in mac!(iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_iter_loop.rs index 02405280ce423..35f4fb7097d89 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.rs @@ -183,3 +183,16 @@ pub fn issue_13184() { let rvalues = &values; for _ in rvalues.iter() {} } + +fn issue14630() { + macro_rules! mac { + (iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in dbg!([1, 2]).iter() {} + //~^ explicit_iter_loop + + for _ in mac!(iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr index 3816bb4db98b3..575dbe7813d75 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr @@ -112,5 +112,11 @@ error: it is more concise to loop over references to containers instead of using LL | for _ in r.iter() {} | ^^^^^^^^ help: to write this more concisely, try: `r` -error: aborting due to 18 previous errors +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> tests/ui/explicit_iter_loop.rs:194:14 + | +LL | for _ in dbg!([1, 2]).iter() {} + | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&dbg!([1, 2])` + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs index 68294292502ac..5d29e0317bb2f 100644 --- a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs +++ b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs @@ -1,6 +1,5 @@ -#![allow(clippy::question_mark, unused)] +#![allow(clippy::question_mark)] #![warn(clippy::filter_map_bool_then)] -//@no-rustfix fn issue11617() { let mut x: Vec = vec![0; 10]; diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr index 2025958136ba6..2990423973e10 100644 --- a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr +++ b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr @@ -1,5 +1,5 @@ error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:7:48 + --> tests/ui/filter_map_bool_then_unfixable.rs:6:48 | LL | let _ = (0..x.len()).zip(x.clone().iter()).filter_map(|(i, v)| { | ________________________________________________^ @@ -16,7 +16,7 @@ LL | | }); = help: to override `-D warnings` add `#[allow(clippy::filter_map_bool_then)]` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:23:26 + --> tests/ui/filter_map_bool_then_unfixable.rs:22:26 | LL | let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); = help: consider using `filter` then `map` instead error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:27:14 + --> tests/ui/filter_map_bool_then_unfixable.rs:26:14 | LL | .filter_map(|&x| if let Some(x) = x { x } else { return None }.then(|| do_something(()))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | .filter_map(|&x| if let Some(x) = x { x } else { return None }. = help: consider using `filter` then `map` instead error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:29:26 + --> tests/ui/filter_map_bool_then_unfixable.rs:28:26 | LL | let _ = x.iter().filter_map(|&x| { | __________________________^ @@ -47,7 +47,7 @@ LL | | }); = help: consider using `filter` then `map` instead error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:47:26 + --> tests/ui/filter_map_bool_then_unfixable.rs:46:26 | LL | let _ = x.iter().filter_map(|&x| { | __________________________^ diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs index ebc3acb1b77f6..7067434953d6e 100644 --- a/src/tools/clippy/tests/ui/ifs_same_cond.rs +++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs @@ -6,19 +6,25 @@ fn ifs_same_cond() { let b = false; if b { + //~^ ifs_same_cond } else if b { + } + + if b { //~^ ifs_same_cond + } else if b { + } else if b { } if a == 1 { - } else if a == 1 { //~^ ifs_same_cond + } else if a == 1 { } if 2 * a == 1 { + //~^ ifs_same_cond } else if 2 * a == 2 { } else if 2 * a == 1 { - //~^ ifs_same_cond } else if a == 1 { } @@ -50,8 +56,8 @@ fn ifs_same_cond() { fn issue10272() { let a = String::from("ha"); if a.contains("ah") { - } else if a.contains("ah") { //~^ ifs_same_cond + } else if a.contains("ah") { // Trigger this lint } else if a.contains("ha") { diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.stderr b/src/tools/clippy/tests/ui/ifs_same_cond.stderr index df21e6f1b8262..7acbc1a6399b6 100644 --- a/src/tools/clippy/tests/ui/ifs_same_cond.stderr +++ b/src/tools/clippy/tests/ui/ifs_same_cond.stderr @@ -1,52 +1,52 @@ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:9:15 - | -LL | } else if b { - | ^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui/ifs_same_cond.rs:8:8 | LL | if b { | ^ +LL | +LL | } else if b { + | ^ + | = note: `-D clippy::ifs-same-cond` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:14:15 - | -LL | } else if a == 1 { - | ^^^^^^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui/ifs_same_cond.rs:13:8 | +LL | if b { + | ^ +LL | +LL | } else if b { + | ^ +LL | } else if b { + | ^ + +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:19:8 + | LL | if a == 1 { | ^^^^^^ +LL | +LL | } else if a == 1 { + | ^^^^^^ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:20:15 - | -LL | } else if 2 * a == 1 { - | ^^^^^^^^^^ - | -note: same as this - --> tests/ui/ifs_same_cond.rs:18:8 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:24:8 | LL | if 2 * a == 1 { | ^^^^^^^^^^ +... +LL | } else if 2 * a == 1 { + | ^^^^^^^^^^ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:53:15 - | -LL | } else if a.contains("ah") { - | ^^^^^^^^^^^^^^^^ - | -note: same as this - --> tests/ui/ifs_same_cond.rs:52:8 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:58:8 | LL | if a.contains("ah") { | ^^^^^^^^^^^^^^^^ +LL | +LL | } else if a.contains("ah") { + | ^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.rs b/src/tools/clippy/tests/ui/impl_trait_in_params.rs index 2039f6339a878..72e3e068c9c79 100644 --- a/src/tools/clippy/tests/ui/impl_trait_in_params.rs +++ b/src/tools/clippy/tests/ui/impl_trait_in_params.rs @@ -1,7 +1,7 @@ #![allow(unused)] #![warn(clippy::impl_trait_in_params)] -//@no-rustfix +//@no-rustfix: has placeholders pub trait Trait {} pub trait AnotherTrait {} diff --git a/src/tools/clippy/tests/ui/infinite_loop.rs b/src/tools/clippy/tests/ui/infinite_loop.rs index 4a0968918bfbe..8ff7f3b0c18d8 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.rs +++ b/src/tools/clippy/tests/ui/infinite_loop.rs @@ -1,5 +1,3 @@ -//@no-rustfix - fn fn_val(i: i32) -> i32 { unimplemented!() } diff --git a/src/tools/clippy/tests/ui/infinite_loop.stderr b/src/tools/clippy/tests/ui/infinite_loop.stderr index 7ba1374d64f46..04da9776c3025 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.stderr +++ b/src/tools/clippy/tests/ui/infinite_loop.stderr @@ -1,5 +1,5 @@ error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:22:11 + --> tests/ui/infinite_loop.rs:20:11 | LL | while y < 10 { | ^^^^^^ @@ -8,7 +8,7 @@ LL | while y < 10 { = note: `#[deny(clippy::while_immutable_condition)]` on by default error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:29:11 + --> tests/ui/infinite_loop.rs:27:11 | LL | while y < 10 && x < 3 { | ^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:38:11 + --> tests/ui/infinite_loop.rs:36:11 | LL | while !cond { | ^^^^^ @@ -24,7 +24,7 @@ LL | while !cond { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:84:11 + --> tests/ui/infinite_loop.rs:82:11 | LL | while i < 3 { | ^^^^^ @@ -32,7 +32,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:91:11 + --> tests/ui/infinite_loop.rs:89:11 | LL | while i < 3 && j > 0 { | ^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:97:11 + --> tests/ui/infinite_loop.rs:95:11 | LL | while i < 3 { | ^^^^^ @@ -48,7 +48,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:114:11 + --> tests/ui/infinite_loop.rs:112:11 | LL | while i < 3 { | ^^^^^ @@ -56,7 +56,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:121:11 + --> tests/ui/infinite_loop.rs:119:11 | LL | while i < 3 { | ^^^^^ @@ -64,7 +64,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:189:15 + --> tests/ui/infinite_loop.rs:187:15 | LL | while self.count < n { | ^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | while self.count < n { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:199:11 + --> tests/ui/infinite_loop.rs:197:11 | LL | while y < 10 { | ^^^^^^ @@ -82,7 +82,7 @@ LL | while y < 10 { = help: rewrite it as `if cond { loop { } }` error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:208:11 + --> tests/ui/infinite_loop.rs:206:11 | LL | while y < 10 { | ^^^^^^ diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index eaa8d00880682..fcd1f795fff09 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: multiple suggestions add `-> !` to the same fn //@aux-build:proc_macros.rs #![allow(clippy::never_loop)] diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.rs b/src/tools/clippy/tests/ui/into_iter_without_iter.rs index 45e34b3930ad0..f0b86e5620e9b 100644 --- a/src/tools/clippy/tests/ui/into_iter_without_iter.rs +++ b/src/tools/clippy/tests/ui/into_iter_without_iter.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: suggestions reference out of scope lifetimes/types //@aux-build:proc_macros.rs #![warn(clippy::into_iter_without_iter)] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/iter_next_loop.rs b/src/tools/clippy/tests/ui/iter_next_loop.rs index 32711c7ef6239..8e62ed963b900 100644 --- a/src/tools/clippy/tests/ui/iter_next_loop.rs +++ b/src/tools/clippy/tests/ui/iter_next_loop.rs @@ -15,3 +15,16 @@ fn main() { let u = Unrelated(&[0]); for _v in u.next() {} // no error } + +fn issue14630() { + macro_rules! mac { + (next $e:expr) => { + $e.iter().next() + }; + } + + for _ in dbg!([1, 2].iter()).next() {} + //~^ iter_next_loop + + for _ in mac!(next [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/iter_next_loop.stderr b/src/tools/clippy/tests/ui/iter_next_loop.stderr index acc55031c3b29..c076e86db93bb 100644 --- a/src/tools/clippy/tests/ui/iter_next_loop.stderr +++ b/src/tools/clippy/tests/ui/iter_next_loop.stderr @@ -7,5 +7,11 @@ LL | for _ in x.iter().next() {} = note: `-D clippy::iter-next-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::iter_next_loop)]` -error: aborting due to 1 previous error +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> tests/ui/iter_next_loop.rs:26:14 + | +LL | for _ in dbg!([1, 2].iter()).next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/iter_out_of_bounds.rs b/src/tools/clippy/tests/ui/iter_out_of_bounds.rs index b34e4ad782409..6458b1342dcd5 100644 --- a/src/tools/clippy/tests/ui/iter_out_of_bounds.rs +++ b/src/tools/clippy/tests/ui/iter_out_of_bounds.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #![deny(clippy::iter_out_of_bounds)] #![allow(clippy::useless_vec)] diff --git a/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr b/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr index 19ac60b9d0ac0..1b3a99e1e9457 100644 --- a/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr +++ b/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr @@ -1,18 +1,18 @@ error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:12:14 + --> tests/ui/iter_out_of_bounds.rs:10:14 | LL | for _ in [1, 2, 3].iter().skip(4) { | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this operation is useless and will create an empty iterator note: the lint level is defined here - --> tests/ui/iter_out_of_bounds.rs:3:9 + --> tests/ui/iter_out_of_bounds.rs:1:9 | LL | #![deny(clippy::iter_out_of_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:17:19 + --> tests/ui/iter_out_of_bounds.rs:15:19 | LL | for (i, _) in [1, 2, 3].iter().take(4).enumerate() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | for (i, _) in [1, 2, 3].iter().take(4).enumerate() { = note: this operation is useless and the returned iterator will simply yield the same items error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:24:14 + --> tests/ui/iter_out_of_bounds.rs:22:14 | LL | for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} = note: this operation is useless and the returned iterator will simply yield the same items error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:27:14 + --> tests/ui/iter_out_of_bounds.rs:25:14 | LL | for _ in [1, 2, 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | for _ in [1, 2, 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:30:14 + --> tests/ui/iter_out_of_bounds.rs:28:14 | LL | for _ in [1; 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | for _ in [1; 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:36:14 + --> tests/ui/iter_out_of_bounds.rs:34:14 | LL | for _ in vec![1, 2, 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | for _ in vec![1, 2, 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:39:14 + --> tests/ui/iter_out_of_bounds.rs:37:14 | LL | for _ in vec![1; 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | for _ in vec![1; 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:43:14 + --> tests/ui/iter_out_of_bounds.rs:41:14 | LL | for _ in x.iter().skip(4) {} | ^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | for _ in x.iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:47:14 + --> tests/ui/iter_out_of_bounds.rs:45:14 | LL | for _ in x.iter().skip(n) {} | ^^^^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | for _ in x.iter().skip(n) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:52:14 + --> tests/ui/iter_out_of_bounds.rs:50:14 | LL | for _ in empty().skip(1) {} | ^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | for _ in empty().skip(1) {} = note: this operation is useless and will create an empty iterator error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:55:14 + --> tests/ui/iter_out_of_bounds.rs:53:14 | LL | for _ in empty().take(1) {} | ^^^^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL | for _ in empty().take(1) {} = note: this operation is useless and the returned iterator will simply yield the same items error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:58:14 + --> tests/ui/iter_out_of_bounds.rs:56:14 | LL | for _ in std::iter::once(1).skip(2) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | for _ in std::iter::once(1).skip(2) {} = note: this operation is useless and will create an empty iterator error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:61:14 + --> tests/ui/iter_out_of_bounds.rs:59:14 | LL | for _ in std::iter::once(1).take(2) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL | for _ in std::iter::once(1).take(2) {} = note: this operation is useless and the returned iterator will simply yield the same items error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:64:14 + --> tests/ui/iter_out_of_bounds.rs:62:14 | LL | for x in [].iter().take(1) { | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_inspect.fixed b/src/tools/clippy/tests/ui/manual_inspect.fixed index ec87fe217aee6..9b768dbad700c 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.fixed +++ b/src/tools/clippy/tests/ui/manual_inspect.fixed @@ -107,7 +107,7 @@ fn main() { let _ = || { let _x = x; }; - return ; + return; } println!("test"); }); diff --git a/src/tools/clippy/tests/ui/manual_inspect.stderr b/src/tools/clippy/tests/ui/manual_inspect.stderr index eb98f9f5995a3..78b085fdfca30 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.stderr +++ b/src/tools/clippy/tests/ui/manual_inspect.stderr @@ -98,7 +98,7 @@ LL | if x.is_empty() { LL | let _ = || { LL ~ let _x = x; LL | }; -LL ~ return ; +LL ~ return; LL | } LL ~ println!("test"); | diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed index 294d1e1506dec..090f0fd30c555 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed @@ -60,7 +60,26 @@ fn main() { let _ = size_of::() * 5 * s_i32.len(); // Ok (MISSED OPPORTUNITY) } -const fn _const(s_i32: &[i32]) { - // True negative: - let _ = s_i32.len() * size_of::(); // Ok, can't use size_of_val in const +#[clippy::msrv = "1.85"] +const fn const_ok(s_i32: &[i32]) { + let _ = std::mem::size_of_val(s_i32); + //~^ manual_slice_size_calculation +} + +#[clippy::msrv = "1.84"] +const fn const_before_msrv(s_i32: &[i32]) { + let _ = s_i32.len() * size_of::(); +} + +fn issue_14802() { + struct IcedSlice { + dst: [u8], + } + + impl IcedSlice { + fn get_len(&self) -> usize { + std::mem::size_of_val(&self.dst) + //~^ manual_slice_size_calculation + } + } } diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs index ae5225663139b..3c19a0eb5cea1 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs @@ -60,7 +60,26 @@ fn main() { let _ = size_of::() * 5 * s_i32.len(); // Ok (MISSED OPPORTUNITY) } -const fn _const(s_i32: &[i32]) { - // True negative: - let _ = s_i32.len() * size_of::(); // Ok, can't use size_of_val in const +#[clippy::msrv = "1.85"] +const fn const_ok(s_i32: &[i32]) { + let _ = s_i32.len() * size_of::(); + //~^ manual_slice_size_calculation +} + +#[clippy::msrv = "1.84"] +const fn const_before_msrv(s_i32: &[i32]) { + let _ = s_i32.len() * size_of::(); +} + +fn issue_14802() { + struct IcedSlice { + dst: [u8], + } + + impl IcedSlice { + fn get_len(&self) -> usize { + self.dst.len() * size_of::() + //~^ manual_slice_size_calculation + } + } } diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr index f07e97a1c8638..8e9b49e4bf295 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr @@ -55,5 +55,17 @@ error: manual slice size calculation LL | let _ = external!(&[1u64][..]).len() * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(external!(&[1u64][..]))` -error: aborting due to 9 previous errors +error: manual slice size calculation + --> tests/ui/manual_slice_size_calculation.rs:65:13 + | +LL | let _ = s_i32.len() * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)` + +error: manual slice size calculation + --> tests/ui/manual_slice_size_calculation.rs:81:13 + | +LL | self.dst.len() * size_of::() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(&self.dst)` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms.fixed b/src/tools/clippy/tests/ui/match_same_arms.fixed new file mode 100644 index 0000000000000..31684a5759fe9 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms.fixed @@ -0,0 +1,142 @@ +#![allow(clippy::manual_range_patterns)] +#![warn(clippy::match_same_arms)] + +pub enum Abc { + A, + B, + C, +} + +fn match_same_arms() { + let _ = match Abc::A { + Abc::B => 1, + _ => 0, + //~^ match_same_arms + }; + + match 0 { + 1 => 'a', + _ => 'b', + //~^ match_same_arms + }; + + match (1, 2, 3) { + (1, .., 3) | (.., 3) => 42, + //~^ match_same_arms + _ => 0, + }; + + let _ = match 42 { + //~^ match_same_arms + 42 | 51 => 1, + 41 | 52 => 2, + //~^ match_same_arms + _ => 0, + }; + + let _ = match 42 { + //~^ match_same_arms + 1 | 2 | 3 => 2, + 4 => 3, + _ => 0, + }; +} + +mod issue4244 { + #[derive(PartialEq, PartialOrd, Eq, Ord)] + pub enum CommandInfo { + BuiltIn { name: String, about: Option }, + External { name: String, path: std::path::PathBuf }, + } + + impl CommandInfo { + pub fn name(&self) -> String { + match self { + //~^ match_same_arms + CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(), + } + } + } +} + +macro_rules! m { + (foo) => {}; + (bar) => {}; +} +macro_rules! foo { + () => { + 1 + }; +} +macro_rules! bar { + () => { + 1 + }; +} + +fn main() { + let x = 0; + let _ = match 0 { + 0 => { + m!(foo); + x + }, + 1 => { + m!(bar); + x + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + m!(foo); + 0 + }, + 1 => { + m!(bar); + 0 + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + let mut x = 0; + #[cfg(not_enabled)] + { + x = 5; + } + #[cfg(not(not_enabled))] + { + x = 6; + } + x + }, + 1 => { + let mut x = 0; + #[cfg(also_not_enabled)] + { + x = 5; + } + #[cfg(not(also_not_enabled))] + { + x = 6; + } + x + }, + _ => 0, + }; + + let _ = match 0 { + 0 => foo!(), + 1 => bar!(), + _ => 1, + }; + + let _ = match 0 { + 0 => cfg!(not_enabled), + 1 => cfg!(also_not_enabled), + _ => false, + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs index 55441948e91b6..39bee01bac22b 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.rs +++ b/src/tools/clippy/tests/ui/match_same_arms.rs @@ -1,4 +1,4 @@ -//@no-rustfix: overlapping suggestions +#![allow(clippy::manual_range_patterns)] #![warn(clippy::match_same_arms)] pub enum Abc { @@ -10,9 +10,17 @@ pub enum Abc { fn match_same_arms() { let _ = match Abc::A { Abc::A => 0, - //~^ match_same_arms Abc::B => 1, _ => 0, + //~^ match_same_arms + }; + + match 0 { + 1 => 'a', + 2 => 'b', + 3 => 'b', + _ => 'b', + //~^ match_same_arms }; match (1, 2, 3) { @@ -24,8 +32,8 @@ fn match_same_arms() { let _ = match 42 { 42 => 1, - 51 => 1, //~^ match_same_arms + 51 => 1, 41 => 2, //~^ match_same_arms 52 => 2, @@ -34,11 +42,9 @@ fn match_same_arms() { let _ = match 42 { 1 => 2, - 2 => 2, //~^ match_same_arms - //~| match_same_arms + 2 => 2, 3 => 2, - //~^ match_same_arms 4 => 3, _ => 0, }; @@ -55,8 +61,8 @@ mod issue4244 { pub fn name(&self) -> String { match self { CommandInfo::BuiltIn { name, .. } => name.to_string(), - CommandInfo::External { name, .. } => name.to_string(), //~^ match_same_arms + CommandInfo::External { name, .. } => name.to_string(), } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr index 3744b83d89cc8..8aa60f8357663 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms.stderr @@ -1,118 +1,121 @@ -error: this match arm has an identical body to the `_` wildcard arm +error: these match arms have identical bodies --> tests/ui/match_same_arms.rs:12:9 | -LL | / Abc::A => 0, -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms.rs:15:9 - | +LL | Abc::A => 0, + | ^^^^^^^^^^^ +LL | Abc::B => 1, LL | _ => 0, - | ^^^^^^ + | ^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise remove the non-wildcard arm + | +LL - Abc::A => 0, + | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:19:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:20:9 + | +LL | 2 => 'b', + | ^^^^^^^^ +LL | 3 => 'b', + | ^^^^^^^^ +LL | _ => 'b', + | ^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arms + | +LL - 2 => 'b', +LL - 3 => 'b', +LL + _ => 'b', + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:27:9 | LL | (1, .., 3) => 42, | ^^^^^^^^^^^^^^^^ +LL | +LL | (.., 3) => 42, + | ^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (1, .., 3) | (.., 3) => 42, LL | LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:27:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:34:9 | +LL | 42 => 1, + | ^^^^^^^ +LL | LL | 51 => 1, | ^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 42 => 1, -LL - 51 => 1, -LL + 51 | 42 => 1, +LL ~ +LL ~ 42 | 51 => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:29:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:37:9 | LL | 41 => 2, | ^^^^^^^ +LL | +LL | 52 => 2, + | ^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ 41 | 52 => 2, LL | LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:37:9 - | -LL | 2 => 2, - | ^^^^^^ - | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm - | -LL - 1 => 2, -LL - 2 => 2, -LL + 2 | 1 => 2, - | - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:40:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:44:9 | -LL | 3 => 2, +LL | 1 => 2, | ^^^^^^ - | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm - | -LL ~ 2 => 2, -LL | LL | -LL ~ 3 | 1 => 2, - | - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:37:9 - | LL | 2 => 2, | ^^^^^^ +LL | 3 => 2, + | ^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ 2 | 3 => 2, -LL | -LL | LL ~ +LL ~ 1 | 2 | 3 => 2, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:58:17 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:63:17 | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | CommandInfo::External { name, .. } => name.to_string(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - CommandInfo::BuiltIn { name, .. } => name.to_string(), -LL - CommandInfo::External { name, .. } => name.to_string(), -LL + CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(), +LL ~ +LL ~ CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(), | -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.fixed b/src/tools/clippy/tests/ui/match_same_arms2.fixed index 0d93e2c728d75..cb860cef1e688 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.fixed +++ b/src/tools/clippy/tests/ui/match_same_arms2.fixed @@ -14,7 +14,7 @@ fn foo() -> bool { fn match_same_arms() { let _ = match 42 { - //~^^^^^^^^^ match_same_arms + //~v match_same_arms _ => { foo(); let mut a = 42 + [23].len() as i32; @@ -27,14 +27,14 @@ fn match_same_arms() { }; let _ = match 42 { - 51 | 42 => foo(), //~^ match_same_arms + 42 | 51 => foo(), _ => true, }; let _ = match Some(42) { - None | Some(_) => 24, //~^ match_same_arms + Some(_) | None => 24, }; let _ = match Some(42) { @@ -55,8 +55,8 @@ fn match_same_arms() { }; match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) => bar(a), //~^ match_same_arms + (Some(a), None) | (None, Some(a)) => bar(a), _ => (), } @@ -69,8 +69,8 @@ fn match_same_arms() { }; let _ = match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) if a == 42 => a, //~^ match_same_arms + (Some(a), None) | (None, Some(a)) if a == 42 => a, _ => 0, }; @@ -124,8 +124,8 @@ fn match_same_arms() { // False negative #2251. match x { Ok(_tmp) => println!("ok"), - Ok(_) | Ok(3) => println!("ok"), //~^ match_same_arms + Ok(3) | Ok(_) => println!("ok"), Err(_) => { unreachable!(); }, @@ -149,10 +149,10 @@ fn match_same_arms() { // still lint if the tokens are the same match 0 { - 1 | 0 => { + //~^^^ match_same_arms + 0 | 1 => { empty!(0); }, - //~^^^ match_same_arms x => { empty!(x); }, @@ -208,9 +208,9 @@ fn main() { // Suggest moving `Foo::X(0)` down. let _ = match Foo::X(0) { - Foo::Y(_) | Foo::Z(0) => 2, - Foo::Z(_) | Foo::X(0) => 1, //~^ match_same_arms + Foo::Y(_) | Foo::Z(0) => 2, + Foo::X(0) | Foo::Z(_) => 1, _ => 0, }; @@ -230,10 +230,10 @@ fn main() { // Lint. let _ = match None { + //~^ match_same_arms Some(Bar { y: 10, z: 0, .. }) => 2, None => 50, - Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, - //~^ match_same_arms + Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1, _ => 200, }; @@ -246,8 +246,8 @@ fn main() { }; let _ = match 0 { - 1 | 0 => cfg!(not_enable), //~^ match_same_arms + 0 | 1 => cfg!(not_enable), _ => false, }; } @@ -262,9 +262,34 @@ mod with_lifetime { impl<'a> MaybeStaticStr<'a> { fn get(&self) -> &'a str { match *self { - MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, //~^ match_same_arms + MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s, } } } } + +fn lint_levels() { + match 1 { + 0 => "a", + 1 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; + + match 2 { + 0 => "a", + 1 | 2 => "b", + //~^ match_same_arms + #[allow(clippy::match_same_arms)] + _ => "b", + }; + + match 3 { + 0 => "a", + 1 | 2 => "b", + //~^ match_same_arms + #[expect(clippy::match_same_arms)] + _ => "b", + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index b0ebc4784f331..0fd5d76e7d3e7 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -23,7 +23,7 @@ fn match_same_arms() { a = -31 - a; a }, - //~^^^^^^^^^ match_same_arms + //~v match_same_arms _ => { foo(); let mut a = 42 + [23].len() as i32; @@ -37,15 +37,15 @@ fn match_same_arms() { let _ = match 42 { 42 => foo(), - 51 => foo(), //~^ match_same_arms + 51 => foo(), _ => true, }; let _ = match Some(42) { Some(_) => 24, - None => 24, //~^ match_same_arms + None => 24, }; let _ = match Some(42) { @@ -67,8 +67,8 @@ fn match_same_arms() { match (Some(42), Some(42)) { (Some(a), None) => bar(a), - (None, Some(a)) => bar(a), //~^ match_same_arms + (None, Some(a)) => bar(a), _ => (), } @@ -82,8 +82,8 @@ fn match_same_arms() { let _ = match (Some(42), Some(42)) { (Some(a), None) if a == 42 => a, - (None, Some(a)) if a == 42 => a, //~^ match_same_arms + (None, Some(a)) if a == 42 => a, _ => 0, }; @@ -140,8 +140,8 @@ fn match_same_arms() { match x { Ok(_tmp) => println!("ok"), Ok(3) => println!("ok"), - Ok(_) => println!("ok"), //~^ match_same_arms + Ok(_) => println!("ok"), Err(_) => { unreachable!(); }, @@ -168,10 +168,10 @@ fn match_same_arms() { 0 => { empty!(0); }, + //~^^^ match_same_arms 1 => { empty!(0); }, - //~^^^ match_same_arms x => { empty!(x); }, @@ -229,9 +229,9 @@ fn main() { // Suggest moving `Foo::X(0)` down. let _ = match Foo::X(0) { Foo::X(0) => 1, + //~^ match_same_arms Foo::Y(_) | Foo::Z(0) => 2, Foo::Z(_) => 1, - //~^ match_same_arms _ => 0, }; @@ -252,10 +252,10 @@ fn main() { // Lint. let _ = match None { Some(Bar { x: 0, y: 5, .. }) => 1, + //~^ match_same_arms Some(Bar { y: 10, z: 0, .. }) => 2, None => 50, Some(Bar { y: 0, x: 5, .. }) => 1, - //~^ match_same_arms _ => 200, }; @@ -269,8 +269,8 @@ fn main() { let _ = match 0 { 0 => cfg!(not_enable), - 1 => cfg!(not_enable), //~^ match_same_arms + 1 => cfg!(not_enable), _ => false, }; } @@ -286,9 +286,36 @@ mod with_lifetime { fn get(&self) -> &'a str { match *self { MaybeStaticStr::Static(s) => s, - MaybeStaticStr::Borrowed(s) => s, //~^ match_same_arms + MaybeStaticStr::Borrowed(s) => s, } } } } + +fn lint_levels() { + match 1 { + 0 => "a", + 1 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; + + match 2 { + 0 => "a", + 1 => "b", + //~^ match_same_arms + 2 => "b", + #[allow(clippy::match_same_arms)] + _ => "b", + }; + + match 3 { + 0 => "a", + 1 => "b", + //~^ match_same_arms + 2 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index 21a8743cc3242..f3031619cce56 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -1,4 +1,4 @@ -error: this match arm has an identical body to the `_` wildcard arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:17:9 | LL | / 42 => { @@ -6,14 +6,10 @@ LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; LL | | if true { ... | +LL | | a LL | | }, -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:27:9 - | + | |_________^ +LL | LL | / _ => { LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; @@ -21,134 +17,169 @@ LL | | if true { ... | LL | | a LL | | }, - | |_________^ + | |_________^ the wildcard arm + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise remove the non-wildcard arm + | +LL - 42 => { +LL - foo(); +LL - let mut a = 42 + [23].len() as i32; +LL - if true { +LL - a += 7; +LL - } +LL - a = -31 - a; +LL - a +LL - }, + | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:40:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:39:9 | +LL | 42 => foo(), + | ^^^^^^^^^^^ +LL | LL | 51 => foo(), | ^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 42 => foo(), -LL - 51 => foo(), -LL + 51 | 42 => foo(), +LL ~ +LL ~ 42 | 51 => foo(), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:47:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:46:9 | +LL | Some(_) => 24, + | ^^^^^^^^^^^^^ +LL | LL | None => 24, | ^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - Some(_) => 24, -LL - None => 24, -LL + None | Some(_) => 24, +LL ~ +LL ~ Some(_) | None => 24, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:70:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:69:9 | +LL | (Some(a), None) => bar(a), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | (None, Some(a)) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - (Some(a), None) => bar(a), -LL - (None, Some(a)) => bar(a), -LL + (None, Some(a)) | (Some(a), None) => bar(a), +LL ~ +LL ~ (Some(a), None) | (None, Some(a)) => bar(a), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:85:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:84:9 | +LL | (Some(a), None) if a == 42 => a, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | (None, Some(a)) if a == 42 => a, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - (Some(a), None) if a == 42 => a, -LL - (None, Some(a)) if a == 42 => a, -LL + (None, Some(a)) | (Some(a), None) if a == 42 => a, +LL ~ +LL ~ (Some(a), None) | (None, Some(a)) if a == 42 => a, | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:91:9 | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | (.., Some(a)) => bar(a), + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (Some(a), ..) | (.., Some(a)) => bar(a), LL | LL ~ _ => (), | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:126:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | (Ok(_), Some(x)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), LL | LL ~ _ => println!("err"), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:143:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:142:9 | +LL | Ok(3) => println!("ok"), + | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | Ok(_) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - Ok(3) => println!("ok"), -LL - Ok(_) => println!("ok"), -LL + Ok(_) | Ok(3) => println!("ok"), +LL ~ +LL ~ Ok(3) | Ok(_) => println!("ok"), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:171:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:168:9 | +LL | / 0 => { +LL | | empty!(0); +LL | | }, + | |_________^ +LL | LL | / 1 => { LL | | empty!(0); LL | | }, | |_________^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 0 => { -LL - empty!(0); -LL - }, -LL - 1 => { -LL + 1 | 0 => { +LL ~ +LL ~ 0 | 1 => { | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:222:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ +... +LL | Foo::Z(_) => 1, + | ^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ Foo::X(0) | Foo::Z(_) => 1, LL | @@ -156,60 +187,106 @@ LL | Foo::X(_) | Foo::Y(_) => 2, LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:233:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:231:9 | +LL | Foo::X(0) => 1, + | ^^^^^^^^^^^^^^ +... LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ Foo::Y(_) | Foo::Z(0) => 2, -LL ~ Foo::Z(_) | Foo::X(0) => 1, +LL ~ +LL | Foo::Y(_) | Foo::Z(0) => 2, +LL ~ Foo::X(0) | Foo::Z(_) => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:257:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:254:9 | +LL | Some(Bar { x: 0, y: 5, .. }) => 1, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ Some(Bar { y: 10, z: 0, .. }) => 2, +LL ~ +LL | Some(Bar { y: 10, z: 0, .. }) => 2, LL | None => 50, -LL ~ Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, +LL ~ Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:272:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:271:9 | +LL | 0 => cfg!(not_enable), + | ^^^^^^^^^^^^^^^^^^^^^ +LL | LL | 1 => cfg!(not_enable), | ^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 0 => cfg!(not_enable), -LL - 1 => cfg!(not_enable), -LL + 1 | 0 => cfg!(not_enable), +LL ~ +LL ~ 0 | 1 => cfg!(not_enable), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:289:17 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:288:17 | +LL | MaybeStaticStr::Static(s) => s, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | MaybeStaticStr::Borrowed(s) => s, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s, | -LL - MaybeStaticStr::Static(s) => s, -LL - MaybeStaticStr::Borrowed(s) => s, -LL + MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, + +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:306:9 + | +LL | 1 => "b", + | ^^^^^^^^ +LL | +LL | 2 => "b", + | ^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ 1 | 2 => "b", +LL | +LL ~ #[allow(clippy::match_same_arms)] + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:315:9 + | +LL | 1 => "b", + | ^^^^^^^^ +LL | +LL | 2 => "b", + | ^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ 1 | 2 => "b", +LL | +LL ~ #[expect(clippy::match_same_arms)] | -error: aborting due to 14 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed index 0c9398933b80e..61a5bd0323a88 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed @@ -7,14 +7,22 @@ fn repeat() -> ! { panic!() } +#[deny(non_exhaustive_omitted_patterns)] pub fn f(x: Ordering) { - #[deny(non_exhaustive_omitted_patterns)] match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => repeat(), - _ => repeat(), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + } + + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), } } @@ -28,21 +36,21 @@ mod f { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => repeat(), - _ => repeat(), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), } } } -// Below should still lint +// Below can still suggest removing the other patterns pub fn g(x: Ordering) { match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } @@ -54,8 +62,8 @@ mod g { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs index 304a9e5c28e76..66f65eb39d0a9 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs @@ -7,15 +7,25 @@ fn repeat() -> ! { panic!() } +#[deny(non_exhaustive_omitted_patterns)] pub fn f(x: Ordering) { - #[deny(non_exhaustive_omitted_patterns)] match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), + //~^ match_same_arms _ => repeat(), } + + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel => repeat(), + //~^ match_same_arms + Ordering::SeqCst | _ => repeat(), + } } mod f { @@ -29,12 +39,13 @@ mod f { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), + //~^ match_same_arms _ => repeat(), } } } -// Below should still lint +// Below can still suggest removing the other patterns pub fn g(x: Ordering) { match x { @@ -42,8 +53,8 @@ pub fn g(x: Ordering) { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } @@ -56,8 +67,8 @@ mod g { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr index aa7f8c95dce84..03252f346c6cc 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr @@ -1,32 +1,80 @@ -error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:44:9 - | -LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:46:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:16:9 | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | _ => repeat(), | ^^^^^^^^^^^^^ + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + | -error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:58:13 +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:25:9 + | +LL | Ordering::AcqRel => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | Ordering::SeqCst | _ => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), -LL | | - | |____________^ help: try removing the arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:60:13 +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:41:13 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | _ => repeat(), | ^^^^^^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:55:9 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | _ => repeat(), + | ^^^^^^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arm + | +LL - Ordering::AcqRel | Ordering::SeqCst => repeat(), + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:69:13 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | _ => repeat(), + | ^^^^^^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arm + | +LL - Ordering::AcqRel | Ordering::SeqCst => repeat(), + | -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed new file mode 100644 index 0000000000000..882ff6bf8944a --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed @@ -0,0 +1,40 @@ +#![allow(clippy::eq_op)] +#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] + +fn main() { + let mut a = 5; + a += 1; + //~^ misrefactored_assign_op + + a += 1; + //~^ misrefactored_assign_op + + a -= 1; + //~^ misrefactored_assign_op + + a *= 99; + //~^ misrefactored_assign_op + + a *= 42; + //~^ misrefactored_assign_op + + a /= 2; + //~^ misrefactored_assign_op + + a %= 5; + //~^ misrefactored_assign_op + + a &= 1; + //~^ misrefactored_assign_op + + a *= a; + //~^ misrefactored_assign_op + + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed new file mode 100644 index 0000000000000..de3a0f1710d24 --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed @@ -0,0 +1,40 @@ +#![allow(clippy::eq_op)] +#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] + +fn main() { + let mut a = 5; + a = a + a + 1; + //~^ misrefactored_assign_op + + a = a + 1 + a; + //~^ misrefactored_assign_op + + a = a - (a - 1); + //~^ misrefactored_assign_op + + a = a * a * 99; + //~^ misrefactored_assign_op + + a = a * 42 * a; + //~^ misrefactored_assign_op + + a = a / (a / 2); + //~^ misrefactored_assign_op + + a = a % (a % 5); + //~^ misrefactored_assign_op + + a = a & a & 1; + //~^ misrefactored_assign_op + + a = a * a * a; + //~^ misrefactored_assign_op + + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.rs b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs new file mode 100644 index 0000000000000..62d83d1619c1e --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs @@ -0,0 +1,40 @@ +#![allow(clippy::eq_op)] +#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] + +fn main() { + let mut a = 5; + a += a + 1; + //~^ misrefactored_assign_op + + a += 1 + a; + //~^ misrefactored_assign_op + + a -= a - 1; + //~^ misrefactored_assign_op + + a *= a * 99; + //~^ misrefactored_assign_op + + a *= 42 * a; + //~^ misrefactored_assign_op + + a /= a / 2; + //~^ misrefactored_assign_op + + a %= a % 5; + //~^ misrefactored_assign_op + + a &= a & 1; + //~^ misrefactored_assign_op + + a *= a * a; + //~^ misrefactored_assign_op + + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} diff --git a/src/tools/clippy/tests/ui/assign_ops2.stderr b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr similarity index 80% rename from src/tools/clippy/tests/ui/assign_ops2.stderr rename to src/tools/clippy/tests/ui/misrefactored_assign_op.stderr index d9ecd3f8b230c..63f3a3e28f12c 100644 --- a/src/tools/clippy/tests/ui/assign_ops2.stderr +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr @@ -1,5 +1,5 @@ error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:8:5 + --> tests/ui/misrefactored_assign_op.rs:6:5 | LL | a += a + 1; | ^^^^^^^^^^ @@ -18,7 +18,7 @@ LL + a = a + a + 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:11:5 + --> tests/ui/misrefactored_assign_op.rs:9:5 | LL | a += 1 + a; | ^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + a = a + 1 + a; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:14:5 + --> tests/ui/misrefactored_assign_op.rs:12:5 | LL | a -= a - 1; | ^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + a = a - (a - 1); | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:17:5 + --> tests/ui/misrefactored_assign_op.rs:15:5 | LL | a *= a * 99; | ^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL + a = a * a * 99; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:20:5 + --> tests/ui/misrefactored_assign_op.rs:18:5 | LL | a *= 42 * a; | ^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL + a = a * 42 * a; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:23:5 + --> tests/ui/misrefactored_assign_op.rs:21:5 | LL | a /= a / 2; | ^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + a = a / (a / 2); | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:26:5 + --> tests/ui/misrefactored_assign_op.rs:24:5 | LL | a %= a % 5; | ^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + a = a % (a % 5); | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:29:5 + --> tests/ui/misrefactored_assign_op.rs:27:5 | LL | a &= a & 1; | ^^^^^^^^^^ @@ -137,7 +137,7 @@ LL + a = a & a & 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:32:5 + --> tests/ui/misrefactored_assign_op.rs:30:5 | LL | a *= a * a; | ^^^^^^^^^^ @@ -153,14 +153,5 @@ LL - a *= a * a; LL + a = a * a * a; | -error: manual implementation of an assign operation - --> tests/ui/assign_ops2.rs:71:5 - | -LL | buf = buf + cows.clone(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` - | - = note: `-D clippy::assign-op-pattern` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/needless_match.fixed b/src/tools/clippy/tests/ui/needless_match.fixed index b2c2bbfaa3613..41acf44023f63 100644 --- a/src/tools/clippy/tests/ui/needless_match.fixed +++ b/src/tools/clippy/tests/ui/needless_match.fixed @@ -301,4 +301,16 @@ pub fn issue13574() -> Option<()> { None } +fn issue14754(t: Result) -> Result { + let _ = match t { + Ok(v) => Ok::<_, &'static str>(v), + err @ Err(_) => return err, + }; + println!("Still here"); + let x = t; + //~^^^^ needless_match + println!("Still here"); + x +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.rs b/src/tools/clippy/tests/ui/needless_match.rs index 1cb670edc60fb..936653b961bbb 100644 --- a/src/tools/clippy/tests/ui/needless_match.rs +++ b/src/tools/clippy/tests/ui/needless_match.rs @@ -364,4 +364,19 @@ pub fn issue13574() -> Option<()> { None } +fn issue14754(t: Result) -> Result { + let _ = match t { + Ok(v) => Ok::<_, &'static str>(v), + err @ Err(_) => return err, + }; + println!("Still here"); + let x = match t { + Ok(v) => Ok::<_, &'static str>(v), + err @ Err(_) => err, + }; + //~^^^^ needless_match + println!("Still here"); + x +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.stderr b/src/tools/clippy/tests/ui/needless_match.stderr index 719b0ef884696..5195ecdfa555e 100644 --- a/src/tools/clippy/tests/ui/needless_match.stderr +++ b/src/tools/clippy/tests/ui/needless_match.stderr @@ -151,5 +151,15 @@ LL | | None LL | | } | |_________^ help: replace it with: `A` -error: aborting due to 14 previous errors +error: this match expression is unnecessary + --> tests/ui/needless_match.rs:373:13 + | +LL | let x = match t { + | _____________^ +LL | | Ok(v) => Ok::<_, &'static str>(v), +LL | | err @ Err(_) => err, +LL | | }; + | |_____^ help: replace it with: `t` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index ad625ad6d5076..17d3862cd8668 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -84,14 +84,14 @@ fn test_macro_call() -> i32 { } fn test_void_fun() { - //~^^ needless_return + //~^ needless_return } fn test_void_if_fun(b: bool) { if b { - //~^^ needless_return + //~^ needless_return } else { - //~^^ needless_return + //~^ needless_return } } @@ -108,7 +108,7 @@ fn test_nested_match(x: u32) { 0 => (), 1 => { let _ = 42; - //~^^ needless_return + //~^ needless_return }, _ => (), //~^ needless_return @@ -156,7 +156,7 @@ mod issue6501 { fn test_closure() { let _ = || { - //~^^ needless_return + //~^ needless_return }; let _ = || {}; //~^ needless_return @@ -220,14 +220,14 @@ async fn async_test_macro_call() -> i32 { } async fn async_test_void_fun() { - //~^^ needless_return + //~^ needless_return } async fn async_test_void_if_fun(b: bool) { if b { - //~^^ needless_return + //~^ needless_return } else { - //~^^ needless_return + //~^ needless_return } } @@ -354,7 +354,7 @@ fn issue9503(x: usize) -> isize { mod issue9416 { pub fn with_newline() { let _ = 42; - //~^^ needless_return + //~^ needless_return } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 41d7e5bdd5065..1c6e7ffa1ee8c 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -85,16 +85,16 @@ fn test_macro_call() -> i32 { fn test_void_fun() { return; - //~^^ needless_return + //~^ needless_return } fn test_void_if_fun(b: bool) { if b { return; - //~^^ needless_return + //~^ needless_return } else { return; - //~^^ needless_return + //~^ needless_return } } @@ -112,7 +112,7 @@ fn test_nested_match(x: u32) { 1 => { let _ = 42; return; - //~^^ needless_return + //~^ needless_return }, _ => return, //~^ needless_return @@ -161,7 +161,7 @@ mod issue6501 { fn test_closure() { let _ = || { return; - //~^^ needless_return + //~^ needless_return }; let _ = || return; //~^ needless_return @@ -226,16 +226,16 @@ async fn async_test_macro_call() -> i32 { async fn async_test_void_fun() { return; - //~^^ needless_return + //~^ needless_return } async fn async_test_void_if_fun(b: bool) { if b { return; - //~^^ needless_return + //~^ needless_return } else { return; - //~^^ needless_return + //~^ needless_return } } @@ -363,7 +363,7 @@ mod issue9416 { pub fn with_newline() { let _ = 42; return; - //~^^ needless_return + //~^ needless_return } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index 80863b9b62b20..26dd265379be0 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -133,12 +133,10 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:86:21 + --> tests/ui/needless_return.rs:87:5 | -LL | fn test_void_fun() { - | _____________________^ -LL | | return; - | |__________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -148,12 +146,10 @@ LL + fn test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:92:11 + --> tests/ui/needless_return.rs:93:9 | -LL | if b { - | ___________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -163,12 +159,10 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:95:13 + --> tests/ui/needless_return.rs:96:9 | -LL | } else { - | _____________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -190,12 +184,10 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:113:24 + --> tests/ui/needless_return.rs:114:13 | -LL | let _ = 42; - | ________________________^ -LL | | return; - | |__________________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -253,12 +245,10 @@ LL + bar.unwrap_or_else(|_| {}) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:162:21 + --> tests/ui/needless_return.rs:163:13 | -LL | let _ = || { - | _____________________^ -LL | | return; - | |__________________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -400,12 +390,10 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:227:33 + --> tests/ui/needless_return.rs:228:5 | -LL | async fn async_test_void_fun() { - | _________________________________^ -LL | | return; - | |__________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -415,12 +403,10 @@ LL + async fn async_test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:233:11 + --> tests/ui/needless_return.rs:234:9 | -LL | if b { - | ___________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -430,12 +416,10 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:236:13 + --> tests/ui/needless_return.rs:237:9 | -LL | } else { - | _____________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -593,12 +577,10 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:364:20 + --> tests/ui/needless_return.rs:365:9 | -LL | let _ = 42; - | ____________________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -608,10 +590,10 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:371:20 + --> tests/ui/needless_return.rs:371:21 | LL | let _ = 42; return; - | ^^^^^^^ + | ^^^^^^ | help: remove `return` | diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index 703c2a3d98479..4ab5bc9acdeeb 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -221,3 +221,56 @@ fn main() { Cout << 142; -Cout; } + +fn issue14592() { + struct MyStruct { + _inner: MyInner, + } + struct MyInner {} + + impl Drop for MyInner { + fn drop(&mut self) { + println!("dropping"); + } + } + + let x = MyStruct { _inner: MyInner {} }; + + let closure = || { + // Do not lint: dropping the assignment or assigning to `_` would + // change the output. + let _x = x; + }; + + println!("1"); + closure(); + println!("2"); + + struct Innocuous { + a: i32, + } + + // Do not lint: one of the fields has a side effect. + let x = MyInner {}; + let closure = || { + let _x = Innocuous { + a: { + x; + 10 + }, + }; + }; + + // Do not lint: the base has a side effect. + let x = MyInner {}; + let closure = || { + let _x = Innocuous { + ..Innocuous { + a: { + x; + 10 + }, + } + }; + }; +} diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 55e287b91596d..ff81c64260274 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -7,85 +7,107 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] +#![allow(invalid_reference_casting)] +#![allow(suspicious_double_ref_op)] +#![allow(invalid_nan_comparisons)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] #![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(double_negations)] +#![allow(drop_bounds)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] +#![allow(clippy::manual_find_map)] #![allow(unpredictable_function_pointer_comparisons)] +#![allow(useless_ptr_null_checks)] +#![allow(for_loops_over_fallibles)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] +#![allow(array_into_iter)] +#![allow(invalid_atomic_ordering)] +#![allow(invalid_null_arguments)] +#![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] +#![allow(let_underscore_drop)] #![allow(clippy::overly_complex_bool_expr)] +#![allow(unexpected_cfgs)] +#![allow(enum_intrinsics_non_enums)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] +#![allow(non_fmt_panics)] +#![allow(named_arguments_used_positionally)] #![allow(clippy::needless_borrow)] +#![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] +#![allow(dangling_pointers_from_temporaries)] #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] -#![allow(clippy::unwrap_or_default)] -#![allow(clippy::invisible_characters)] -#![allow(invalid_reference_casting)] -#![allow(suspicious_double_ref_op)] -#![allow(invalid_nan_comparisons)] -#![allow(invalid_null_arguments)] -#![allow(double_negations)] -#![allow(drop_bounds)] -#![allow(dropping_copy_types)] -#![allow(dropping_references)] -#![allow(useless_ptr_null_checks)] -#![allow(for_loops_over_fallibles)] -#![allow(forgetting_copy_types)] -#![allow(forgetting_references)] -#![allow(array_into_iter)] -#![allow(invalid_atomic_ordering)] -#![allow(invalid_value)] -#![allow(invalid_from_utf8_unchecked)] -#![allow(let_underscore_drop)] -#![allow(unexpected_cfgs)] -#![allow(enum_intrinsics_non_enums)] -#![allow(non_fmt_panics)] -#![allow(named_arguments_used_positionally)] -#![allow(dangling_pointers_from_temporaries)] +#![allow(unnecessary_transmutes)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![allow(clippy::unwrap_or_default)] #![allow(ambiguous_wide_pointer_comparisons)] -#![allow(clippy::reversed_empty_ranges)] -#![allow(unnecessary_transmutes)] +#![allow(clippy::invisible_characters)] #![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` #![warn(clippy::box_collection)] //~ ERROR: lint `clippy::box_vec` +#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` #![warn(clippy::redundant_static_lifetimes)] //~ ERROR: lint `clippy::const_static_lifetime` #![warn(clippy::cognitive_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` #![warn(clippy::derived_hash_with_manual_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` #![warn(clippy::disallowed_methods)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_types)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` +#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` +#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` #![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` #![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` +#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` #![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result` #![warn(clippy::non_canonical_clone_impl)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` #![warn(clippy::non_canonical_partial_ord_impl)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` #![warn(clippy::arithmetic_side_effects)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` +#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::overly_complex_bool_expr)] //~ ERROR: lint `clippy::logic_bug` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` #![warn(clippy::new_without_default)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::bind_instead_of_map)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::option_expect_used` @@ -93,49 +115,27 @@ #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` #![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` #![warn(clippy::panicking_overflow_checks)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` +#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` #![warn(clippy::needless_borrow)] //~ ERROR: lint `clippy::ref_in_deref` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_add_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::module_name_repetitions)] //~ ERROR: lint `clippy::stutter` +#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` #![warn(clippy::missing_const_for_thread_local)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` #![warn(clippy::recursive_format_impl)] //~ ERROR: lint `clippy::to_string_in_display` -#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` -#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` -#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` -#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` -#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` -#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` -#![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` -#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` -#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` -#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` -#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` -#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` -#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` -#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` -#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` -#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` -#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` -#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` -#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` -#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` -#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` -#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` -#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` #![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` -#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` -#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` -#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` -#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_float_to_int` -#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 31dcd2cea0818..b5d5d07e639a0 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -7,85 +7,107 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] +#![allow(invalid_reference_casting)] +#![allow(suspicious_double_ref_op)] +#![allow(invalid_nan_comparisons)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] #![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(double_negations)] +#![allow(drop_bounds)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] +#![allow(clippy::manual_find_map)] #![allow(unpredictable_function_pointer_comparisons)] +#![allow(useless_ptr_null_checks)] +#![allow(for_loops_over_fallibles)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] +#![allow(array_into_iter)] +#![allow(invalid_atomic_ordering)] +#![allow(invalid_null_arguments)] +#![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] +#![allow(let_underscore_drop)] #![allow(clippy::overly_complex_bool_expr)] +#![allow(unexpected_cfgs)] +#![allow(enum_intrinsics_non_enums)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] +#![allow(non_fmt_panics)] +#![allow(named_arguments_used_positionally)] #![allow(clippy::needless_borrow)] +#![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] +#![allow(dangling_pointers_from_temporaries)] #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] -#![allow(clippy::unwrap_or_default)] -#![allow(clippy::invisible_characters)] -#![allow(invalid_reference_casting)] -#![allow(suspicious_double_ref_op)] -#![allow(invalid_nan_comparisons)] -#![allow(invalid_null_arguments)] -#![allow(double_negations)] -#![allow(drop_bounds)] -#![allow(dropping_copy_types)] -#![allow(dropping_references)] -#![allow(useless_ptr_null_checks)] -#![allow(for_loops_over_fallibles)] -#![allow(forgetting_copy_types)] -#![allow(forgetting_references)] -#![allow(array_into_iter)] -#![allow(invalid_atomic_ordering)] -#![allow(invalid_value)] -#![allow(invalid_from_utf8_unchecked)] -#![allow(let_underscore_drop)] -#![allow(unexpected_cfgs)] -#![allow(enum_intrinsics_non_enums)] -#![allow(non_fmt_panics)] -#![allow(named_arguments_used_positionally)] -#![allow(dangling_pointers_from_temporaries)] +#![allow(unnecessary_transmutes)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![allow(clippy::unwrap_or_default)] #![allow(ambiguous_wide_pointer_comparisons)] -#![allow(clippy::reversed_empty_ranges)] -#![allow(unnecessary_transmutes)] +#![allow(clippy::invisible_characters)] #![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` #![warn(clippy::block_in_if_condition_stmt)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` #![warn(clippy::blocks_in_if_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` #![warn(clippy::box_vec)] //~ ERROR: lint `clippy::box_vec` +#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` #![warn(clippy::const_static_lifetime)] //~ ERROR: lint `clippy::const_static_lifetime` #![warn(clippy::cyclomatic_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` #![warn(clippy::derive_hash_xor_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` #![warn(clippy::disallowed_method)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_type)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` +#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` +#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` #![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` +#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` #![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result` #![warn(clippy::incorrect_clone_impl_on_copy_type)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` #![warn(clippy::integer_arithmetic)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` +#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::logic_bug)] //~ ERROR: lint `clippy::logic_bug` +#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` #![warn(clippy::new_without_default_derive)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::option_and_then_some)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::option_expect_used)] //~ ERROR: lint `clippy::option_expect_used` @@ -93,49 +115,27 @@ #![warn(clippy::option_map_unwrap_or_else)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` #![warn(clippy::option_unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` #![warn(clippy::overflow_check_conditional)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` +#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` #![warn(clippy::ref_in_deref)] //~ ERROR: lint `clippy::ref_in_deref` #![warn(clippy::result_expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::result_map_unwrap_or_else)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::result_unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_push_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::stutter)] //~ ERROR: lint `clippy::stutter` +#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` #![warn(clippy::thread_local_initializer_can_be_made_const)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` #![warn(clippy::to_string_in_display)] //~ ERROR: lint `clippy::to_string_in_display` -#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` -#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` -#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` -#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` -#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` -#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` -#![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` -#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` -#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` -#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` -#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` -#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` -#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` -#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` -#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` -#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` -#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` -#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` -#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` -#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` -#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` -#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` -#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` -#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` -#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` -#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(clippy::transmute_float_to_int)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` #![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` #![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` -#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` -#![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` -#![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` -#![warn(clippy::transmute_float_to_int)] //~ ERROR: lint `clippy::transmute_float_to_int` -#![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index a8d5c96acc3a7..2487dfc8eba44 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -37,407 +37,407 @@ error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` -error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` +error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` --> tests/ui/rename.rs:73:9 | +LL | #![warn(clippy::cast_ref_to_mut)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` + +error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` + --> tests/ui/rename.rs:74:9 + | +LL | #![warn(clippy::clone_double_ref)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` + +error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` + --> tests/ui/rename.rs:75:9 + | +LL | #![warn(clippy::cmp_nan)] + | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` + +error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` + --> tests/ui/rename.rs:76:9 + | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` -error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:78:9 - | -LL | #![warn(clippy::eval_order_dependence)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` - -error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:79:9 - | -LL | #![warn(clippy::find_map)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` - -error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:80:9 - | -LL | #![warn(clippy::filter_map)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` - -error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` +error: lint `clippy::double_neg` has been renamed to `double_negations` --> tests/ui/rename.rs:81:9 | -LL | #![warn(clippy::fn_address_comparisons)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` +LL | #![warn(clippy::double_neg)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` -error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` --> tests/ui/rename.rs:82:9 | -LL | #![warn(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` +LL | #![warn(clippy::drop_bounds)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` +error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` --> tests/ui/rename.rs:83:9 | -LL | #![warn(clippy::if_let_redundant_pattern_matching)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` +LL | #![warn(clippy::drop_copy)] + | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` -error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` +error: lint `clippy::drop_ref` has been renamed to `dropping_references` --> tests/ui/rename.rs:84:9 | -LL | #![warn(clippy::if_let_some_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +LL | #![warn(clippy::drop_ref)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` -error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` +error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` --> tests/ui/rename.rs:85:9 | -LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` +LL | #![warn(clippy::eval_order_dependence)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` -error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` +error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` --> tests/ui/rename.rs:86:9 | -LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` +LL | #![warn(clippy::filter_map)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` -error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` +error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` --> tests/ui/rename.rs:87:9 | -LL | #![warn(clippy::integer_arithmetic)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` +LL | #![warn(clippy::find_map)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` -error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` +error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` --> tests/ui/rename.rs:88:9 | -LL | #![warn(clippy::logic_bug)] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` +LL | #![warn(clippy::fn_address_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` -error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` +error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` --> tests/ui/rename.rs:89:9 | -LL | #![warn(clippy::new_without_default_derive)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` +LL | #![warn(clippy::fn_null_check)] + | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` -error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` +error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` --> tests/ui/rename.rs:90:9 | -LL | #![warn(clippy::option_and_then_some)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` +LL | #![warn(clippy::for_loop_over_option)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` --> tests/ui/rename.rs:91:9 | -LL | #![warn(clippy::option_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::for_loop_over_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` --> tests/ui/rename.rs:92:9 | -LL | #![warn(clippy::option_map_unwrap_or)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` --> tests/ui/rename.rs:93:9 | -LL | #![warn(clippy::option_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::forget_copy)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` -error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::forget_ref` has been renamed to `forgetting_references` --> tests/ui/rename.rs:94:9 | -LL | #![warn(clippy::option_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::forget_ref)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` -error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` --> tests/ui/rename.rs:95:9 | -LL | #![warn(clippy::overflow_check_conditional)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` +LL | #![warn(clippy::identity_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` -error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` +error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` --> tests/ui/rename.rs:96:9 | -LL | #![warn(clippy::ref_in_deref)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` +LL | #![warn(clippy::if_let_redundant_pattern_matching)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` -error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` --> tests/ui/rename.rs:97:9 | -LL | #![warn(clippy::result_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::if_let_some_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` -error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` --> tests/ui/rename.rs:98:9 | -LL | #![warn(clippy::result_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` -error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` --> tests/ui/rename.rs:99:9 | -LL | #![warn(clippy::result_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` -error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` +error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` --> tests/ui/rename.rs:100:9 | -LL | #![warn(clippy::single_char_push_str)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` +LL | #![warn(clippy::integer_arithmetic)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` -error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` --> tests/ui/rename.rs:101:9 | -LL | #![warn(clippy::stutter)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` +LL | #![warn(clippy::into_iter_on_array)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` -error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` --> tests/ui/rename.rs:102:9 | -LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` +LL | #![warn(clippy::invalid_atomic_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` -error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` +error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` --> tests/ui/rename.rs:103:9 | -LL | #![warn(clippy::to_string_in_display)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` +LL | #![warn(clippy::invalid_null_ptr_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` -error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` --> tests/ui/rename.rs:104:9 | -LL | #![warn(clippy::unwrap_or_else_default)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` +LL | #![warn(clippy::invalid_ref)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` -error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` +error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` --> tests/ui/rename.rs:105:9 | -LL | #![warn(clippy::zero_width_space)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` +LL | #![warn(clippy::invalid_utf8_in_unchecked)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` -error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` +error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` --> tests/ui/rename.rs:106:9 | -LL | #![warn(clippy::cast_ref_to_mut)] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` +LL | #![warn(clippy::let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` -error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` +error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` --> tests/ui/rename.rs:107:9 | -LL | #![warn(clippy::clone_double_ref)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` +LL | #![warn(clippy::logic_bug)] + | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` -error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` +error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` --> tests/ui/rename.rs:108:9 | -LL | #![warn(clippy::cmp_nan)] - | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` +LL | #![warn(clippy::maybe_misused_cfg)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` -error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` --> tests/ui/rename.rs:109:9 | -LL | #![warn(clippy::invalid_null_ptr_usage)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` +LL | #![warn(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` -error: lint `clippy::double_neg` has been renamed to `double_negations` +error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` --> tests/ui/rename.rs:110:9 | -LL | #![warn(clippy::double_neg)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` +LL | #![warn(clippy::mismatched_target_os)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` -error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` --> tests/ui/rename.rs:111:9 | -LL | #![warn(clippy::drop_bounds)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` +LL | #![warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` -error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` +error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` --> tests/ui/rename.rs:112:9 | -LL | #![warn(clippy::drop_copy)] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` +LL | #![warn(clippy::option_and_then_some)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` -error: lint `clippy::drop_ref` has been renamed to `dropping_references` +error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` --> tests/ui/rename.rs:113:9 | -LL | #![warn(clippy::drop_ref)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +LL | #![warn(clippy::option_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` --> tests/ui/rename.rs:114:9 | -LL | #![warn(clippy::fn_null_check)] - | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` +LL | #![warn(clippy::option_map_unwrap_or)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> tests/ui/rename.rs:115:9 | -LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` +LL | #![warn(clippy::option_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` --> tests/ui/rename.rs:116:9 | -LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` +LL | #![warn(clippy::option_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` --> tests/ui/rename.rs:117:9 | -LL | #![warn(clippy::for_loops_over_fallibles)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` +LL | #![warn(clippy::overflow_check_conditional)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` -error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` +error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` --> tests/ui/rename.rs:118:9 | -LL | #![warn(clippy::forget_copy)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` +LL | #![warn(clippy::panic_params)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` -error: lint `clippy::forget_ref` has been renamed to `forgetting_references` +error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` --> tests/ui/rename.rs:119:9 | -LL | #![warn(clippy::forget_ref)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` +LL | #![warn(clippy::positional_named_format_parameters)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` -error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` +error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` --> tests/ui/rename.rs:120:9 | -LL | #![warn(clippy::into_iter_on_array)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` +LL | #![warn(clippy::ref_in_deref)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` -error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` --> tests/ui/rename.rs:121:9 | -LL | #![warn(clippy::invalid_atomic_ordering)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` +LL | #![warn(clippy::result_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::invalid_ref` has been renamed to `invalid_value` +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> tests/ui/rename.rs:122:9 | -LL | #![warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` +LL | #![warn(clippy::result_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` --> tests/ui/rename.rs:123:9 | -LL | #![warn(clippy::invalid_utf8_in_unchecked)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` +LL | #![warn(clippy::result_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` +error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` --> tests/ui/rename.rs:124:9 | -LL | #![warn(clippy::let_underscore_drop)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` +LL | #![warn(clippy::reverse_range_loop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` -error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` --> tests/ui/rename.rs:125:9 | -LL | #![warn(clippy::maybe_misused_cfg)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` +LL | #![warn(clippy::single_char_push_str)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` -error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` --> tests/ui/rename.rs:126:9 | -LL | #![warn(clippy::mem_discriminant_non_enum)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` +LL | #![warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` -error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` --> tests/ui/rename.rs:127:9 | -LL | #![warn(clippy::mismatched_target_os)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` +LL | #![warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` -error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` +error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` --> tests/ui/rename.rs:128:9 | -LL | #![warn(clippy::panic_params)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` +LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` -error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` +error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` --> tests/ui/rename.rs:129:9 | -LL | #![warn(clippy::positional_named_format_parameters)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` +LL | #![warn(clippy::to_string_in_display)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` -error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` +error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` --> tests/ui/rename.rs:130:9 | -LL | #![warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` +LL | #![warn(clippy::transmute_float_to_int)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` -error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` +error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` --> tests/ui/rename.rs:131:9 | -LL | #![warn(clippy::undropped_manually_drops)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` +LL | #![warn(clippy::transmute_int_to_char)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` -error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` +error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` --> tests/ui/rename.rs:132:9 | -LL | #![warn(clippy::unknown_clippy_lints)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` +LL | #![warn(clippy::transmute_int_to_float)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` -error: lint `clippy::unused_label` has been renamed to `unused_labels` +error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` --> tests/ui/rename.rs:133:9 | -LL | #![warn(clippy::unused_label)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` +LL | #![warn(clippy::transmute_num_to_bytes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` -error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` +error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` --> tests/ui/rename.rs:134:9 | -LL | #![warn(clippy::vtable_address_comparisons)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` +LL | #![warn(clippy::undropped_manually_drops)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` -error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` +error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` --> tests/ui/rename.rs:135:9 | -LL | #![warn(clippy::reverse_range_loop)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` +LL | #![warn(clippy::unknown_clippy_lints)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` -error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` +error: lint `clippy::unused_label` has been renamed to `unused_labels` --> tests/ui/rename.rs:136:9 | -LL | #![warn(clippy::transmute_int_to_float)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` +LL | #![warn(clippy::unused_label)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` +error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` --> tests/ui/rename.rs:137:9 | -LL | #![warn(clippy::transmute_int_to_char)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` +LL | #![warn(clippy::unwrap_or_else_default)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` -error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` +error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` --> tests/ui/rename.rs:138:9 | -LL | #![warn(clippy::transmute_float_to_int)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` +LL | #![warn(clippy::vtable_address_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` --> tests/ui/rename.rs:139:9 | -LL | #![warn(clippy::transmute_num_to_bytes)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` +LL | #![warn(clippy::zero_width_space)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: aborting due to 73 previous errors diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs index a98b73c9e1c82..3f205b322ab08 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs @@ -31,34 +31,34 @@ fn ifs_same_cond_fn() { let obj = Struct; if function() { - } else if function() { //~^ same_functions_in_if_condition + } else if function() { } if fn_arg(a) { - } else if fn_arg(a) { //~^ same_functions_in_if_condition + } else if fn_arg(a) { } if obj.method() { - } else if obj.method() { //~^ same_functions_in_if_condition + } else if obj.method() { } if obj.method_arg(a) { - } else if obj.method_arg(a) { //~^ same_functions_in_if_condition + } else if obj.method_arg(a) { } let mut v = vec![1]; if v.pop().is_none() { - } else if v.pop().is_none() { //~^ same_functions_in_if_condition + } else if v.pop().is_none() { } if v.len() == 42 { - } else if v.len() == 42 { //~^ same_functions_in_if_condition + } else if v.len() == 42 { } if v.len() == 1 { diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr index 35dcbadce5953..59f4511757d61 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr @@ -1,79 +1,62 @@ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:34:15 - | -LL | } else if function() { - | ^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:33:8 | LL | if function() { | ^^^^^^^^^^ +LL | +LL | } else if function() { + | ^^^^^^^^^^ + | note: the lint level is defined here --> tests/ui/same_functions_in_if_condition.rs:2:9 | LL | #![deny(clippy::same_functions_in_if_condition)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:39:15 - | -LL | } else if fn_arg(a) { - | ^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:38:8 | LL | if fn_arg(a) { | ^^^^^^^^^ +LL | +LL | } else if fn_arg(a) { + | ^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:44:15 - | -LL | } else if obj.method() { - | ^^^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:43:8 | LL | if obj.method() { | ^^^^^^^^^^^^ +LL | +LL | } else if obj.method() { + | ^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:49:15 - | -LL | } else if obj.method_arg(a) { - | ^^^^^^^^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:48:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ - -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:55:15 - | -LL | } else if v.pop().is_none() { +LL | +LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ - | -note: same as this + +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:54:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ +LL | +LL | } else if v.pop().is_none() { + | ^^^^^^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:60:15 - | -LL | } else if v.len() == 42 { - | ^^^^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:59:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ +LL | +LL | } else if v.len() == 42 { + | ^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.rs b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs index c6c0cb347dc68..25884450b0842 100644 --- a/src/tools/clippy/tests/ui/single_range_in_vec_init.rs +++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs //@no-rustfix: overlapping suggestions -#![allow(clippy::no_effect, clippy::useless_vec, unused)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)] #![warn(clippy::single_range_in_vec_init)] #![feature(generic_arg_infer)] diff --git a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.fixed b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.fixed similarity index 100% rename from src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.fixed rename to src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.fixed diff --git a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.rs b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs similarity index 100% rename from src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.rs rename to src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs diff --git a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.stderr b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr similarity index 80% rename from src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.stderr rename to src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr index e334ca5241eba..4998b9bd2cc40 100644 --- a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.stderr +++ b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr @@ -1,5 +1,5 @@ error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)` - --> tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.rs:6:19 + --> tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs:6:19 | LL | fn aa(a: Aa::new(); + s.remove(&"hello".to_owned()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 7954a4ad4ce77..f2dbd1db3c9f6 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -675,3 +675,9 @@ mod issue_14242 { rc_slice_provider().to_vec().into_iter() } } + +fn issue14833() { + use std::collections::HashSet; + let mut s = HashSet::<&String>::new(); + s.remove(&"hello".to_owned()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr index ba562f5a50bec..13d71271e211c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr @@ -1,13 +1,8 @@ error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:9:1 | -LL | / fn func1(a: bool, b: bool) -> Option { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn func1(a: bool, b: bool) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_wraps)]` @@ -16,7 +11,7 @@ help: remove `Option` from the return type... LL - fn func1(a: bool, b: bool) -> Option { LL + fn func1(a: bool, b: bool) -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL ~ return 42; LL | } @@ -30,21 +25,15 @@ LL ~ return 1337; error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:24:1 | -LL | / fn func2(a: bool, b: bool) -> Option { -LL | | -LL | | -LL | | if a && b { -... | -LL | | if a { Some(20) } else { Some(30) } -LL | | } - | |_^ +LL | fn func2(a: bool, b: bool) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func2(a: bool, b: bool) -> Option { LL + fn func2(a: bool, b: bool) -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL ~ return 10; LL | } @@ -54,19 +43,15 @@ LL ~ if a { 20 } else { 30 } error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:44:1 | -LL | / fn func5() -> Option { -LL | | -LL | | -LL | | Some(1) -LL | | } - | |_^ +LL | fn func5() -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func5() -> Option { LL + fn func5() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL - Some(1) LL + 1 @@ -75,19 +60,15 @@ LL + 1 error: this function's return value is unnecessarily wrapped by `Result` --> tests/ui/unnecessary_wraps.rs:56:1 | -LL | / fn func7() -> Result { -LL | | -LL | | -LL | | Ok(1) -LL | | } - | |_^ +LL | fn func7() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Result` from the return type... | LL - fn func7() -> Result { LL + fn func7() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Ok()` from returning expressions | LL - Ok(1) LL + 1 @@ -96,19 +77,15 @@ LL + 1 error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:86:5 | -LL | / fn func12() -> Option { -LL | | -LL | | -LL | | Some(1) -LL | | } - | |_____^ +LL | fn func12() -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func12() -> Option { LL + fn func12() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL - Some(1) LL + 1 @@ -117,13 +94,8 @@ LL + 1 error: this function's return value is unnecessary --> tests/ui/unnecessary_wraps.rs:115:1 | -LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the return type... | @@ -144,13 +116,8 @@ LL ~ return ; error: this function's return value is unnecessary --> tests/ui/unnecessary_wraps.rs:130:1 | -LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the return type... | diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed index 8c1f948fb5806..3c3ea5a736d43 100644 --- a/src/tools/clippy/tests/ui/useless_asref.fixed +++ b/src/tools/clippy/tests/ui/useless_asref.fixed @@ -248,6 +248,16 @@ impl Issue12357 { } } +fn issue_14828() { + pub trait T { + fn as_ref(&self) {} + } + + impl T for () {} + + ().as_ref(); +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_asref.rs b/src/tools/clippy/tests/ui/useless_asref.rs index d9db2d4f5596a..c173dd677152d 100644 --- a/src/tools/clippy/tests/ui/useless_asref.rs +++ b/src/tools/clippy/tests/ui/useless_asref.rs @@ -248,6 +248,16 @@ impl Issue12357 { } } +fn issue_14828() { + pub trait T { + fn as_ref(&self) {} + } + + impl T for () {} + + ().as_ref(); +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_concat.fixed b/src/tools/clippy/tests/ui/useless_concat.fixed new file mode 100644 index 0000000000000..360b6f6ce82ee --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_concat.fixed @@ -0,0 +1,41 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::useless_concat)] +#![allow(clippy::print_literal)] + +extern crate proc_macros; +use proc_macros::{external, with_span}; + +macro_rules! my_concat { + ($fmt:literal $(, $e:expr)*) => { + println!(concat!("ERROR: ", $fmt), $($e,)*); + } +} + +fn main() { + let x = ""; //~ useless_concat + let x = "c"; //~ useless_concat + let x = "\""; //~ useless_concat + let x = "true"; //~ useless_concat + let x = "1"; //~ useless_concat + let x = "1.0000"; //~ useless_concat + let x = "1"; //~ useless_concat + let x = "1"; //~ useless_concat + let x = "1.0000"; //~ useless_concat + let x = "1.0000"; //~ useless_concat + let x = "a😀\n"; //~ useless_concat + let x = "a"; //~ useless_concat + let x = "1"; //~ useless_concat + println!("b: {}", "a"); //~ useless_concat + // Should not lint. + let x = concat!("a", "b"); + let local_i32 = 1; + my_concat!("{}", local_i32); + let x = concat!(file!(), "#L", line!()); + + external! { concat!(); } + with_span! { + span + concat!(); + } +} diff --git a/src/tools/clippy/tests/ui/useless_concat.rs b/src/tools/clippy/tests/ui/useless_concat.rs new file mode 100644 index 0000000000000..338d20a48ae96 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_concat.rs @@ -0,0 +1,41 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::useless_concat)] +#![allow(clippy::print_literal)] + +extern crate proc_macros; +use proc_macros::{external, with_span}; + +macro_rules! my_concat { + ($fmt:literal $(, $e:expr)*) => { + println!(concat!("ERROR: ", $fmt), $($e,)*); + } +} + +fn main() { + let x = concat!(); //~ useless_concat + let x = concat!('c'); //~ useless_concat + let x = concat!('"'); //~ useless_concat + let x = concat!(true); //~ useless_concat + let x = concat!(1f32); //~ useless_concat + let x = concat!(1.0000f32); //~ useless_concat + let x = concat!(1_f32); //~ useless_concat + let x = concat!(1_); //~ useless_concat + let x = concat!(1.0000_f32); //~ useless_concat + let x = concat!(1.0000_); //~ useless_concat + let x = concat!("a\u{1f600}\n"); //~ useless_concat + let x = concat!(r##"a"##); //~ useless_concat + let x = concat!(1); //~ useless_concat + println!("b: {}", concat!("a")); //~ useless_concat + // Should not lint. + let x = concat!("a", "b"); + let local_i32 = 1; + my_concat!("{}", local_i32); + let x = concat!(file!(), "#L", line!()); + + external! { concat!(); } + with_span! { + span + concat!(); + } +} diff --git a/src/tools/clippy/tests/ui/useless_concat.stderr b/src/tools/clippy/tests/ui/useless_concat.stderr new file mode 100644 index 0000000000000..43d6d9ff5799d --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_concat.stderr @@ -0,0 +1,89 @@ +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:16:13 + | +LL | let x = concat!(); + | ^^^^^^^^^ help: replace with: `""` + | + = note: `-D clippy::useless-concat` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_concat)]` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:17:13 + | +LL | let x = concat!('c'); + | ^^^^^^^^^^^^ help: replace with: `"c"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:18:13 + | +LL | let x = concat!('"'); + | ^^^^^^^^^^^^ help: replace with: `"\""` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:19:13 + | +LL | let x = concat!(true); + | ^^^^^^^^^^^^^ help: replace with: `"true"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:20:13 + | +LL | let x = concat!(1f32); + | ^^^^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:21:13 + | +LL | let x = concat!(1.0000f32); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `"1.0000"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:22:13 + | +LL | let x = concat!(1_f32); + | ^^^^^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:23:13 + | +LL | let x = concat!(1_); + | ^^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:24:13 + | +LL | let x = concat!(1.0000_f32); + | ^^^^^^^^^^^^^^^^^^^ help: replace with: `"1.0000"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:25:13 + | +LL | let x = concat!(1.0000_); + | ^^^^^^^^^^^^^^^^ help: replace with: `"1.0000"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:26:13 + | +LL | let x = concat!("a\u{1f600}\n"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `"a😀\n"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:27:13 + | +LL | let x = concat!(r##"a"##); + | ^^^^^^^^^^^^^^^^^ help: replace with: `"a"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:28:13 + | +LL | let x = concat!(1); + | ^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:29:23 + | +LL | println!("b: {}", concat!("a")); + | ^^^^^^^^^^^^ help: replace with: `"a"` + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index 489caacf21235..ad30c94f34781 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -427,3 +427,18 @@ mod issue11819 { } } } + +fn issue14739() { + use std::ops::Range; + + const R: Range = 2..7; + + R.into_iter().all(|_x| true); // no lint + + R.into_iter().any(|_x| true); // no lint + + R.for_each(|_x| {}); + //~^ useless_conversion + let _ = R.map(|_x| 0); + //~^ useless_conversion +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 4f3a3b00ea206..505afb340009a 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -427,3 +427,18 @@ mod issue11819 { } } } + +fn issue14739() { + use std::ops::Range; + + const R: Range = 2..7; + + R.into_iter().all(|_x| true); // no lint + + R.into_iter().any(|_x| true); // no lint + + R.into_iter().for_each(|_x| {}); + //~^ useless_conversion + let _ = R.into_iter().map(|_x| 0); + //~^ useless_conversion +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 3cde2a786e466..3bfaf1411c2c6 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -377,5 +377,17 @@ LL - takes_into_iter(self.my_field.into_iter()); LL + takes_into_iter(&mut *self.my_field); | -error: aborting due to 41 previous errors +error: useless conversion to the same type: `std::ops::Range` + --> tests/ui/useless_conversion.rs:440:5 + | +LL | R.into_iter().for_each(|_x| {}); + | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` + +error: useless conversion to the same type: `std::ops::Range` + --> tests/ui/useless_conversion.rs:442:13 + | +LL | let _ = R.into_iter().map(|_x| 0); + | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` + +error: aborting due to 43 previous errors diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs index 4beeef421f30e..dcbfd16843de4 100644 --- a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs +++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs @@ -71,6 +71,27 @@ fn test2(map: HashMap, key: &str) -> HashMap { todo!(); } +fn issue14822() { + trait Trait { + type T; + } + struct S(T::T); + + // The `delay_bug` happens when evaluating the pointer metadata of `S` which depends on + // whether `T::T` is `Sized`. Since the type alias doesn't have a trait bound of `T: Trait` + // evaluating `T::T: Sized` ultimately fails with `NoSolution`. + type A = HashMap>; + type B = HashMap>; + + enum E {} + impl Trait for E { + type T = (); + } + type C = HashMap>; + type D = HashMap>; + //~^ zero_sized_map_values +} + fn main() { let _: HashMap = HashMap::new(); //~^ zero_sized_map_values diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr index ed8536acfe8fa..d29491fa05c76 100644 --- a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr +++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr @@ -81,7 +81,15 @@ LL | fn test(map: HashMap, key: &str) -> HashMap { = help: consider using a set instead error: map with zero-sized value type - --> tests/ui/zero_sized_hashmap_values.rs:75:34 + --> tests/ui/zero_sized_hashmap_values.rs:91:14 + | +LL | type D = HashMap>; + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> tests/ui/zero_sized_hashmap_values.rs:96:34 | LL | let _: HashMap = HashMap::new(); | ^^^^^^^ @@ -89,7 +97,7 @@ LL | let _: HashMap = HashMap::new(); = help: consider using a set instead error: map with zero-sized value type - --> tests/ui/zero_sized_hashmap_values.rs:75:12 + --> tests/ui/zero_sized_hashmap_values.rs:96:12 | LL | let _: HashMap = HashMap::new(); | ^^^^^^^^^^^^^^^^^^^ @@ -97,12 +105,12 @@ LL | let _: HashMap = HashMap::new(); = help: consider using a set instead error: map with zero-sized value type - --> tests/ui/zero_sized_hashmap_values.rs:81:12 + --> tests/ui/zero_sized_hashmap_values.rs:102:12 | LL | let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); | ^^^^^^^^^^^^^ | = help: consider using a set instead -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index eb2f9f9dd61f5..389f22c6a2ca3 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -1,7 +1,7 @@ [relabel] allow-unauthenticated = [ "A-*", "C-*", "E-*", "I-*", "L-*", "P-*", "S-*", "T-*", - "good-first-issue", "beta-nominated" + "good first issue", "beta-nominated" ] # Allows shortcuts like `@rustbot ready`