Auto merge of #3681 - rust-lang:rustup-2024-06-17, r=RalfJung

Automatic Rustup
This commit is contained in:
bors 2024-06-17 10:22:19 +00:00
commit c84fa48eb1
860 changed files with 11722 additions and 6202 deletions

View file

@ -17,12 +17,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -181,34 +175,28 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.4"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.17"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.18"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
dependencies = [
"cfg-if",
]
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crypto-common"
@ -331,12 +319,12 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "junction"
version = "1.0.0"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca39ef0d69b18e6a2fd14c2f0a1d593200f4a4ed949b240b5917ab51fac754cb"
checksum = "1c9c415a9b7b1e86cd5738f39d34c9e78c765da7fb1756dbd7d31b3b0d2e7afa"
dependencies = [
"scopeguard",
"winapi",
"windows-sys",
]
[[package]]

View file

@ -19,8 +19,6 @@ lto = "off"
# Forces frame pointers to be used with `-Cforce-frame-pointers`.
# This can be helpful for profiling at a small performance cost.
frame-pointers = true
# Build the llvm-bitcode-linker as it is required for running nvptx tests
llvm-bitcode-linker = true
[llvm]
# Having this set to true disrupts compiler development workflows for people who use `llvm.download-ci-llvm = true`

View file

@ -16,7 +16,7 @@ download-ci-llvm = false
# Make sure they don't get set when installing from source.
channel = "nightly"
download-rustc = false
# Build the llvm-bitcode-linker as it is required for running nvptx tests
# Build the llvm-bitcode-linker
llvm-bitcode-linker = true
[dist]

View file

@ -10,8 +10,6 @@ bench-stage = 0
incremental = true
# Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown.
lto = "off"
# Build the llvm-bitcode-linker as it is required for running nvptx tests
llvm-bitcode-linker = true
[llvm]
# Will download LLVM from CI if available on your platform.

View file

@ -12,8 +12,6 @@ incremental = true
# Using these defaults will download the stage2 compiler (see `download-rustc`
# setting) and the stage2 toolchain should therefore be used for these defaults.
download-rustc = "if-unchanged"
# Build the llvm-bitcode-linker as it is required for running nvptx tests
llvm-bitcode-linker = true
[build]
# Document with the in-tree rustdoc by default, since `download-rustc` makes it quick to compile.

View file

@ -1,7 +1,7 @@
//! Runs rustfmt on the repository.
use crate::core::builder::Builder;
use crate::utils::helpers::{output, program_out_of_date, t};
use crate::utils::helpers::{self, output, program_out_of_date, t};
use build_helper::ci::CiEnv;
use build_helper::git::get_git_modified_files;
use ignore::WalkBuilder;
@ -93,7 +93,7 @@ fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, Str
return Ok(None);
}
get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &vec!["rs"])
get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &["rs"])
}
#[derive(serde_derive::Deserialize)]
@ -160,7 +160,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) {
override_builder.add(&format!("!{ignore}")).expect(&ignore);
}
}
let git_available = match Command::new("git")
let git_available = match helpers::git(None)
.arg("--version")
.stdout(Stdio::null())
.stderr(Stdio::null())
@ -172,9 +172,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) {
let mut adjective = None;
if git_available {
let in_working_tree = match build
.config
.git()
let in_working_tree = match helpers::git(Some(&build.src))
.arg("rev-parse")
.arg("--is-inside-work-tree")
.stdout(Stdio::null())
@ -186,9 +184,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) {
};
if in_working_tree {
let untracked_paths_output = output(
build
.config
.git()
helpers::git(Some(&build.src))
.arg("status")
.arg("--porcelain")
.arg("-z")

View file

@ -159,7 +159,7 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String {
// in that case.
let closest_upstream = get_git_merge_base(&config.git_config(), Some(&config.src))
.unwrap_or_else(|_| "HEAD".into());
let mut rev_list = config.git();
let mut rev_list = helpers::git(Some(&config.src));
rev_list.args(&[
PathBuf::from("rev-list"),
format!("--author={}", config.stage0_metadata.config.git_merge_commit_email).into(),
@ -252,7 +252,7 @@ pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool {
// We assume we have access to git, so it's okay to unconditionally pass
// `true` here.
let llvm_sha = detect_llvm_sha(config, true);
let head_sha = output(config.git().arg("rev-parse").arg("HEAD"));
let head_sha = output(helpers::git(Some(&config.src)).arg("rev-parse").arg("HEAD"));
let head_sha = head_sha.trim();
llvm_sha == head_sha
}

View file

@ -149,12 +149,14 @@ impl Step for Miri {
&[],
);
miri.add_rustc_lib_path(builder);
// Forward arguments.
miri.arg("--").arg("--target").arg(target.rustc_target_arg());
miri.args(builder.config.args());
// miri tests need to know about the stage sysroot
miri.env("MIRI_SYSROOT", &miri_sysroot);
miri.arg("--sysroot").arg(miri_sysroot);
// Forward arguments. This may contain further arguments to the program
// after another --, so this must be at the end.
miri.args(builder.config.args());
let mut miri = Command::from(miri);
builder.run(&mut miri);

View file

@ -8,7 +8,7 @@
use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::t;
use crate::utils::change_tracker::CONFIG_CHANGE_HISTORY;
use crate::utils::helpers::hex_encode;
use crate::utils::helpers::{self, hex_encode};
use crate::Config;
use sha2::Digest;
use std::env::consts::EXE_SUFFIX;
@ -482,10 +482,13 @@ impl Step for Hook {
// install a git hook to automatically run tidy, if they want
fn install_git_hook_maybe(config: &Config) -> io::Result<()> {
let git = config.git().args(["rev-parse", "--git-common-dir"]).output().map(|output| {
assert!(output.status.success(), "failed to run `git`");
PathBuf::from(t!(String::from_utf8(output.stdout)).trim())
})?;
let git = helpers::git(Some(&config.src))
.args(["rev-parse", "--git-common-dir"])
.output()
.map(|output| {
assert!(output.status.success(), "failed to run `git`");
PathBuf::from(t!(String::from_utf8(output.stdout)).trim())
})?;
let hooks_dir = git.join("hooks");
let dst = hooks_dir.join("pre-push");
if dst.exists() {

View file

@ -823,19 +823,29 @@ impl Step for LibcxxVersionTool {
fn run(self, builder: &Builder<'_>) -> LibcxxVersion {
let out_dir = builder.out.join(self.target.to_string()).join("libcxx-version");
let _ = fs::remove_dir_all(&out_dir);
t!(fs::create_dir_all(&out_dir));
let compiler = builder.cxx(self.target).unwrap();
let mut cmd = Command::new(compiler);
let executable = out_dir.join(exe("libcxx-version", self.target));
cmd.arg("-o").arg(&executable).arg(builder.src.join("src/tools/libcxx-version/main.cpp"));
builder.run_cmd(&mut cmd);
// This is a sanity-check specific step, which means it is frequently called (when using
// CI LLVM), and compiling `src/tools/libcxx-version/main.cpp` at the beginning of the bootstrap
// invocation adds a fair amount of overhead to the process (see https://github.com/rust-lang/rust/issues/126423).
// Therefore, we want to avoid recompiling this file unnecessarily.
if !executable.exists() {
panic!("Something went wrong. {} is not present", executable.display());
if !out_dir.exists() {
t!(fs::create_dir_all(&out_dir));
}
let compiler = builder.cxx(self.target).unwrap();
let mut cmd = Command::new(compiler);
cmd.arg("-o")
.arg(&executable)
.arg(builder.src.join("src/tools/libcxx-version/main.cpp"));
builder.run_cmd(&mut cmd);
if !executable.exists() {
panic!("Something went wrong. {} is not present", executable.display());
}
}
let version_output = output(&mut Command::new(executable));

View file

@ -5,7 +5,7 @@
//! [Toolstate]: https://forge.rust-lang.org/infra/toolstate.html
use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::utils::helpers::t;
use crate::utils::helpers::{self, t};
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
use std::env;
@ -13,7 +13,6 @@ use std::fmt;
use std::fs;
use std::io::{Seek, SeekFrom};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time;
// Each cycle is 42 days long (6 weeks); the last week is 35..=42 then.
@ -102,12 +101,8 @@ fn print_error(tool: &str, submodule: &str) {
fn check_changed_files(toolstates: &HashMap<Box<str>, ToolState>) {
// Changed files
let output = std::process::Command::new("git")
.arg("diff")
.arg("--name-status")
.arg("HEAD")
.arg("HEAD^")
.output();
let output =
helpers::git(None).arg("diff").arg("--name-status").arg("HEAD").arg("HEAD^").output();
let output = match output {
Ok(o) => o,
Err(e) => {
@ -324,7 +319,7 @@ fn checkout_toolstate_repo() {
t!(fs::remove_dir_all(TOOLSTATE_DIR));
}
let status = Command::new("git")
let status = helpers::git(None)
.arg("clone")
.arg("--depth=1")
.arg(toolstate_repo())
@ -342,7 +337,7 @@ fn checkout_toolstate_repo() {
/// Sets up config and authentication for modifying the toolstate repo.
fn prepare_toolstate_config(token: &str) {
fn git_config(key: &str, value: &str) {
let status = Command::new("git").arg("config").arg("--global").arg(key).arg(value).status();
let status = helpers::git(None).arg("config").arg("--global").arg(key).arg(value).status();
let success = match status {
Ok(s) => s.success(),
Err(_) => false,
@ -406,8 +401,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) {
publish_test_results(current_toolstate);
// `git commit` failing means nothing to commit.
let status = t!(Command::new("git")
.current_dir(TOOLSTATE_DIR)
let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR)))
.arg("commit")
.arg("-a")
.arg("-m")
@ -418,8 +412,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) {
break;
}
let status = t!(Command::new("git")
.current_dir(TOOLSTATE_DIR)
let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR)))
.arg("push")
.arg("origin")
.arg("master")
@ -431,15 +424,13 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) {
}
eprintln!("Sleeping for 3 seconds before retrying push");
std::thread::sleep(std::time::Duration::from_secs(3));
let status = t!(Command::new("git")
.current_dir(TOOLSTATE_DIR)
let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR)))
.arg("fetch")
.arg("origin")
.arg("master")
.status());
assert!(status.success());
let status = t!(Command::new("git")
.current_dir(TOOLSTATE_DIR)
let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR)))
.arg("reset")
.arg("--hard")
.arg("origin/master")
@ -458,7 +449,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) {
/// `publish_toolstate.py` script if the PR passes all tests and is merged to
/// master.
fn publish_test_results(current_toolstate: &ToolstateData) {
let commit = t!(std::process::Command::new("git").arg("rev-parse").arg("HEAD").output());
let commit = t!(helpers::git(None).arg("rev-parse").arg("HEAD").output());
let commit = t!(String::from_utf8(commit.stdout));
let toolstate_serialized = t!(serde_json::to_string(&current_toolstate));

View file

@ -10,7 +10,7 @@ use std::env;
use std::fmt::{self, Display};
use std::fs;
use std::io::IsTerminal;
use std::path::{Path, PathBuf};
use std::path::{absolute, Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
use std::sync::OnceLock;
@ -20,7 +20,7 @@ use crate::core::build_steps::llvm;
use crate::core::config::flags::{Color, Flags, Warnings};
use crate::utils::cache::{Interned, INTERNER};
use crate::utils::channel::{self, GitInfo};
use crate::utils::helpers::{exe, output, t};
use crate::utils::helpers::{self, exe, output, t};
use build_helper::exit;
use serde::{Deserialize, Deserializer};
use serde_derive::Deserialize;
@ -1248,7 +1248,7 @@ impl Config {
// Infer the source directory. This is non-trivial because we want to support a downloaded bootstrap binary,
// running on a completely different machine from where it was compiled.
let mut cmd = Command::new("git");
let mut cmd = helpers::git(None);
// NOTE: we cannot support running from outside the repository because the only other path we have available
// is set at compile time, which can be wrong if bootstrap was downloaded rather than compiled locally.
// We still support running outside the repository if we find we aren't in a git directory.
@ -1437,7 +1437,7 @@ impl Config {
// To avoid writing to random places on the file system, `config.out` needs to be an absolute path.
if !config.out.is_absolute() {
// `canonicalize` requires the path to already exist. Use our vendored copy of `absolute` instead.
config.out = crate::utils::helpers::absolute(&config.out);
config.out = absolute(&config.out).expect("can't make empty path absolute");
}
config.initial_rustc = if let Some(rustc) = rustc {
@ -2090,15 +2090,6 @@ impl Config {
build_helper::util::try_run(cmd, self.is_verbose())
}
/// A git invocation which runs inside the source directory.
///
/// Use this rather than `Command::new("git")` in order to support out-of-tree builds.
pub(crate) fn git(&self) -> Command {
let mut git = Command::new("git");
git.current_dir(&self.src);
git
}
pub(crate) fn test_args(&self) -> Vec<&str> {
let mut test_args = match self.cmd {
Subcommand::Test { ref test_args, .. }
@ -2130,7 +2121,7 @@ impl Config {
"`Config::read_file_by_commit` is not supported in non-git sources."
);
let mut git = self.git();
let mut git = helpers::git(Some(&self.src));
git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap()));
output(&mut git)
}
@ -2436,7 +2427,8 @@ impl Config {
};
// Handle running from a directory other than the top level
let top_level = output(self.git().args(["rev-parse", "--show-toplevel"]));
let top_level =
output(helpers::git(Some(&self.src)).args(["rev-parse", "--show-toplevel"]));
let top_level = top_level.trim_end();
let compiler = format!("{top_level}/compiler/");
let library = format!("{top_level}/library/");
@ -2444,7 +2436,7 @@ impl Config {
// Look for a version to compare to based on the current commit.
// Only commits merged by bors will have CI artifacts.
let merge_base = output(
self.git()
helpers::git(Some(&self.src))
.arg("rev-list")
.arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email))
.args(["-n1", "--first-parent", "HEAD"]),
@ -2459,8 +2451,7 @@ impl Config {
}
// Warn if there were changes to the compiler or standard library since the ancestor commit.
let has_changes = !t!(self
.git()
let has_changes = !t!(helpers::git(Some(&self.src))
.args(["diff-index", "--quiet", commit, "--", &compiler, &library])
.status())
.success();
@ -2533,13 +2524,14 @@ impl Config {
if_unchanged: bool,
) -> Option<String> {
// Handle running from a directory other than the top level
let top_level = output(self.git().args(["rev-parse", "--show-toplevel"]));
let top_level =
output(helpers::git(Some(&self.src)).args(["rev-parse", "--show-toplevel"]));
let top_level = top_level.trim_end();
// Look for a version to compare to based on the current commit.
// Only commits merged by bors will have CI artifacts.
let merge_base = output(
self.git()
helpers::git(Some(&self.src))
.arg("rev-list")
.arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email))
.args(["-n1", "--first-parent", "HEAD"]),
@ -2554,7 +2546,7 @@ impl Config {
}
// Warn if there were changes to the compiler or standard library since the ancestor commit.
let mut git = self.git();
let mut git = helpers::git(Some(&self.src));
git.args(["diff-index", "--quiet", commit, "--"]);
for path in modified_paths {

View file

@ -128,6 +128,9 @@ pub fn check(build: &mut Build) {
eprintln!(
"Consider upgrading libstdc++ or disabling the `llvm.download-ci-llvm` option."
);
eprintln!(
"If you choose to upgrade libstdc++, run `x clean` or delete `build/host/libcxx-version` manually after the upgrade."
);
}
}
tool::LibcxxVersion::Llvm(_) => {
@ -137,19 +140,20 @@ pub fn check(build: &mut Build) {
}
// We need cmake, but only if we're actually building LLVM or sanitizers.
let building_llvm = build
.hosts
.iter()
.map(|host| {
build.config.llvm_enabled(*host)
&& build
.config
.target_config
.get(host)
.map(|config| config.llvm_config.is_none())
.unwrap_or(true)
})
.any(|build_llvm_ourselves| build_llvm_ourselves);
let building_llvm = !build.config.llvm_from_ci
&& build
.hosts
.iter()
.map(|host| {
build.config.llvm_enabled(*host)
&& build
.config
.target_config
.get(host)
.map(|config| config.llvm_config.is_none())
.unwrap_or(true)
})
.any(|build_llvm_ourselves| build_llvm_ourselves);
let need_cmake = building_llvm || build.config.any_sanitizers_to_build();
if need_cmake && cmd_finder.maybe_have("cmake").is_none() {

View file

@ -522,14 +522,10 @@ impl Build {
// check_submodule
let checked_out_hash =
output(Command::new("git").args(["rev-parse", "HEAD"]).current_dir(&absolute_path));
output(helpers::git(Some(&absolute_path)).args(["rev-parse", "HEAD"]));
// update_submodules
let recorded = output(
Command::new("git")
.args(["ls-tree", "HEAD"])
.arg(relative_path)
.current_dir(&self.config.src),
);
let recorded =
output(helpers::git(Some(&self.src)).args(["ls-tree", "HEAD"]).arg(relative_path));
let actual_hash = recorded
.split_whitespace()
.nth(2)
@ -543,10 +539,7 @@ impl Build {
println!("Updating submodule {}", relative_path.display());
self.run(
Command::new("git")
.args(["submodule", "-q", "sync"])
.arg(relative_path)
.current_dir(&self.config.src),
helpers::git(Some(&self.src)).args(["submodule", "-q", "sync"]).arg(relative_path),
);
// Try passing `--progress` to start, then run git again without if that fails.
@ -554,9 +547,7 @@ impl Build {
// Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
// even though that has no relation to the upstream for the submodule.
let current_branch = {
let output = self
.config
.git()
let output = helpers::git(Some(&self.src))
.args(["symbolic-ref", "--short", "HEAD"])
.stderr(Stdio::inherit())
.output();
@ -568,7 +559,7 @@ impl Build {
}
};
let mut git = self.config.git();
let mut git = helpers::git(Some(&self.src));
if let Some(branch) = current_branch {
// If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
// This syntax isn't accepted by `branch.{branch}`. Strip it.
@ -590,11 +581,11 @@ impl Build {
// Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
// diff-index reports the modifications through the exit status
let has_local_modifications = !self.run_cmd(
BootstrapCommand::from(
Command::new("git")
.args(["diff-index", "--quiet", "HEAD"])
.current_dir(&absolute_path),
)
BootstrapCommand::from(helpers::git(Some(&absolute_path)).args([
"diff-index",
"--quiet",
"HEAD",
]))
.allow_failure()
.output_mode(match self.is_verbose() {
true => OutputMode::PrintAll,
@ -602,14 +593,14 @@ impl Build {
}),
);
if has_local_modifications {
self.run(Command::new("git").args(["stash", "push"]).current_dir(&absolute_path));
self.run(helpers::git(Some(&absolute_path)).args(["stash", "push"]));
}
self.run(Command::new("git").args(["reset", "-q", "--hard"]).current_dir(&absolute_path));
self.run(Command::new("git").args(["clean", "-qdfx"]).current_dir(&absolute_path));
self.run(helpers::git(Some(&absolute_path)).args(["reset", "-q", "--hard"]));
self.run(helpers::git(Some(&absolute_path)).args(["clean", "-qdfx"]));
if has_local_modifications {
self.run(Command::new("git").args(["stash", "pop"]).current_dir(absolute_path));
self.run(helpers::git(Some(&absolute_path)).args(["stash", "pop"]));
}
}
@ -621,8 +612,7 @@ impl Build {
return;
}
let output = output(
self.config
.git()
helpers::git(Some(&self.src))
.args(["config", "--file"])
.arg(self.config.src.join(".gitmodules"))
.args(["--get-regexp", "path"]),
@ -1563,10 +1553,14 @@ impl Build {
// Figure out how many merge commits happened since we branched off master.
// That's our beta number!
// (Note that we use a `..` range, not the `...` symmetric difference.)
output(self.config.git().arg("rev-list").arg("--count").arg("--merges").arg(format!(
"refs/remotes/origin/{}..HEAD",
self.config.stage0_metadata.config.nightly_branch
)))
output(
helpers::git(Some(&self.src)).arg("rev-list").arg("--count").arg("--merges").arg(
format!(
"refs/remotes/origin/{}..HEAD",
self.config.stage0_metadata.config.nightly_branch
),
),
)
});
let n = count.trim().parse().unwrap();
self.prerelease_version.set(Some(n));
@ -1984,15 +1978,13 @@ fn envify(s: &str) -> String {
/// In case of errors during `git` command execution (e.g., in tarball sources), default values
/// are used to prevent panics.
pub fn generate_smart_stamp_hash(dir: &Path, additional_input: &str) -> String {
let diff = Command::new("git")
.current_dir(dir)
let diff = helpers::git(Some(dir))
.arg("diff")
.output()
.map(|o| String::from_utf8(o.stdout).unwrap_or_default())
.unwrap_or_default();
let status = Command::new("git")
.current_dir(dir)
let status = helpers::git(Some(dir))
.arg("status")
.arg("--porcelain")
.arg("-z")

View file

@ -7,11 +7,12 @@
use std::fs;
use std::path::Path;
use std::process::Command;
use crate::utils::helpers::{output, t};
use crate::Build;
use super::helpers;
#[derive(Clone, Default)]
pub enum GitInfo {
/// This is not a git repository.
@ -44,7 +45,7 @@ impl GitInfo {
}
// Make sure git commands work
match Command::new("git").arg("rev-parse").current_dir(dir).output() {
match helpers::git(Some(dir)).arg("rev-parse").output() {
Ok(ref out) if out.status.success() => {}
_ => return GitInfo::Absent,
}
@ -57,17 +58,15 @@ impl GitInfo {
// Ok, let's scrape some info
let ver_date = output(
Command::new("git")
.current_dir(dir)
helpers::git(Some(dir))
.arg("log")
.arg("-1")
.arg("--date=short")
.arg("--pretty=format:%cd"),
);
let ver_hash = output(Command::new("git").current_dir(dir).arg("rev-parse").arg("HEAD"));
let short_ver_hash = output(
Command::new("git").current_dir(dir).arg("rev-parse").arg("--short=9").arg("HEAD"),
);
let ver_hash = output(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD"));
let short_ver_hash =
output(helpers::git(Some(dir)).arg("rev-parse").arg("--short=9").arg("HEAD"));
GitInfo::Present(Some(Info {
commit_date: ver_date.trim().to_string(),
sha: ver_hash.trim().to_string(),

View file

@ -331,115 +331,6 @@ fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
})
}
/// Copied from `std::path::absolute` until it stabilizes.
///
/// FIXME: this shouldn't exist.
pub(crate) fn absolute(path: &Path) -> PathBuf {
if path.as_os_str().is_empty() {
panic!("can't make empty path absolute");
}
#[cfg(unix)]
{
t!(absolute_unix(path), format!("could not make path absolute: {}", path.display()))
}
#[cfg(windows)]
{
t!(absolute_windows(path), format!("could not make path absolute: {}", path.display()))
}
#[cfg(not(any(unix, windows)))]
{
println!("WARNING: bootstrap is not supported on non-unix platforms");
t!(std::fs::canonicalize(t!(std::env::current_dir()))).join(path)
}
}
#[cfg(unix)]
/// Make a POSIX path absolute without changing its semantics.
fn absolute_unix(path: &Path) -> io::Result<PathBuf> {
// This is mostly a wrapper around collecting `Path::components`, with
// exceptions made where this conflicts with the POSIX specification.
// See 4.13 Pathname Resolution, IEEE Std 1003.1-2017
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
use std::os::unix::prelude::OsStrExt;
let mut components = path.components();
let path_os = path.as_os_str().as_bytes();
let mut normalized = if path.is_absolute() {
// "If a pathname begins with two successive <slash> characters, the
// first component following the leading <slash> characters may be
// interpreted in an implementation-defined manner, although more than
// two leading <slash> characters shall be treated as a single <slash>
// character."
if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
components.next();
PathBuf::from("//")
} else {
PathBuf::new()
}
} else {
env::current_dir()?
};
normalized.extend(components);
// "Interfaces using pathname resolution may specify additional constraints
// when a pathname that does not name an existing directory contains at
// least one non- <slash> character and contains one or more trailing
// <slash> characters".
// A trailing <slash> is also meaningful if "a symbolic link is
// encountered during pathname resolution".
if path_os.ends_with(b"/") {
normalized.push("");
}
Ok(normalized)
}
#[cfg(windows)]
fn absolute_windows(path: &std::path::Path) -> std::io::Result<std::path::PathBuf> {
use std::ffi::OsString;
use std::io::Error;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::ptr::null_mut;
#[link(name = "kernel32")]
extern "system" {
fn GetFullPathNameW(
lpFileName: *const u16,
nBufferLength: u32,
lpBuffer: *mut u16,
lpFilePart: *mut *const u16,
) -> u32;
}
unsafe {
// encode the path as UTF-16
let path: Vec<u16> = path.as_os_str().encode_wide().chain([0]).collect();
let mut buffer = Vec::new();
// Loop until either success or failure.
loop {
// Try to get the absolute path
let len = GetFullPathNameW(
path.as_ptr(),
buffer.len().try_into().unwrap(),
buffer.as_mut_ptr(),
null_mut(),
);
match len as usize {
// Failure
0 => return Err(Error::last_os_error()),
// Buffer is too small, resize.
len if len > buffer.len() => buffer.resize(len, 0),
// Success!
len => {
buffer.truncate(len);
return Ok(OsString::from_wide(&buffer).into());
}
}
}
}
}
/// Adapted from <https://github.com/llvm/llvm-project/blob/782e91224601e461c019e0a4573bbccc6094fbcd/llvm/cmake/modules/HandleLLVMOptions.cmake#L1058-L1079>
///
/// When `clang-cl` is used with instrumentation, we need to add clang's runtime library resource
@ -598,3 +489,19 @@ pub fn check_cfg_arg(name: &str, values: Option<&[&str]>) -> String {
};
format!("--check-cfg=cfg({name}{next})")
}
/// Prepares `Command` that runs git inside the source directory if given.
///
/// Whenever a git invocation is needed, this function should be preferred over
/// manually building a git `Command`. This approach allows us to manage bootstrap-specific
/// needs/hacks from a single source, rather than applying them on next to every `Command::new("git")`,
/// which is painful to ensure that the required change is applied on each one of them correctly.
pub fn git(source_dir: Option<&Path>) -> Command {
let mut git = Command::new("git");
if let Some(source_dir) = source_dir {
git.current_dir(source_dir);
}
git
}

View file

@ -25,27 +25,6 @@ fn test_make() {
}
}
#[cfg(unix)]
#[test]
fn test_absolute_unix() {
use crate::utils::helpers::absolute_unix;
// Test an absolute path
let path = PathBuf::from("/home/user/file.txt");
assert_eq!(absolute_unix(&path).unwrap(), PathBuf::from("/home/user/file.txt"));
// Test an absolute path with double leading slashes
let path = PathBuf::from("//root//file.txt");
assert_eq!(absolute_unix(&path).unwrap(), PathBuf::from("//root/file.txt"));
// Test a relative path
let path = PathBuf::from("relative/path");
assert_eq!(
absolute_unix(&path).unwrap(),
std::env::current_dir().unwrap().join("relative/path")
);
}
#[test]
fn test_beta_rev_parsing() {
// single digit revision

View file

@ -6,8 +6,12 @@ FROM centos:7
WORKDIR /build
# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault.
RUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \
-e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!'
RUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf
RUN yum upgrade -y && \
yum install -y epel-release && \
yum install -y \
automake \
bzip2 \

View file

@ -6,8 +6,12 @@ FROM centos:7
WORKDIR /build
# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault.
RUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \
-e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!'
RUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf
RUN yum upgrade -y && \
yum install -y epel-release && \
yum install -y \
automake \
bzip2 \

View file

@ -26,27 +26,27 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# Duplicated in dist-various-2 Dockerfile.
# FIXME: Move to canonical triple
ENV \
AR_x86_64_fuchsia=x86_64-unknown-fuchsia-ar \
CC_x86_64_fuchsia=x86_64-unknown-fuchsia-clang \
CFLAGS_x86_64_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -I/usr/local/core-linux-amd64-fuchsia-sdk/pkg/fdio/include" \
CXX_x86_64_fuchsia=x86_64-unknown-fuchsia-clang++ \
CXXFLAGS_x86_64_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -I/usr/local/core-linux-amd64-fuchsia-sdk/pkg/fdio/include" \
LDFLAGS_x86_64_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -L/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/lib"
AR_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-ar \
CC_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang \
CFLAGS_x86_64_unknown_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -I/usr/local/core-linux-amd64-fuchsia-sdk/pkg/fdio/include" \
CXX_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang++ \
CXXFLAGS_x86_64_unknown_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -I/usr/local/core-linux-amd64-fuchsia-sdk/pkg/fdio/include" \
LDFLAGS_x86_64_unknown_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -L/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/lib"
WORKDIR /tmp
COPY scripts/shared.sh /tmp/
COPY scripts/build-fuchsia-toolchain.sh /tmp/
RUN /tmp/build-fuchsia-toolchain.sh
ENV CARGO_TARGET_X86_64_FUCHSIA_AR /usr/local/bin/llvm-ar
ENV CARGO_TARGET_X86_64_FUCHSIA_RUSTFLAGS \
ENV CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_AR /usr/local/bin/llvm-ar
ENV CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_RUSTFLAGS \
-C panic=abort \
-C force-unwind-tables=yes \
-C link-arg=--sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot \
-Lnative=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot/lib \
-Lnative=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/lib
ENV TARGETS=x86_64-fuchsia
ENV TARGETS=x86_64-unknown-fuchsia
ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnu
ENV TARGETS=$TARGETS,wasm32-unknown-unknown
@ -69,11 +69,11 @@ ENV RUST_CONFIGURE_ARGS \
--llvm-libunwind=in-tree \
--enable-extended \
--disable-docs \
--set target.x86_64-fuchsia.cc=/usr/local/bin/clang \
--set target.x86_64-fuchsia.cxx=/usr/local/bin/clang++ \
--set target.x86_64-fuchsia.ar=/usr/local/bin/llvm-ar \
--set target.x86_64-fuchsia.ranlib=/usr/local/bin/llvm-ranlib \
--set target.x86_64-fuchsia.linker=/usr/local/bin/ld.lld
--set target.x86_64-unknown-fuchsia.cc=/usr/local/bin/clang \
--set target.x86_64-unknown-fuchsia.cxx=/usr/local/bin/clang++ \
--set target.x86_64-unknown-fuchsia.ar=/usr/local/bin/llvm-ar \
--set target.x86_64-unknown-fuchsia.ranlib=/usr/local/bin/llvm-ranlib \
--set target.x86_64-unknown-fuchsia.linker=/usr/local/bin/ld.lld
ENV SCRIPT \
python3 ../x.py install --target $TARGETS compiler/rustc library/std clippy && \
bash ../src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh

View file

@ -35,7 +35,7 @@ PICK_REFS=()
# commit hash of fuchsia.git and some other repos in the "monorepo" checkout, in
# addition to versions of prebuilts. It should be bumped regularly by the
# Fuchsia team  we aim for every 1-2 months.
INTEGRATION_SHA=1011e3298775ee7cdf6f6dc73e808d6a86e33bd6
INTEGRATION_SHA=737ebdd83afa47b742ca8325fad0176952fcefbd
checkout=fuchsia
jiri=.jiri_root/bin/jiri

View file

@ -4,13 +4,13 @@ set -ex
source shared.sh
FUCHSIA_SDK_URL=https://chrome-infra-packages.appspot.com/dl/fuchsia/sdk/core/linux-amd64
FUCHSIA_SDK_ID=version:20.20240412.3.1
FUCHSIA_SDK_SHA256=cc52f3497487dd813c89d9316e6967efcea89c7759edccf3e40fcf3662e53f19
FUCHSIA_SDK_ID=version:21.20240610.2.1
FUCHSIA_SDK_SHA256=2d2d057fc3f0404197cced2200f88cbcdaaf5fbf6475955045091f8676791ce7
FUCHSIA_SDK_USR_DIR=/usr/local/core-linux-amd64-fuchsia-sdk
CLANG_DOWNLOAD_URL=\
https://chrome-infra-packages.appspot.com/dl/fuchsia/third_party/clang/linux-amd64
CLANG_DOWNLOAD_ID=git_revision:c777c011a709dffd4fa5e79cad7947b7c3405d02
CLANG_DOWNLOAD_SHA256=779167422ad73c292f049dcea5569f84577af9292189ed2749518b966a4d0844
CLANG_DOWNLOAD_ID=git_revision:3809e20afc68d7d03821f0ec59b928dcf9befbf4
CLANG_DOWNLOAD_SHA256=3c2c442b61cd9e8f1b567738f6d53cffe11b3fc820e7dae87a82a0859be8f204
install_clang() {
mkdir -p clang_download

View file

@ -286,7 +286,7 @@ class TestEnvironment:
# Look up the product bundle transfer manifest.
self.log_info("Looking up the product bundle transfer manifest...")
product_name = "minimal." + self.triple_to_arch(self.target)
fuchsia_version = "20.20240412.3.1"
fuchsia_version = "21.20240610.2.1"
out = self.check_output(
[

View file

@ -390,9 +390,7 @@ auto:
- image: x86_64-mingw
env:
SCRIPT: make ci-mingw
RUST_CONFIGURE_ARGS: >-
--build=x86_64-pc-windows-gnu
--enable-profiler
RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu
# We are intentionally allowing an old toolchain on this builder (and that's
# incompatible with LLVM downloads today).
NO_DOWNLOAD_CI_LLVM: 1
@ -440,7 +438,6 @@ auto:
RUST_CONFIGURE_ARGS: >-
--build=i686-pc-windows-gnu
--enable-full-tools
--enable-profiler
# We are intentionally allowing an old toolchain on this builder (and that's
# incompatible with LLVM downloads today).
NO_DOWNLOAD_CI_LLVM: 1
@ -454,7 +451,6 @@ auto:
RUST_CONFIGURE_ARGS: >-
--build=x86_64-pc-windows-gnu
--enable-full-tools
--enable-profiler
# We are intentionally allowing an old toolchain on this builder (and that's
# incompatible with LLVM downloads today).
NO_DOWNLOAD_CI_LLVM: 1

View file

@ -71,6 +71,7 @@
- [*-unknown-hermit](platform-support/hermit.md)
- [\*-unknown-netbsd\*](platform-support/netbsd.md)
- [*-unknown-openbsd](platform-support/openbsd.md)
- [*-unknown-redox](platform-support/redox.md)
- [\*-unknown-uefi](platform-support/unknown-uefi.md)
- [wasm32-wasip1](platform-support/wasm32-wasip1.md)
- [wasm32-wasip1-threads](platform-support/wasm32-wasip1-threads.md)

View file

@ -11,8 +11,8 @@ development process.
In order to accomplish that goal, `rustc` accepts the `--check-cfg` flag, which specifies
whether to check conditions and how to check them.
> **Note:** No implicit expectation is added when using `--cfg`. Users are expected to
pass all expected names and values using the _check cfg specification_.
> **Note:** For interacting with this through Cargo,
see [Cargo Specifics](check-cfg/cargo-specifics.md) page.
[^reachable]: `rustc` promises to at least check reachable `#[cfg]`, and while non-reachable
`#[cfg]` are not currently checked, they may well be checked in the future without it being a
@ -23,6 +23,9 @@ breaking change.
To specify expected names and values, the _check cfg specification_ provides the `cfg(...)`
option which enables specifying for an expected config name and it's expected values.
> **Note:** No implicit expectation is added when using `--cfg`. Users are expected to
pass all expected names and values using the _check cfg specification_.
It has this basic form:
```bash

View file

@ -203,7 +203,7 @@ target | std | notes
`x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27)
[`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | x86_64 OpenHarmony
[`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat
`x86_64-unknown-redox` | ✓ | Redox OS
[`x86_64-unknown-redox`](platform-support/redox.md) | ✓ | Redox OS
[`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI
[^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue].
@ -258,8 +258,8 @@ target | std | host | notes
`aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI)
[`aarch64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD
[`aarch64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | ARM64 OpenBSD
`aarch64-unknown-redox` | ? | | ARM64 Redox OS
`aarch64-uwp-windows-msvc` | ? | |
[`aarch64-unknown-redox`](platform-support/redox.md) | ✓ | | ARM64 Redox OS
`aarch64-uwp-windows-msvc` | | |
`aarch64-wrs-vxworks` | ? | |
`aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI)
`aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian)
@ -300,8 +300,9 @@ target | std | host | notes
[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd [^x86_32-floats-return-ABI]
[`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI]
[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD [^x86_32-floats-return-ABI]
`i686-uwp-windows-gnu` | ? | | [^x86_32-floats-return-ABI]
`i686-uwp-windows-msvc` | ? | | [^x86_32-floats-return-ABI]
[`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS
`i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI]
`i686-uwp-windows-msvc` | ✓ | | [^x86_32-floats-return-ABI]
[`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI]
`i686-wrs-vxworks` | ? | | [^x86_32-floats-return-ABI]
[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | ? | | LoongArch64 Linux (LP64D ABI) with musl 1.2.3
@ -362,7 +363,7 @@ target | std | host | notes
[`sparc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/sparc64
[`thumbv4t-none-eabi`](platform-support/armv4t-none-eabi.md) | * | | Thumb-mode Bare Armv4T
[`thumbv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Thumb-mode Bare Armv5TE
`thumbv7a-pc-windows-msvc` | ? | |
`thumbv7a-pc-windows-msvc` | | |
`thumbv7a-uwp-windows-msvc` | ✓ | |
`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.3
[`wasm32-wasip2`](platform-support/wasm32-wasip2.md) | ✓ | | WebAssembly

View file

@ -0,0 +1,53 @@
# `*-unknown-redox`
**Tier: 2/3**
Targets for the [Redox OS](https://redox-os.org/) operating
system.
Target triplets available so far:
- `x86_64-unknown-redox` (tier 2)
- `aarch64-unknown-redox` (tier 3)
- `i686-unknown-redox` (tier 3)
## Target maintainers
- Jeremy Soller ([@jackpot51](https://github.com/jackpot51))
## Requirements
These targets are natively compiled and can be cross-compiled. Std is fully supported.
The targets are only expected to work with the latest version of Redox OS as the ABI is not yet stable.
`extern "C"` uses the official calling convention of the respective architectures.
Redox OS binaries use ELF as file format.
## Building the target
You can build Rust with support for the targets by adding it to the `target` list in `config.toml`. In addition a copy of [relibc] needs to be present in the linker search path.
```toml
[build]
build-stage = 1
target = [
"<HOST_TARGET>",
"x86_64-unknown-redox",
"aarch64-unknown-redox",
"i686-unknown-redox",
]
```
[relibc]: https://gitlab.redox-os.org/redox-os/relibc
## Building Rust programs and testing
Rust does not yet ship pre-compiled artifacts for Redox OS except for x86_64-unknown-redox.
The easiest way to build and test programs for Redox OS is using [redoxer](https://gitlab.redox-os.org/redox-os/redoxer) which sets up the required compiler toolchain for building as well as runs programs inside a Redox OS VM using QEMU.
## Cross-compilation toolchains and C code
The target supports C code. Pre-compiled C toolchains can be found at <https://static.redox-os.org/toolchain/>.

View file

@ -7,8 +7,6 @@
set -Euo pipefail
# https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570
unset GIT_DIR
ROOT_DIR="$(git rev-parse --show-toplevel)"
echo "Running pre-push script $ROOT_DIR/x test tidy"

View file

@ -103,6 +103,7 @@ static TARGETS: &[&str] = &[
"i686-unknown-freebsd",
"i686-unknown-linux-gnu",
"i686-unknown-linux-musl",
"i686-unknown-redox",
"i686-unknown-uefi",
"loongarch64-unknown-linux-gnu",
"loongarch64-unknown-none",
@ -495,7 +496,7 @@ impl Builder {
Some(p) => p,
None => return false,
};
pkg.target.get(&c.target).is_some()
pkg.target.contains_key(&c.target)
};
extensions.retain(&has_component);
components.retain(&has_component);

View file

@ -21,7 +21,7 @@ fn output_result(cmd: &mut Command) -> Result<String, String> {
String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
));
}
Ok(String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?)
String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))
}
/// Finds the remote for rust-lang/rust.
@ -64,18 +64,14 @@ pub fn rev_exists(rev: &str, git_dir: Option<&Path>) -> Result<bool, String> {
match output.status.code() {
Some(0) => Ok(true),
Some(128) => Ok(false),
None => {
return Err(format!(
"git didn't exit properly: {}",
String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
));
}
Some(code) => {
return Err(format!(
"git command exited with status code: {code}: {}",
String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
));
}
None => Err(format!(
"git didn't exit properly: {}",
String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
)),
Some(code) => Err(format!(
"git command exited with status code: {code}: {}",
String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
)),
}
}
@ -96,7 +92,7 @@ pub fn updated_master_branch(
}
}
Err(format!("Cannot find any suitable upstream master branch"))
Err("Cannot find any suitable upstream master branch".to_owned())
}
pub fn get_git_merge_base(
@ -118,7 +114,7 @@ pub fn get_git_merge_base(
pub fn get_git_modified_files(
config: &GitConfig<'_>,
git_dir: Option<&Path>,
extensions: &Vec<&str>,
extensions: &[&str],
) -> Result<Option<Vec<String>>, String> {
let merge_base = get_git_merge_base(config, git_dir)?;

@ -1 +1 @@
Subproject commit 4dcbca118ab7f9ffac4728004c983754bc6a04ff
Subproject commit a1f47ec3f7cd076986f1bfcd7061f2e8cb1a726e

View file

@ -6,11 +6,69 @@ document.
## Unreleased / Beta / In Rust Nightly
[93f0a9a9...master](https://github.com/rust-lang/rust-clippy/compare/93f0a9a9...master)
[ca3b3937...master](https://github.com/rust-lang/rust-clippy/compare/ca3b3937...master)
## Rust 1.79
Current stable, released 2024-06-13
[View all 102 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-03-08T11%3A13%3A58Z..2024-04-18T15%3A50%3A50Z+base%3Amaster)
### New Lints
* Added [`legacy_numeric_constants`] to `style`
[#12312](https://github.com/rust-lang/rust-clippy/pull/12312)
* Added [`missing_transmute_annotations`] to `suspicious`
[#12239](https://github.com/rust-lang/rust-clippy/pull/12239)
* Added [`integer_division_remainder_used`] to `restriction`
[#12451](https://github.com/rust-lang/rust-clippy/pull/12451)
* Added [`duplicated_attributes`] to `suspicious`
[#12378](https://github.com/rust-lang/rust-clippy/pull/12378)
* Added [`manual_unwrap_or_default`] to `suspicious`
[#12440](https://github.com/rust-lang/rust-clippy/pull/12440)
* Added [`zero_repeat_side_effects`] to `suspicious`
[#12449](https://github.com/rust-lang/rust-clippy/pull/12449)
* Added [`const_is_empty`] to `suspicious`
[#12310](https://github.com/rust-lang/rust-clippy/pull/12310)
### Moves and Deprecations
* Moved [`box_default`] to `style` (From `perf`)
[#12601](https://github.com/rust-lang/rust-clippy/pull/12601)
* Moved [`manual_clamp`] to `complexity` (From `nursery` now warn-by-default)
[#12543](https://github.com/rust-lang/rust-clippy/pull/12543)
* Moved [`readonly_write_lock`] to `perf` (From `nursery` now warn-by-default)
[#12479](https://github.com/rust-lang/rust-clippy/pull/12479)
### Enhancements
* [`module_name_repetitions`]: Added the [`allowed-prefixes`] configuration to allow common prefixes.
[#12573](https://github.com/rust-lang/rust-clippy/pull/12573)
* [`cast_sign_loss`], [`cast_possible_truncation`], [`cast_lossless`]: Are now allowed in macros
[#12631](https://github.com/rust-lang/rust-clippy/pull/12631)
* [`manual_clamp`]: Now only lints on constant min and max values
[#12543](https://github.com/rust-lang/rust-clippy/pull/12543)
* [`assigning_clones`]: Now considers the [`msrv`] configuration
[#12511](https://github.com/rust-lang/rust-clippy/pull/12511)
* [`needless_return`], [`useless_let_if_seq`], [`mut_mut`], [`read_zero_byte_vec`], [`unused_io_amount`],
[`unused_peekable`]: Now respects `#[allow]` attributes on the affected statement instead
[#12446](https://github.com/rust-lang/rust-clippy/pull/12446)
### False Positive Fixes
* [`cast_lossless`]: No longer lints when casting to `u128`
[#12496](https://github.com/rust-lang/rust-clippy/pull/12496)
* [`std_instead_of_core`] No longer lints on modules that are only in `std`
[#12447](https://github.com/rust-lang/rust-clippy/pull/12447)
### ICE Fixes
* [`needless_return`]: No longer crashes on non-ascii characters
[#12493](https://github.com/rust-lang/rust-clippy/pull/12493)
## Rust 1.78
Current stable, released 2024-05-02
Released 2024-05-02
[View all 112 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-01-26T05%3A46%3A23Z..2024-03-07T16%3A25%3A52Z+base%3Amaster)
@ -5474,6 +5532,7 @@ Released 2018-09-13
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
[`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
[`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
@ -5567,6 +5626,7 @@ Released 2018-09-13
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
[`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args
[`needless_character_iteration`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_character_iteration
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
@ -5576,6 +5636,7 @@ Released 2018-09-13
[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
[`needless_maybe_sized`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_maybe_sized
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals

View file

@ -1,6 +1,6 @@
[package]
name = "clippy"
version = "0.1.80"
version = "0.1.81"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"

View file

@ -172,7 +172,7 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
Note: `allow` means to suppress the lint for your code. With `warn` the lint
will only emit a warning, while with `deny` the lint will emit an error, when
triggering for your code. An error causes clippy to exit with an error code, so
triggering for your code. An error causes Clippy to exit with an error code, so
is useful in scripts like CI/CD.
If you do not want to include your lint levels in your code, you can globally
@ -238,7 +238,7 @@ define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
### Specifying the minimum supported Rust version
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
specifying the minimum supported Rust version (MSRV) in the clippy configuration file.
specifying the minimum supported Rust version (MSRV) in the Clippy configuration file.
```toml
msrv = "1.30.0"

View file

@ -99,7 +99,7 @@ For more details and options, refer to the Cargo documentation.
### Specifying the minimum supported Rust version
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the
minimum supported Rust version (MSRV) in the clippy configuration file.
minimum supported Rust version (MSRV) in the Clippy configuration file.
```toml
msrv = "1.30.0"

View file

@ -107,7 +107,7 @@ More about [intellij] command usage and reasons.
## lintcheck
`cargo lintcheck` will build and run clippy on a fixed set of crates and
`cargo lintcheck` will build and run Clippy on a fixed set of crates and
generate a log of the results. You can `git diff` the updated log against its
previous version and see what impact your lint made on a small set of crates.
If you add a new lint, please audit the resulting warnings and make sure there

View file

@ -163,11 +163,11 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// // example code where clippy issues a warning
/// // example code where Clippy issues a warning
/// ```
/// Use instead:
/// ```rust
/// // example code which does not raise clippy warning
/// // example code which does not raise Clippy warning
/// ```
#[clippy::version = "1.70.0"] // <- In which version was this implemented, keep it up to date!
pub LINT_NAME, // <- The lint name IN_ALL_CAPS

View file

@ -428,7 +428,7 @@ selection of possible matches is produced by the pattern syntax. In the second
stage, the named subpattern references can be used to do additional tests like
asserting that a node hasn't been created as part of a macro expansion.
## Implementing clippy lints using patterns
## Implementing Clippy lints using patterns
As a "real-world" example, I re-implemented the `collapsible_if` lint using
patterns. The code can be found
@ -572,7 +572,7 @@ The pattern syntax and the *PatternTree* are independent of specific syntax tree
implementations (rust ast / hir, syn, ...). When looking at the different
pattern examples in the previous sections, it can be seen that the patterns
don't contain any information specific to a certain syntax tree implementation.
In contrast, clippy lints currently match against ast / hir syntax tree nodes
In contrast, Clippy lints currently match against ast / hir syntax tree nodes
and therefore directly depend on their implementation.
The connection between the *PatternTree* and specific syntax tree
@ -690,7 +690,7 @@ change, only the `IsMatch` trait implementations need to be adapted and existing
lints can remain unchanged. This also means that if the `IsMatch` trait
implementations were integrated into the compiler, updating the `IsMatch`
implementations would be required for the compiler to compile successfully. This
could reduce the number of times clippy breaks because of changes in the
could reduce the number of times Clippy breaks because of changes in the
compiler. Another advantage of the pattern's independence is that converting an
`EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole
pattern matching code. In fact, the pattern might work just fine without any
@ -777,7 +777,7 @@ complexity to solve a relatively minor problem.
The issue of users not knowing about the *PatternTree* structure could be solved
by a tool that, given a rust program, generates a pattern that matches only this
program (similar to the clippy author lint).
program (similar to the Clippy author lint).
For some simple cases (like the first example above), it might be possible to
successfully mix Rust and pattern syntax. This space could be further explored
@ -789,7 +789,7 @@ The pattern syntax is heavily inspired by regular expressions (repetitions,
alternatives, sequences, ...).
From what I've seen until now, other linters also implement lints that directly
work on syntax tree data structures, just like clippy does currently. I would
work on syntax tree data structures, just like Clippy does currently. I would
therefore consider the pattern syntax to be *new*, but please correct me if I'm
wrong.
@ -982,5 +982,5 @@ pattern!{
}
```
In the future, clippy could use this system to also provide lints for custom
In the future, Clippy could use this system to also provide lints for custom
syntaxes like those found in macros.

View file

@ -1,6 +1,6 @@
[package]
name = "clippy_config"
version = "0.1.80"
version = "0.1.81"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1,5 +1,5 @@
This is a dummy crate to publish to crates.io. It primarily exists to ensure
that folks trying to install clippy from crates.io get redirected to the
that folks trying to install Clippy from crates.io get redirected to the
`rustup` technique.
Before publishing, be sure to rename `clippy_dummy` to `clippy` in `Cargo.toml`,

View file

@ -1,9 +1,9 @@
Installing clippy via crates.io is deprecated. Please use the following:
Installing Clippy via crates.io is deprecated. Please use the following:
```terminal
rustup component add clippy
```
on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary.
on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing Clippy binary.
See [the homepage](https://github.com/rust-lang/rust-clippy/#clippy) for more information

View file

@ -1,6 +1,6 @@
[package]
name = "clippy_lints"
version = "0.1.80"
version = "0.1.81"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"

View file

@ -1,51 +0,0 @@
use super::{Attribute, MAYBE_MISUSED_CFG};
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
use rustc_span::sym;
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
if attr.has_name(sym::cfg)
&& let Some(items) = attr.meta_item_list()
{
check_nested_misused_cfg(cx, &items);
}
}
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
for item in items {
if let NestedMetaItem::MetaItem(meta) = item {
if let Some(ident) = meta.ident()
&& ident.name.as_str() == "features"
&& let Some(val) = meta.value_str()
{
span_lint_and_sugg(
cx,
MAYBE_MISUSED_CFG,
meta.span,
"'feature' may be misspelled as 'features'",
"did you mean",
format!("feature = \"{val}\""),
Applicability::MaybeIncorrect,
);
}
if let MetaItemKind::List(list) = &meta.kind {
check_nested_misused_cfg(cx, list);
// If this is not a list, then we check for `cfg(test)`.
} else if let Some(ident) = meta.ident()
&& matches!(ident.name.as_str(), "tests" | "Test")
{
span_lint_and_sugg(
cx,
MAYBE_MISUSED_CFG,
meta.span,
format!("'test' may be misspelled as '{}'", ident.name.as_str()),
"did you mean",
"test".to_string(),
Applicability::MaybeIncorrect,
);
}
}
}
}

View file

@ -1,90 +0,0 @@
use super::{Attribute, MISMATCHED_TARGET_OS};
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
use rustc_span::{sym, Span};
static UNIX_SYSTEMS: &[&str] = &[
"android",
"dragonfly",
"emscripten",
"freebsd",
"fuchsia",
"haiku",
"illumos",
"ios",
"l4re",
"linux",
"macos",
"netbsd",
"openbsd",
"redox",
"solaris",
"vxworks",
];
// NOTE: windows is excluded from the list because it's also a valid target family.
static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"];
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
fn find_os(name: &str) -> Option<&'static str> {
UNIX_SYSTEMS
.iter()
.chain(NON_UNIX_SYSTEMS.iter())
.find(|&&os| os == name)
.copied()
}
fn is_unix(name: &str) -> bool {
UNIX_SYSTEMS.iter().any(|&os| os == name)
}
fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
let mut mismatched = Vec::new();
for item in items {
if let NestedMetaItem::MetaItem(meta) = item {
match &meta.kind {
MetaItemKind::List(list) => {
mismatched.extend(find_mismatched_target_os(list));
},
MetaItemKind::Word => {
if let Some(ident) = meta.ident()
&& let Some(os) = find_os(ident.name.as_str())
{
mismatched.push((os, ident.span));
}
},
MetaItemKind::NameValue(..) => {},
}
}
}
mismatched
}
if attr.has_name(sym::cfg)
&& let Some(list) = attr.meta_item_list()
&& let mismatched = find_mismatched_target_os(&list)
&& !mismatched.is_empty()
{
let mess = "operating system used in target family position";
span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
// Avoid showing the unix suggestion multiple times in case
// we have more than one mismatch for unix-like systems
let mut unix_suggested = false;
for (os, span) in mismatched {
let sugg = format!("target_os = \"{os}\"");
diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
if !unix_suggested && is_unix(os) {
diag.help("did you mean `unix`?");
unix_suggested = true;
}
}
});
}
}

View file

@ -7,8 +7,6 @@ mod deprecated_semver;
mod duplicated_attributes;
mod empty_line_after;
mod inline_always;
mod maybe_misused_cfg;
mod mismatched_target_os;
mod mixed_attributes_style;
mod non_minimal_cfg;
mod should_panic_without_expect;
@ -270,39 +268,6 @@ declare_clippy_lint! {
"usage of `cfg_attr(rustfmt)` instead of tool attributes"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for cfg attributes having operating systems used in target family position.
///
/// ### Why is this bad?
/// The configuration option will not be recognised and the related item will not be included
/// by the conditional compilation engine.
///
/// ### Example
/// ```no_run
/// #[cfg(linux)]
/// fn conditional() { }
/// ```
///
/// Use instead:
/// ```no_run
/// # mod hidden {
/// #[cfg(target_os = "linux")]
/// fn conditional() { }
/// # }
///
/// // or
///
/// #[cfg(unix)]
/// fn conditional() { }
/// ```
/// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
#[clippy::version = "1.45.0"]
pub MISMATCHED_TARGET_OS,
correctness,
"usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for attributes that allow lints without a reason.
@ -391,38 +356,6 @@ declare_clippy_lint! {
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[cfg(features = "...")]` and suggests to replace it with
/// `#[cfg(feature = "...")]`.
///
/// It also checks if `cfg(test)` was misspelled.
///
/// ### Why is this bad?
/// Misspelling `feature` as `features` or `test` as `tests` can be sometimes hard to spot. It
/// may cause conditional compilation not work quietly.
///
/// ### Example
/// ```no_run
/// #[cfg(features = "some-feature")]
/// fn conditional() { }
/// #[cfg(tests)]
/// mod tests { }
/// ```
///
/// Use instead:
/// ```no_run
/// #[cfg(feature = "some-feature")]
/// fn conditional() { }
/// #[cfg(test)]
/// mod tests { }
/// ```
#[clippy::version = "1.69.0"]
pub MAYBE_MISUSED_CFG,
suspicious,
"prevent from misusing the wrong attr name"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for
@ -530,7 +463,7 @@ declare_clippy_lint! {
/// #[allow(dead_code)]
/// fn foo() {}
/// ```
#[clippy::version = "1.78.0"]
#[clippy::version = "1.79.0"]
pub DUPLICATED_ATTRIBUTES,
suspicious,
"duplicated attribute"
@ -612,11 +545,9 @@ pub struct EarlyAttributes {
impl_lint_pass!(EarlyAttributes => [
DEPRECATED_CFG_ATTR,
MISMATCHED_TARGET_OS,
EMPTY_LINE_AFTER_OUTER_ATTR,
EMPTY_LINE_AFTER_DOC_COMMENTS,
NON_MINIMAL_CFG,
MAYBE_MISUSED_CFG,
DEPRECATED_CLIPPY_CFG_ATTR,
UNNECESSARY_CLIPPY_CFG,
]);
@ -629,9 +560,7 @@ impl EarlyLintPass for EarlyAttributes {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
deprecated_cfg_attr::check(cx, attr, &self.msrv);
deprecated_cfg_attr::check_clippy(cx, attr);
mismatched_target_os::check(cx, attr);
non_minimal_cfg::check(cx, attr);
maybe_misused_cfg::check(cx, attr);
}
extract_msrv_attr!(EarlyContext);

View file

@ -1,15 +1,11 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_block_with_applicability;
use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::{for_each_expr, Descend};
use clippy_utils::{get_parent_expr, higher, is_from_proc_macro};
use core::ops::ControlFlow;
use clippy_utils::{higher, is_from_proc_macro};
use rustc_errors::Applicability;
use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::declare_lint_pass;
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@ -124,30 +120,6 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions {
);
}
}
} else {
let _: Option<!> = for_each_expr(cond, |e| {
if let ExprKind::Closure(closure) = e.kind {
// do not lint if the closure is called using an iterator (see #1141)
if let Some(parent) = get_parent_expr(cx, e)
&& let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind
&& let caller = cx.typeck_results().expr_ty(self_arg)
&& let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
&& implements_trait(cx, caller, iter_id, &[])
{
return ControlFlow::Continue(Descend::No);
}
let body = cx.tcx.hir().body(closure.body);
let ex = &body.value;
if let ExprKind::Block(block, _) = ex.kind {
if !body.value.span.from_expansion() && !block.stmts.is_empty() {
span_lint(cx, BLOCKS_IN_CONDITIONS, ex.span, complex_block_message.clone());
return ControlFlow::Continue(Descend::No);
}
}
}
ControlFlow::Continue(Descend::Yes)
});
}
}
}

View file

@ -174,6 +174,25 @@ fn check_inverted_bool_in_condition(
);
}
fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
&& !expr.span.from_expansion()
&& !inner.span.from_expansion()
&& let Some(suggestion) = simplify_not(cx, inner)
&& cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
{
span_lint_and_sugg(
cx,
NONMINIMAL_BOOL,
expr.span,
"this boolean expression can be simplified",
"try",
suggestion,
Applicability::MachineApplicable,
);
}
}
struct NonminimalBoolVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
}
@ -232,6 +251,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
_ => (),
}
}
if self.cx.typeck_results().expr_ty(e).is_never() {
return Err("contains never type".to_owned());
}
for (n, expr) in self.terminals.iter().enumerate() {
if eq_expr_value(self.cx, e, expr) {
#[expect(clippy::cast_possible_truncation)]
@ -542,8 +566,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
}
};
if improvements.is_empty() {
let mut visitor = NotSimplificationVisitor { cx: self.cx };
visitor.visit_expr(e);
check_simplify_not(self.cx, e);
} else {
nonminimal_bool_lint(
improvements
@ -586,30 +609,3 @@ fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
.get_diagnostic_item(sym::Ord)
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
}
struct NotSimplificationVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
&& !expr.span.from_expansion()
&& !inner.span.from_expansion()
&& let Some(suggestion) = simplify_not(self.cx, inner)
&& self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
{
span_lint_and_sugg(
self.cx,
NONMINIMAL_BOOL,
expr.span,
"this boolean expression can be simplified",
"try",
suggestion,
Applicability::MachineApplicable,
);
}
walk_expr(self, expr);
}
}

View file

@ -71,12 +71,6 @@ struct CargoToml {
workspace: Workspace,
}
#[derive(Default, Debug)]
struct LintsAndGroups {
lints: Vec<Spanned<String>>,
groups: Vec<(Spanned<String>, Spanned<LintConfig>)>,
}
fn toml_span(range: Range<usize>, file: &SourceFile) -> Span {
Span::new(
file.start_pos + BytePos::from_usize(range.start),
@ -86,27 +80,28 @@ fn toml_span(range: Range<usize>, file: &SourceFile) -> Span {
)
}
fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, file: &SourceFile) {
let mut by_priority = BTreeMap::<_, LintsAndGroups>::new();
fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet<&str>, file: &SourceFile) {
let mut lints = Vec::new();
let mut groups = Vec::new();
for (name, config) in table {
let lints_and_groups = by_priority.entry(config.as_ref().priority()).or_default();
if groups.contains(name.get_ref().as_str()) {
lints_and_groups.groups.push((name, config));
if name.get_ref() == "warnings" {
continue;
}
if known_groups.contains(name.get_ref().as_str()) {
groups.push((name, config));
} else {
lints_and_groups.lints.push(name);
lints.push((name, config.into_inner()));
}
}
let low_priority = by_priority
.iter()
.find(|(_, lints_and_groups)| !lints_and_groups.lints.is_empty())
.map_or(-1, |(&lowest_lint_priority, _)| lowest_lint_priority - 1);
for (priority, LintsAndGroups { lints, groups }) in by_priority {
let Some(last_lint_alphabetically) = lints.last() else {
continue;
};
for (group, config) in groups {
for (group, group_config) in groups {
let priority = group_config.get_ref().priority();
let level = group_config.get_ref().level();
if let Some((conflict, _)) = lints
.iter()
.rfind(|(_, lint_config)| lint_config.priority() == priority && lint_config.level() != level)
{
span_lint_and_then(
cx,
LINT_GROUPS_PRIORITY,
@ -116,22 +111,23 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>,
group.as_ref()
),
|diag| {
let config_span = toml_span(config.span(), file);
if config.as_ref().is_implicit() {
let config_span = toml_span(group_config.span(), file);
if group_config.as_ref().is_implicit() {
diag.span_label(config_span, "has an implicit priority of 0");
}
// add the label to next lint after this group that has the same priority
let lint = lints
.iter()
.filter(|lint| lint.span().start > group.span().start)
.min_by_key(|lint| lint.span().start)
.unwrap_or(last_lint_alphabetically);
diag.span_label(toml_span(lint.span(), file), "has the same priority as this lint");
diag.span_label(toml_span(conflict.span(), file), "has the same priority as this lint");
diag.note("the order of the lints in the table is ignored by Cargo");
let mut suggestion = String::new();
let low_priority = lints
.iter()
.map(|(_, config)| config.priority().saturating_sub(1))
.min()
.unwrap_or(-1);
Serialize::serialize(
&LintConfigTable {
level: config.as_ref().level().into(),
level: level.into(),
priority: Some(low_priority),
},
toml::ser::ValueSerializer::new(&mut suggestion),

View file

@ -3,7 +3,7 @@ use std::ops::ControlFlow;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::visitors::{for_each_expr, Descend};
use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
use clippy_utils::{method_chain_args, sext};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
@ -266,7 +266,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
let mut res = vec![];
for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
// We don't check for mul/div/rem methods here, but we could.
if let ExprKind::Binary(op, lhs, _rhs) = sub_expr.kind {
if matches!(op.node, BinOpKind::Mul | BinOpKind::Div) {
@ -315,7 +315,7 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
fn exprs_with_add_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
let mut res = vec![];
for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
// We don't check for add methods here, but we could.
if let ExprKind::Binary(op, _lhs, _rhs) = sub_expr.kind {
if matches!(op.node, BinOpKind::Add) {

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::numeric_literal::NumericLiteral;
use clippy_utils::source::snippet_opt;
use clippy_utils::visitors::{for_each_expr, Visitable};
use clippy_utils::visitors::{for_each_expr_without_closures, Visitable};
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
use rustc_ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
@ -245,7 +245,7 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
/// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark,
/// dark path reimplementing this (or something similar).
fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool {
for_each_expr(expr, |expr| {
for_each_expr_without_closures(expr, |expr| {
// Calls are a `Path`, and usage of locals are a `Path`. So, this checks
// - call() as i32
// - local as i32

View file

@ -3,7 +3,7 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
use core::ops::ControlFlow;
use rustc_ast::ast::Attribute;
@ -65,7 +65,7 @@ impl CognitiveComplexity {
let mut cc = 1u64;
let mut returns = 0u64;
let _: Option<!> = for_each_expr(expr, |e| {
let _: Option<!> = for_each_expr_without_closures(expr, |e| {
match e.kind {
ExprKind::If(_, _, _) => {
cc += 1;

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::visitors::{for_each_expr_with_closures, Visitable};
use clippy_utils::visitors::{for_each_expr, Visitable};
use clippy_utils::{get_enclosing_block, path_to_local_id};
use core::ops::ControlFlow;
use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
@ -82,7 +82,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI
let mut has_read_access = false;
// Inspect all expressions and sub-expressions in the block.
for_each_expr_with_closures(cx, block, |expr| {
for_each_expr(cx, block, |expr| {
// Ignore expressions that are not simply `id`.
if !path_to_local_id(expr, id) {
return ControlFlow::Continue(());

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
use clippy_utils::visitors::for_each_expr;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{
capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
@ -362,7 +362,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
/// Checks if the statement modifies or moves any of the given locals.
fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool {
for_each_expr(s, |e| {
for_each_expr_without_closures(s, |e| {
if let Some(id) = path_to_local(e)
&& locals.contains(&id)
&& !capture_local_usage(cx, e).is_imm_ref()
@ -413,7 +413,7 @@ fn scan_block_for_eq<'tcx>(
let mut cond_locals = HirIdSet::default();
for &cond in conds {
let _: Option<!> = for_each_expr(cond, |e| {
let _: Option<!> = for_each_expr_without_closures(cond, |e| {
if let Some(id) = path_to_local(e) {
cond_locals.insert(id);
}

View file

@ -58,8 +58,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
crate::attrs::INLINE_ALWAYS_INFO,
crate::attrs::MAYBE_MISUSED_CFG_INFO,
crate::attrs::MISMATCHED_TARGET_OS_INFO,
crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
crate::attrs::NON_MINIMAL_CFG_INFO,
crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
@ -419,6 +417,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::MAP_UNWRAP_OR_INFO,
crate::methods::MUT_MUTEX_LOCK_INFO,
crate::methods::NAIVE_BYTECOUNT_INFO,
crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO,
crate::methods::NEEDLESS_COLLECT_INFO,
crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO,
crate::methods::NEEDLESS_OPTION_TAKE_INFO,
@ -449,7 +448,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO,
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
crate::methods::SINGLE_CHAR_PATTERN_INFO,
crate::methods::SKIP_WHILE_NEXT_INFO,
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
crate::methods::STRING_EXTEND_CHARS_INFO,
@ -531,6 +529,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
crate::needless_if::NEEDLESS_IF_INFO,
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO,
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO,
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
@ -656,6 +655,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,
crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO,
crate::string_patterns::MANUAL_PATTERN_CHAR_COMPARISON_INFO,
crate::string_patterns::SINGLE_CHAR_PATTERN_INFO,
crate::strings::STRING_ADD_INFO,
crate::strings::STRING_ADD_ASSIGN_INFO,
crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO,

View file

@ -215,3 +215,29 @@ declare_deprecated_lint! {
pub WRONG_PUB_SELF_CONVENTION,
"set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect the `#[cfg(features)]` and `#[cfg(tests)]` typos.
///
/// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs
#[clippy::version = "1.80.0"]
pub MAYBE_MISUSED_CFG,
"this lint has been replaced by `unexpected_cfgs`"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect invalid `#[cfg(linux)]` attributes.
///
/// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs
#[clippy::version = "1.80.0"]
pub MISMATCHED_TARGET_OS,
"this lint has been replaced by `unexpected_cfgs`"
}

View file

@ -37,7 +37,7 @@ pub fn check(
cx,
MISSING_SAFETY_DOC,
span,
"unsafe function's docs miss `# Safety` section",
"unsafe function's docs are missing a `# Safety` section",
),
(true, Safety::Safe) => span_lint(
cx,

View file

@ -9,8 +9,8 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{
self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty,
TypeVisitableExt, TypeckResults, TyCtxt,
self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt,
TypeVisitableExt, TypeckResults,
};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym;
@ -123,7 +123,8 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
) =>
{
let callee_ty = typeck.expr_ty(callee).peel_refs();
let callee_ty_raw = typeck.expr_ty(callee);
let callee_ty = callee_ty_raw.peel_refs();
if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
|| !check_inputs(typeck, body.params, None, args)
{
@ -170,15 +171,25 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
{
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
if let Ok((ClosureKind::FnMut, _)) = cx.tcx.infer_ctxt().build().type_implements_fn_trait(
cx.param_env,
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
ty::PredicatePolarity::Positive,
) && path_to_local(callee).map_or(false, |l| {
if path_to_local(callee).map_or(false, |l| {
// FIXME: Do we really need this `local_used_in` check?
// Isn't it checking something like... `callee(callee)`?
// If somehow this check is needed, add some test for it,
// 'cuz currently nothing changes after deleting this check.
local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
}) {
// Mutable closure is used after current expr; we cannot consume it.
snippet = format!("&mut {snippet}");
match cx.tcx.infer_ctxt().build().type_implements_fn_trait(
cx.param_env,
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
ty::PredicatePolarity::Positive,
) {
// Mutable closure is used after current expr; we cannot consume it.
Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
snippet = format!("&{snippet}");
},
_ => (),
}
}
diag.span_suggestion(
expr.span,

View file

@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
return;
}
if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
if is_whole && !sym_str.contains(['e', 'E']) {
// Normalize the literal by stripping the fractional portion
if sym_str.split('.').next().unwrap() != float_str {
// If the type suffix is missing the suggestion would be

View file

@ -2,7 +2,8 @@ use clippy_utils::consts::Constant::{Int, F32, F64};
use clippy_utils::consts::{constant, constant_simple, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{
eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg,
eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal,
peel_blocks, sugg,
};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@ -759,7 +760,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind {
let recv_ty = cx.typeck_results().expr_ty(receiver);
if recv_ty.is_floating_point() && !is_no_std_crate(cx) {
if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) {
match path.ident.name.as_str() {
"ln" => check_ln1p(cx, expr, receiver),
"log" => check_log_base(cx, expr, receiver, args),

View file

@ -424,14 +424,14 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
&& implements_trait(cx, target, display_trait_id, &[])
&& let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span.source_callsite())
{
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
if n_needed_derefs == 0 && !needs_ref {
span_lint_and_sugg(
cx,
TO_STRING_IN_FORMAT_ARGS,
to_string_span.with_lo(receiver.span.hi()),
to_string_span.with_lo(receiver.span.source_callsite().hi()),
format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
"remove this",
String::new(),

View file

@ -14,7 +14,7 @@ use clippy_utils::attrs::is_proc_macro;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_must_use_ty;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{return_ty, trait_ref_of_method};
use core::ops::ControlFlow;
@ -226,7 +226,7 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
}
fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
for_each_expr(body.value, |e| {
for_each_expr_without_closures(body.value, |e| {
use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
match e.kind {

View file

@ -5,7 +5,7 @@ use rustc_span::def_id::LocalDefId;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::type_is_unsafe_function;
use clippy_utils::visitors::for_each_expr_with_closures;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{iter_input_pats, path_to_local};
use core::ops::ControlFlow;
@ -49,7 +49,7 @@ fn check_raw_ptr<'tcx>(
if !raw_ptrs.is_empty() {
let typeck = cx.tcx.typeck_body(body.id());
let _: Option<!> = for_each_expr_with_closures(cx, body.value, |e| {
let _: Option<!> = for_each_expr(cx, body.value, |e| {
match e.kind {
hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
for arg in args {

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
use clippy_utils::visitors::for_each_expr;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{get_async_fn_body, is_async_fn};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
@ -153,7 +153,7 @@ fn lint_implicit_returns(
ExprKind::Loop(block, ..) => {
let mut add_return = false;
let _: Option<!> = for_each_expr(block, |e| {
let _: Option<!> = for_each_expr_without_closures(block, |e| {
if let ExprKind::Break(dest, sub_expr) = e.kind {
if dest.target_id.ok() == Some(expr.hir_id) {
if call_site_span.is_none() && e.span.ctxt() == ctxt {

View file

@ -3,8 +3,8 @@ use clippy_utils::source::snippet;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir::def_id::DefId;
use rustc_hir::{
GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, TyKind, AssocItemConstraint,
WherePredicate,
AssocItemConstraint, GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier,
TyKind, WherePredicate,
};
use rustc_hir_analysis::lower_ty;
use rustc_lint::{LateContext, LateLintPass};
@ -83,8 +83,8 @@ fn emit_lint(
let mut sugg = vec![(implied_span_extended, String::new())];
// We also might need to include associated item constraints that were specified in the implied bound,
// but omitted in the implied-by bound:
// We also might need to include associated item constraints that were specified in the implied
// bound, but omitted in the implied-by bound:
// `fn f() -> impl Deref<Target = u8> + DerefMut`
// If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
let omitted_constraints: Vec<_> = implied_constraints

View file

@ -2,12 +2,14 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::higher;
use clippy_utils::ty::{deref_chain, get_adt_inherent_method};
use clippy_utils::{higher, is_from_proc_macro};
use rustc_ast::ast::RangeLimits;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass;
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@ -100,11 +102,21 @@ impl IndexingSlicing {
impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) {
if (self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id))
|| is_from_proc_macro(cx, expr)
{
return;
}
if let ExprKind::Index(array, index, _) = &expr.kind {
if let ExprKind::Index(array, index, _) = &expr.kind
&& let expr_ty = cx.typeck_results().expr_ty(array)
&& let mut deref = deref_chain(cx, expr_ty)
&& deref.any(|l| {
l.peel_refs().is_slice()
|| l.peel_refs().is_array()
|| ty_has_applicable_get_function(cx, l.peel_refs(), expr_ty, expr)
})
{
let note = "the suggestion might not be applicable in constant blocks";
let ty = cx.typeck_results().expr_ty(array).peel_refs();
if let Some(range) = higher::Range::hir(index) {
@ -231,3 +243,33 @@ fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u1
(start, end)
}
/// Checks if the output Ty of the `get` method on this Ty (if any) matches the Ty returned by the
/// indexing operation (if any).
fn ty_has_applicable_get_function<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
array_ty: Ty<'tcx>,
index_expr: &Expr<'_>,
) -> bool {
if let ty::Adt(_, _) = array_ty.kind()
&& let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| {
cx.tcx
.fn_sig(m.def_id)
.skip_binder()
.output()
.skip_binder()
})
&& let ty::Adt(def, args) = get_output_ty.kind()
&& cx.tcx.is_diagnostic_item(sym::Option, def.0.did)
&& let Some(option_generic_param) = args.first()
&& let generic_ty = option_generic_param.expect_ty().peel_refs()
// FIXME: ideally this would handle type params and projections properly, for now just assume it's the same type
&& (cx.typeck_results().expr_ty(index_expr).peel_refs() == generic_ty.peel_refs()
|| matches!(generic_ty.peel_refs().kind(), ty::Param(_) | ty::Alias(_, _)))
{
true
} else {
false
}
}

View file

@ -22,7 +22,7 @@ declare_clippy_lint! {
/// ```no_run
/// let my_div = 10 >> 1;
/// ```
#[clippy::version = "1.78.0"]
#[clippy::version = "1.79.0"]
pub INTEGER_DIVISION_REMAINDER_USED,
restriction,
"use of disallowed default division and remainder operations"

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::get_parent_as_impl;
use clippy_utils::source::snippet;
use clippy_utils::ty::{implements_trait, make_normalized_projection};
use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection};
use rustc_ast::Mutability;
use rustc_errors::Applicability;
use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
@ -9,8 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_session::declare_lint_pass;
use rustc_span::{sym, Symbol};
use std::iter;
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@ -124,33 +123,6 @@ fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
.is_some_and(|did| cx.effective_visibilities.is_exported(did))
}
/// Returns the deref chain of a type, starting with the type itself.
fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx {
iter::successors(Some(ty), |&ty| {
if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
&& implements_trait(cx, ty, deref_did, &[])
{
make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty])
} else {
None
}
})
}
fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) {
cx.tcx.inherent_impls(ty_did).into_iter().flatten().any(|&did| {
cx.tcx
.associated_items(did)
.filter_by_name_unhygienic(method_name)
.next()
.is_some_and(|item| item.kind == ty::AssocKind::Fn)
})
} else {
false
}
}
impl LateLintPass<'_> for IterWithoutIntoIter {
fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
if !in_external_macro(cx.sess(), item.span)
@ -167,7 +139,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
}
&& !deref_chain(cx, ty).any(|ty| {
// We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name)
ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some()
})
&& let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
if item.ident.name == sym!(IntoIter) {

View file

@ -28,7 +28,7 @@ declare_clippy_lint! {
/// ```rust
/// let eps = f32::EPSILON;
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.79.0"]
pub LEGACY_NUMERIC_CONSTANTS,
style,
"checks for usage of legacy std numeric constants and methods"

View file

@ -67,4 +67,12 @@
"clippy::wrong_pub_self_convention",
"set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
);
store.register_removed(
"clippy::maybe_misused_cfg",
"this lint has been replaced by `unexpected_cfgs`",
);
store.register_removed(
"clippy::mismatched_target_os",
"this lint has been replaced by `unexpected_cfgs`",
);
}

View file

@ -251,6 +251,7 @@ mod needless_else;
mod needless_for_each;
mod needless_if;
mod needless_late_init;
mod needless_maybe_sized;
mod needless_parens_on_range_literals;
mod needless_pass_by_ref_mut;
mod needless_pass_by_value;
@ -325,6 +326,7 @@ mod size_of_in_element_count;
mod size_of_ref;
mod slow_vector_initialization;
mod std_instead_of_core;
mod string_patterns;
mod strings;
mod strlen_on_c_strings;
mod suspicious_operation_groupings;
@ -1058,6 +1060,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized));
store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
@ -1165,6 +1168,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
..Default::default()
})
});
store.register_late_pass(|_| Box::new(string_patterns::StringPatterns));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -43,7 +43,7 @@ declare_clippy_lint! {
/// let x: Option<Vec<String>> = Some(Vec::new());
/// let y: Vec<String> = x.unwrap_or_default();
/// ```
#[clippy::version = "1.78.0"]
#[clippy::version = "1.79.0"]
pub MANUAL_UNWRAP_OR_DEFAULT,
suspicious,
"check if a `match` or `if let` can be simplified with `unwrap_or_default`"
@ -57,7 +57,8 @@ fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<HirId> {
// Since it comes from a pattern binding, we need to get the parent to actually match
// against it.
&& let Some(def_id) = cx.tcx.opt_parent(def_id)
&& cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id)
&& (cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id)
|| cx.tcx.lang_items().get(LangItem::ResultOk) == Some(def_id))
{
let mut bindings = Vec::new();
pat.each_binding(|_, id, _, _| bindings.push(id));
@ -80,6 +81,14 @@ fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<
&& cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id)
{
Some(arm.body)
} else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind
&& let Some(def_id) = path.res.opt_def_id()
// Since it comes from a pattern binding, we need to get the parent to actually match
// against it.
&& let Some(def_id) = cx.tcx.opt_parent(def_id)
&& cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id)
{
Some(arm.body)
} else if let PatKind::Wild = arm.pat.kind {
// We consider that the `Some` check will filter it out if it's not right.
Some(arm.body)

View file

@ -2,7 +2,7 @@ use clippy_config::msrvs::Msrv;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::matching_root_macro_call;
use clippy_utils::source::snippet;
use clippy_utils::visitors::{for_each_expr, is_local_used};
use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used};
use clippy_utils::{in_constant, path_to_local};
use rustc_ast::{BorrowKind, LitKind};
use rustc_errors::Applicability;
@ -249,7 +249,7 @@ fn emit_redundant_guards<'tcx>(
/// an error in the future, and rustc already actively warns against this (see rust#41620),
/// so we don't consider those as usable within patterns for linting purposes.
fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
for_each_expr(expr, |expr| {
for_each_expr_without_closures(expr, |expr| {
if match expr.kind {
ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat,
ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => {

View file

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::sugg::{make_unop, Sugg};
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr};
use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures};
use clippy_utils::{higher, is_expn_of, is_trait_method};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -283,7 +283,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
// to see that there aren't any let chains anywhere in the guard, as that would break
// if we suggest `t.is_none() && (let X = y && z)` for:
// `match t { None if let X = y && z => true, _ => false }`
let has_nested_let_chain = for_each_expr(guard, |expr| {
let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| {
if matches!(expr.kind, ExprKind::Let(..)) {
ControlFlow::Break(())
} else {

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{eq_expr_value, get_parent_expr};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
@ -46,7 +46,7 @@ fn collect_replace_calls<'tcx>(
let mut methods = VecDeque::new();
let mut from_args = VecDeque::new();
let _: Option<()> = for_each_expr(expr, |e| {
let _: Option<()> = for_each_expr_without_closures(expr, |e| {
if let Some(("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);

View file

@ -66,6 +66,7 @@ mod map_flatten;
mod map_identity;
mod map_unwrap_or;
mod mut_mutex_lock;
mod needless_character_iteration;
mod needless_collect;
mod needless_option_as_deref;
mod needless_option_take;
@ -93,7 +94,6 @@ mod seek_from_current;
mod seek_to_start_instead_of_rewind;
mod single_char_add_str;
mod single_char_insert_string;
mod single_char_pattern;
mod single_char_push_string;
mod skip_while_next;
mod stable_sort_primitive;
@ -1140,38 +1140,6 @@ declare_clippy_lint! {
"not returning type containing `Self` in a `new` method"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for string methods that receive a single-character
/// `str` as an argument, e.g., `_.split("x")`.
///
/// ### Why is this bad?
/// While this can make a perf difference on some systems,
/// benchmarks have proven inconclusive. But at least using a
/// char literal makes it clear that we are looking at a single
/// character.
///
/// ### Known problems
/// Does not catch multi-byte unicode characters. This is by
/// design, on many machines, splitting by a non-ascii char is
/// actually slower. Please do your own measurements instead of
/// relying solely on the results of this lint.
///
/// ### Example
/// ```rust,ignore
/// _.split("x");
/// ```
///
/// Use instead:
/// ```rust,ignore
/// _.split('x');
/// ```
#[clippy::version = "pre 1.29.0"]
pub SINGLE_CHAR_PATTERN,
pedantic,
"using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calling `.step_by(0)` on iterators which panics.
@ -4084,12 +4052,33 @@ declare_clippy_lint! {
/// ```no_run
/// println!("the string is empty");
/// ```
#[clippy::version = "1.78.0"]
#[clippy::version = "1.79.0"]
pub CONST_IS_EMPTY,
suspicious,
"is_empty() called on strings known at compile time"
}
declare_clippy_lint! {
/// ### What it does
/// Checks if an iterator is used to check if a string is ascii.
///
/// ### Why is this bad?
/// The `str` type already implements the `is_ascii` method.
///
/// ### Example
/// ```no_run
/// "foo".chars().all(|c| c.is_ascii());
/// ```
/// Use instead:
/// ```no_run
/// "foo".is_ascii();
/// ```
#[clippy::version = "1.80.0"]
pub NEEDLESS_CHARACTER_ITERATION,
suspicious,
"is_ascii() called on a char iterator"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@ -4147,7 +4136,6 @@ impl_lint_pass!(Methods => [
FLAT_MAP_OPTION,
INEFFICIENT_TO_STRING,
NEW_RET_NO_SELF,
SINGLE_CHAR_PATTERN,
SINGLE_CHAR_ADD_STR,
SEARCH_IS_SOME,
FILTER_NEXT,
@ -4255,6 +4243,7 @@ impl_lint_pass!(Methods => [
UNNECESSARY_RESULT_MAP_OR_ELSE,
MANUAL_C_STR_LITERALS,
UNNECESSARY_GET_THEN_CHECK,
NEEDLESS_CHARACTER_ITERATION,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -4301,7 +4290,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
single_char_add_str::check(cx, expr, receiver, args);
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv);
},
ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
@ -4462,6 +4450,7 @@ impl Methods {
},
("all", [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
needless_character_iteration::check(cx, expr, recv, arg, true);
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
iter_overeager_cloned::check(
cx,
@ -4482,6 +4471,7 @@ impl Methods {
},
("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(
cx,

View file

@ -0,0 +1,124 @@
use rustc_errors::Applicability;
use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::Span;
use super::utils::get_last_chain_binding_hir_id;
use super::NEEDLESS_CHARACTER_ITERATION;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
while let ExprKind::AddrOf(_, _, e) = expr.kind {
expr = e;
}
expr
}
fn handle_expr(
cx: &LateContext<'_>,
expr: &Expr<'_>,
first_param: HirId,
span: Span,
before_chars: Span,
revert: bool,
is_all: bool,
) {
match expr.kind {
ExprKind::MethodCall(method, receiver, [], _) => {
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
// `is_ascii`, then only `.all()` should warn.
if revert != is_all
&& method.ident.name.as_str() == "is_ascii"
&& path_to_local_id(receiver, first_param)
&& let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
&& *char_arg_ty.kind() == ty::Char
&& let Some(snippet) = snippet_opt(cx, before_chars)
{
span_lint_and_sugg(
cx,
NEEDLESS_CHARACTER_ITERATION,
span,
"checking if a string is ascii using iterators",
"try",
format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }),
Applicability::MachineApplicable,
);
}
},
ExprKind::Block(block, _) => {
if block.stmts.iter().any(|stmt| !matches!(stmt.kind, StmtKind::Let(_))) {
// If there is something else than let bindings, then better not emit the lint.
return;
}
if let Some(block_expr) = block.expr
// First we ensure that this is a "binding chain" (each statement is a binding
// of the previous one) and that it is a binding of the closure argument.
&& let Some(last_chain_binding_id) =
get_last_chain_binding_hir_id(first_param, block.stmts)
{
handle_expr(
cx,
block_expr,
last_chain_binding_id,
span,
before_chars,
revert,
is_all,
);
}
},
ExprKind::Unary(UnOp::Not, expr) => handle_expr(cx, expr, first_param, span, before_chars, !revert, is_all),
ExprKind::Call(fn_path, [arg]) => {
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
// `is_ascii`, then only `.all()` should warn.
if revert != is_all
&& let ExprKind::Path(path) = fn_path.kind
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
&& match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
&& path_to_local_id(peels_expr_ref(arg), first_param)
&& let Some(snippet) = snippet_opt(cx, before_chars)
{
span_lint_and_sugg(
cx,
NEEDLESS_CHARACTER_ITERATION,
span,
"checking if a string is ascii using iterators",
"try",
format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }),
Applicability::MachineApplicable,
);
}
},
_ => {},
}
}
pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>, is_all: bool) {
if let ExprKind::Closure(&Closure { body, .. }) = closure_arg.kind
&& let body = cx.tcx.hir().body(body)
&& let Some(first_param) = body.params.first()
&& let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind
&& method.ident.name.as_str() == "chars"
&& let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs()
&& *str_ty.kind() == ty::Str
{
let expr_start = recv.span;
while let ExprKind::MethodCall(_, new_recv, _, _) = recv.kind {
recv = new_recv;
}
let body_expr = peel_blocks(body.value);
handle_expr(
cx,
body_expr,
first_param.pat.hir_id,
recv.span.with_hi(call_expr.span.hi()),
recv.span.with_hi(expr_start.hi()),
false,
is_all,
);
}
}

View file

@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(
let map = cx.tcx.hir();
let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id));
reference_visitor.visit_body(&body);
reference_visitor.visit_body(body);
if reference_visitor.found_reference {
return;

View file

@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
lit.span,
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
"try",
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
format!("\"{}\"", pushed_path_lit.trim_start_matches(['/', '\\'])),
Applicability::MachineApplicable,
);
}

View file

@ -2,7 +2,7 @@ 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::{get_parent_expr, is_trait_method, strip_pat_refs};
use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs};
use hir::ExprKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
@ -156,13 +156,3 @@ pub(super) fn check<'tcx>(
}
}
}
fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
if let Some(parent_expr) = get_parent_expr(cx, expr)
&& let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
&& receiver.hir_id == expr.hir_id
{
return true;
}
false
}

View file

@ -1,8 +1,8 @@
use super::utils::get_hint_if_single_char_arg;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
use rustc_ast::BorrowKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{self as hir, ExprKind};
use rustc_lint::LateContext;
use super::SINGLE_CHAR_ADD_STR;
@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
/// lint for length-1 `str`s as argument for `insert_str`
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability, false) {
if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) {
let base_string_snippet =
snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
@ -25,4 +25,43 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
applicability,
);
}
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind
&& let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
&& path_segment.ident.name == rustc_span::sym::to_string
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
{
let base_string_snippet =
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
let extension_string =
snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})");
span_lint_and_sugg(
cx,
SINGLE_CHAR_ADD_STR,
expr.span,
"calling `insert_str()` using a single-character converted to string",
"consider using `insert` without `to_string()`",
sugg,
applicability,
);
}
}
fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
if cx.typeck_results().expr_ty(expr).is_ref()
&& let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
&& ty.is_char()
{
return true;
}
false
}
fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
cx.typeck_results().expr_ty(expr).is_char()
}

View file

@ -1,64 +0,0 @@
use super::utils::get_hint_if_single_char_arg;
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::symbol::Symbol;
use super::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),
];
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
pub(super) fn check(
cx: &LateContext<'_>,
_expr: &hir::Expr<'_>,
method_name: Symbol,
receiver: &hir::Expr<'_>,
args: &[hir::Expr<'_>],
) {
for &(method, pos) in &PATTERN_METHODS {
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind()
&& ty.is_str()
&& method_name.as_str() == method
&& args.len() > pos
&& let arg = &args[pos]
&& let mut applicability = Applicability::MachineApplicable
&& let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true)
{
span_lint_and_sugg(
cx,
SINGLE_CHAR_PATTERN,
arg.span,
"single-character string constant used as pattern",
"consider using a `char`",
hint,
applicability,
);
}
}
}

View file

@ -1,8 +1,8 @@
use super::utils::get_hint_if_single_char_arg;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
use rustc_ast::BorrowKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{self as hir, ExprKind};
use rustc_lint::LateContext;
use super::SINGLE_CHAR_ADD_STR;
@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
/// lint for length-1 `str`s as argument for `push_str`
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) {
if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) {
let base_string_snippet =
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
let sugg = format!("{base_string_snippet}.push({extension_string})");
@ -24,4 +24,42 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
applicability,
);
}
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind
&& let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
&& path_segment.ident.name == rustc_span::sym::to_string
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
{
let base_string_snippet =
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
let extension_string =
snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})");
span_lint_and_sugg(
cx,
SINGLE_CHAR_ADD_STR,
expr.span,
"calling `push_str()` using a single-character converted to string",
"consider using `push` without `to_string()`",
sugg,
applicability,
);
}
}
fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
if cx.typeck_results().expr_ty(expr).is_ref()
&& let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
&& ty.is_char()
{
return true;
}
false
}
fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
cx.typeck_results().expr_ty(expr).is_char()
}

View file

@ -3,7 +3,7 @@ use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_context;
use clippy_utils::usage::local_used_after_expr;
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
use clippy_utils::visitors::{for_each_expr, Descend};
use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
@ -209,7 +209,7 @@ fn indirect_usage<'tcx>(
}) = stmt.kind
{
let mut path_to_binding = None;
let _: Option<!> = for_each_expr_with_closures(cx, init_expr, |e| {
let _: Option<!> = for_each_expr(cx, init_expr, |e| {
if path_to_local_id(e, binding) {
path_to_binding = Some(e);
}

View file

@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::is_copy;
use clippy_utils::usage::mutated_variables;
use clippy_utils::visitors::{for_each_expr, Descend};
use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
use core::ops::ControlFlow;
use rustc_hir as hir;
@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
let _: Option<!> = for_each_expr(body.value, |e| {
let _: Option<!> = for_each_expr_without_closures(body.value, |e| {
if let hir::ExprKind::Ret(Some(e)) = &e.kind {
let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e);
found_mapping |= found_mapping_res;

View file

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::ForLoop;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
use clippy_utils::visitors::for_each_expr;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
@ -61,7 +61,7 @@ pub fn check_for_loop_iter(
fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
let mut change = false;
if let ExprKind::Block(block, ..) = body.kind {
for_each_expr(block, |e| {
for_each_expr_without_closures(block, |e| {
match e.kind {
ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
change |= !can_mut_borrow_both(cx, caller, assignee);

View file

@ -4,10 +4,11 @@ use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath, Stmt, StmtKind};
use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use super::utils::get_last_chain_binding_hir_id;
use super::UNNECESSARY_RESULT_MAP_OR_ELSE;
fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) {
@ -25,22 +26,6 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &E
);
}
fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
for stmt in statements {
if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init
&& let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
&& let hir::def::Res::Local(local_hir_id) = path.res
&& local_hir_id == hir_id
{
hir_id = local.pat.hir_id;
} else {
return None;
}
}
Some(hir_id)
}
fn handle_qpath(
cx: &LateContext<'_>,
expr: &Expr<'_>,

View file

@ -1,10 +1,7 @@
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{get_parent_expr, path_to_local_id, usage};
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat};
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty};
@ -49,48 +46,6 @@ pub(super) fn derefs_to_slice<'tcx>(
}
}
pub(super) fn get_hint_if_single_char_arg(
cx: &LateContext<'_>,
arg: &Expr<'_>,
applicability: &mut Applicability,
ascii_only: bool,
) -> Option<String> {
if let ExprKind::Lit(lit) = &arg.kind
&& let ast::LitKind::Str(r, style) = lit.node
&& let string = r.as_str()
&& let len = if ascii_only {
string.len()
} else {
string.chars().count()
}
&& len == 1
{
let snip = snippet_with_applicability(cx, arg.span, string, applicability);
let ch = if let ast::StrStyle::Raw(nhash) = style {
let nhash = nhash as usize;
// for raw string: r##"a"##
&snip[(nhash + 2)..(snip.len() - 1 - nhash)]
} else {
// for regular string: "a"
&snip[1..(snip.len() - 1)]
};
let hint = format!(
"'{}'",
match ch {
"'" => "\\'",
r"\" => "\\\\",
"\\\"" => "\"", // no need to escape `"` in `'"'`
_ => ch,
}
);
Some(hint)
} else {
None
}
}
/// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a
/// use of `CloneOrCopyVisitor`.
pub(super) fn clone_or_copy_needed<'tcx>(
@ -175,3 +130,19 @@ impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
.any(|hir_id| path_to_local_id(expr, *hir_id))
}
}
pub(super) fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
for stmt in statements {
if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init
&& let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
&& let rustc_hir::def::Res::Local(local_hir_id) = path.res
&& local_hir_id == hir_id
{
hir_id = local.pat.hir_id;
} else {
return None;
}
}
Some(hir_id)
}

View file

@ -6,7 +6,7 @@ use rustc_span::Span;
use super::ZERO_PREFIXED_LITERAL;
pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) {
let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0');
let trimmed_lit_snip = lit_snip.trim_start_matches(['_', '0']);
span_lint_and_then(
cx,
ZERO_PREFIXED_LITERAL,
@ -20,7 +20,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) {
Applicability::MaybeIncorrect,
);
// do not advise to use octal form if the literal cannot be expressed in base 8.
if !lit_snip.contains(|c| c == '8' || c == '9') {
if !lit_snip.contains(['8', '9']) {
diag.span_suggestion(
lit_span,
"if you mean to use an octal constant, use `0o`",

View file

@ -4,7 +4,7 @@ use std::ops::ControlFlow;
use clippy_utils::comparisons::{normalize_comparison, Rel};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{eq_expr_value, hash_expr, higher};
use rustc_ast::{LitKind, RangeLimits};
use rustc_data_structures::packed::Pu128;
@ -405,7 +405,7 @@ impl LateLintPass<'_> for MissingAssertsForIndexing {
fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
let mut map = UnhashMap::default();
for_each_expr(body.value, |expr| {
for_each_expr_without_closures(body.value, |expr| {
check_index(cx, expr, &mut map);
check_assert(cx, expr, &mut map);
ControlFlow::<!, ()>::Continue(())

View file

@ -1,7 +1,6 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::ty::has_drop;
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
use rustc_hir as hir;
use rustc_hir::def_id::CRATE_DEF_ID;
@ -121,10 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
}
},
FnKind::Method(_, sig, ..) => {
if trait_ref_of_method(cx, def_id).is_some()
|| already_const(sig.header)
|| method_accepts_droppable(cx, def_id)
{
if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) {
return;
}
},
@ -151,26 +147,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
let mir = cx.tcx.optimized_mir(def_id);
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
cx.tcx.dcx().span_err(span, err);
}
} else {
if let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
}
}
extract_msrv_attr!(LateContext);
}
/// Returns true if any of the method parameters is a type that implements `Drop`. The method
/// can't be made const then, because `drop` can't be const-evaluated.
fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
// If any of the params are droppable, return true
sig.inputs().iter().any(|&ty| has_drop(cx, ty))
}
// We don't have to lint on something that's already `const`
#[must_use]
fn already_const(header: hir::FnHeader) -> bool {

View file

@ -110,7 +110,7 @@ fn should_lint<'tcx>(
// Is there a call to `DebugStruct::debug_struct`? Do lint if there is.
let mut has_debug_struct = false;
for_each_expr(block, |expr| {
for_each_expr(cx, block, |expr| {
if let ExprKind::MethodCall(path, recv, ..) = &expr.kind {
let recv_ty = typeck_results.expr_ty(recv).peel_refs();
@ -165,7 +165,7 @@ fn check_struct<'tcx>(
let mut has_direct_field_access = false;
let mut field_accesses = FxHashSet::default();
for_each_expr(block, |expr| {
for_each_expr(cx, block, |expr| {
if let ExprKind::Field(target, ident) = expr.kind
&& let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs()
&& target_ty == self_ty

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::visitors::{for_each_expr_with_closures, Descend, Visitable};
use clippy_utils::visitors::{for_each_expr, Descend, Visitable};
use core::ops::ControlFlow::Continue;
use hir::def::{DefKind, Res};
use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp};
@ -96,7 +96,7 @@ fn collect_unsafe_exprs<'tcx>(
node: impl Visitable<'tcx>,
unsafe_ops: &mut Vec<(&'static str, Span)>,
) {
for_each_expr_with_closures(cx, node, |expr| {
for_each_expr(cx, node, |expr| {
match expr.kind {
ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)),

View file

@ -6,8 +6,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{
higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt,
span_extract_comment, 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, SpanlessEq,
};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -154,7 +154,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
snip = snip.blockify();
}
if condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id) {
if (condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id))
|| is_receiver_of_method_call(cx, e)
|| is_as_argument(cx, e)
{
snip = snip.maybe_par();
}
@ -442,3 +445,7 @@ fn fetch_assign<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, bool)
None
}
}
fn is_as_argument(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
matches!(get_parent_expr(cx, e).map(|e| e.kind), Some(ExprKind::Cast(_, _)))
}

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::path_to_local;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::needs_ordered_drop;
use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used};
use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used};
use core::ops::ControlFlow;
use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::{
@ -63,7 +63,7 @@ declare_clippy_lint! {
declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
for_each_expr_with_closures(cx, stmt, |e| {
for_each_expr(cx, stmt, |e| {
if matches!(e.kind, ExprKind::Assign(..)) {
ControlFlow::Break(())
} else {
@ -74,7 +74,7 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) ->
}
fn contains_let(cond: &Expr<'_>) -> bool {
for_each_expr(cond, |e| {
for_each_expr_without_closures(cond, |e| {
if matches!(e.kind, ExprKind::Let(_)) {
ControlFlow::Break(())
} else {

View file

@ -0,0 +1,164 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_errors::Applicability;
use rustc_hir::def_id::{DefId, DefIdMap};
use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifier, WherePredicate};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{ClauseKind, PredicatePolarity};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::Ident;
declare_clippy_lint! {
/// ### What it does
/// Lints `?Sized` bounds applied to type parameters that cannot be unsized
///
/// ### Why is this bad?
/// The `?Sized` bound is misleading because it cannot be satisfied by an
/// unsized type
///
/// ### Example
/// ```rust
/// // `T` cannot be unsized because `Clone` requires it to be `Sized`
/// fn f<T: Clone + ?Sized>(t: &T) {}
/// ```
/// Use instead:
/// ```rust
/// fn f<T: Clone>(t: &T) {}
///
/// // or choose alternative bounds for `T` so that it can be unsized
/// ```
#[clippy::version = "1.79.0"]
pub NEEDLESS_MAYBE_SIZED,
suspicious,
"a `?Sized` bound that is unusable due to a `Sized` requirement"
}
declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]);
#[allow(clippy::struct_field_names)]
struct Bound<'tcx> {
/// The [`DefId`] of the type parameter the bound refers to
param: DefId,
ident: Ident,
trait_bound: &'tcx PolyTraitRef<'tcx>,
modifier: TraitBoundModifier,
predicate_pos: usize,
bound_pos: usize,
}
/// Finds all of the [`Bound`]s that refer to a type parameter and are not from a macro expansion
fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item = Bound<'tcx>> {
generics
.predicates
.iter()
.enumerate()
.filter_map(|(predicate_pos, predicate)| {
let WherePredicate::BoundPredicate(bound_predicate) = predicate else {
return None;
};
let (param, ident) = bound_predicate.bounded_ty.as_generic_param()?;
Some(
bound_predicate
.bounds
.iter()
.enumerate()
.filter_map(move |(bound_pos, bound)| match bound {
&GenericBound::Trait(ref trait_bound, modifier) => Some(Bound {
param,
ident,
trait_bound,
modifier,
predicate_pos,
bound_pos,
}),
GenericBound::Outlives(_) => None,
})
.filter(|bound| !bound.trait_bound.span.from_expansion()),
)
})
.flatten()
}
/// Searches the supertraits of the trait referred to by `trait_bound` recursively, returning the
/// path taken to find a `Sized` bound if one is found
fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) -> Option<Vec<DefId>> {
fn search(cx: &LateContext<'_>, path: &mut Vec<DefId>) -> bool {
let trait_def_id = *path.last().unwrap();
if Some(trait_def_id) == cx.tcx.lang_items().sized_trait() {
return true;
}
for &(predicate, _) in cx.tcx.super_predicates_of(trait_def_id).predicates {
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
&& trait_predicate.polarity == PredicatePolarity::Positive
&& !path.contains(&trait_predicate.def_id())
{
path.push(trait_predicate.def_id());
if search(cx, path) {
return true;
}
path.pop();
}
}
false
}
let mut path = vec![trait_bound.trait_ref.trait_def_id()?];
search(cx, &mut path).then_some(path)
}
impl LateLintPass<'_> for NeedlessMaybeSized {
fn check_generics(&mut self, cx: &LateContext<'_>, generics: &Generics<'_>) {
let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else {
return;
};
let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics)
.filter(|bound| {
bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait)
&& bound.modifier == TraitBoundModifier::Maybe
})
.map(|bound| (bound.param, bound))
.collect();
for bound in type_param_bounds(generics) {
if bound.modifier == TraitBoundModifier::None
&& let Some(sized_bound) = maybe_sized_params.get(&bound.param)
&& let Some(path) = path_to_sized_bound(cx, bound.trait_bound)
{
span_lint_and_then(
cx,
NEEDLESS_MAYBE_SIZED,
sized_bound.trait_bound.span,
"`?Sized` bound is ignored because of a `Sized` requirement",
|diag| {
let ty_param = sized_bound.ident;
diag.span_note(
bound.trait_bound.span,
format!("`{ty_param}` cannot be unsized because of the bound"),
);
for &[current_id, next_id] in path.array_windows() {
let current = cx.tcx.item_name(current_id);
let next = cx.tcx.item_name(next_id);
diag.note(format!("...because `{current}` has the bound `{next}`"));
}
diag.span_suggestion_verbose(
generics.span_for_bound_removal(sized_bound.predicate_pos, sized_bound.bound_pos),
"change the bounds that require `Sized`, or remove the `?Sized` bound",
"",
Applicability::MaybeIncorrect,
);
},
);
return;
}
}
}
}

View file

@ -1,7 +1,7 @@
use super::needless_pass_by_value::requires_exact_signature;
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet;
use clippy_utils::visitors::for_each_expr_with_closures;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::Applicability;
@ -205,7 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
// We retrieve all the closures declared in the function because they will not be found
// by `euv::Delegate`.
let mut closures: FxHashSet<LocalDefId> = FxHashSet::default();
for_each_expr_with_closures(cx, body, |expr| {
for_each_expr(cx, body, |expr| {
if let ExprKind::Closure(closure) = expr.kind {
closures.insert(closure.def_id);
}

View file

@ -1,10 +1,11 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::ty::implements_trait;
use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, std_or_core};
use clippy_utils::{is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::EarlyBinder;
use rustc_session::declare_lint_pass;
use rustc_span::sym;
@ -111,7 +112,7 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL
impl LateLintPass<'_> for NonCanonicalImpls {
#[expect(clippy::too_many_lines)]
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) {
let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else {
return;
};
@ -128,6 +129,9 @@ impl LateLintPass<'_> for NonCanonicalImpls {
let ExprKind::Block(block, ..) = body.value.kind else {
return;
};
if in_external_macro(cx.sess(), block.span) || is_from_proc_macro(cx, impl_item) {
return;
}
if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
&& let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)

View file

@ -7,7 +7,7 @@ use std::ptr;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::in_constant;
use clippy_utils::macros::macro_backtrace;
use clippy_utils::ty::InteriorMut;
use clippy_utils::ty::{implements_trait, InteriorMut};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{
@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::impl_lint_pass;
use rustc_span::{sym, InnerSpan, Span, DUMMY_SP};
use rustc_span::{sym, Span, DUMMY_SP};
use rustc_target::abi::VariantIdx;
// FIXME: this is a correctness problem but there's no suitable
@ -127,19 +127,19 @@ declare_clippy_lint! {
}
#[derive(Copy, Clone)]
enum Source {
Item { item: Span },
enum Source<'tcx> {
Item { item: Span, ty: Ty<'tcx> },
Assoc { item: Span },
Expr { expr: Span },
}
impl Source {
impl Source<'_> {
#[must_use]
fn lint(&self) -> (&'static Lint, &'static str, Span) {
match self {
Self::Item { item } | Self::Assoc { item, .. } => (
Self::Item { item, .. } | Self::Assoc { item, .. } => (
DECLARE_INTERIOR_MUTABLE_CONST,
"a `const` item should never be interior mutable",
"a `const` item should not be interior mutable",
*item,
),
Self::Expr { expr } => (
@ -151,16 +151,24 @@ impl Source {
}
}
fn lint(cx: &LateContext<'_>, source: Source) {
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 { .. } => {
let const_kw_span = span.from_inner(InnerSpan::new(0, 5));
diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
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 { .. } => {
@ -311,7 +319,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
&& 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 });
lint(cx, Source::Item { item: it.span, ty });
}
}
}

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
@ -62,7 +62,7 @@ pub(super) fn check<'tcx>(
};
let mut found = false;
let found_multiple = for_each_expr(e, |e| {
let found_multiple = for_each_expr_without_closures(e, |e| {
if eq_expr_value(cx, assignee, e) {
if found {
return ControlFlow::Break(());

Some files were not shown because too many files have changed in this diff Show more