Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
384b53eee2
91 changed files with 2049 additions and 691 deletions
|
|
@ -42,7 +42,7 @@ walkdir = "2.3"
|
|||
filetime = "0.2.9"
|
||||
itertools = "0.12"
|
||||
pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] }
|
||||
askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] }
|
||||
askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] }
|
||||
|
||||
# UI test dependencies
|
||||
if_chain = "1.0"
|
||||
|
|
@ -70,3 +70,10 @@ harness = false
|
|||
[[test]]
|
||||
name = "dogfood"
|
||||
harness = false
|
||||
|
||||
# quine-mc_cluskey makes up a significant part of the runtime in dogfood
|
||||
# due to the number of conditions in the clippy_lints crate
|
||||
# and enabling optimizations for that specific dependency helps a bit
|
||||
# without increasing total build times.
|
||||
[profile.dev.package.quine-mc_cluskey]
|
||||
opt-level = 3
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ providing the `LateContext` (`cx`), our expression at hand, and
|
|||
the symbol of the trait in question:
|
||||
|
||||
```rust
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_span::symbol::sym;
|
||||
|
|
@ -25,7 +25,7 @@ use rustc_span::symbol::sym;
|
|||
impl LateLintPass<'_> for CheckIteratorTraitLint {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[])
|
||||
});
|
||||
if implements_iterator {
|
||||
// [...]
|
||||
|
|
|
|||
|
|
@ -1026,7 +1026,7 @@ The order of associated items in traits.
|
|||
The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
|
||||
reference.
|
||||
|
||||
**Default Value:** `target_pointer_width * 2`
|
||||
**Default Value:** `target_pointer_width`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
|
|
|
|||
|
|
@ -828,7 +828,7 @@ define_Conf! {
|
|||
trait_assoc_item_kinds_order: SourceItemOrderingTraitAssocItemKinds = DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER.into(),
|
||||
/// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
|
||||
/// reference.
|
||||
#[default_text = "target_pointer_width * 2"]
|
||||
#[default_text = "target_pointer_width"]
|
||||
#[lints(trivially_copy_pass_by_ref)]
|
||||
trivial_copy_size_limit: Option<u64> = None,
|
||||
/// The maximum complexity a type can have
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::utils::{
|
||||
ClippyInfo, ErrAction, FileUpdater, UpdateMode, UpdateStatus, panic_action, run_with_args_split, run_with_output,
|
||||
ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, split_args_for_threads,
|
||||
walk_dir_no_dot_or_target,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use rustc_lexer::{TokenKind, tokenize};
|
||||
|
|
@ -9,7 +10,6 @@ use std::io::{self, Read};
|
|||
use std::ops::ControlFlow;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{self, Command, Stdio};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub enum Error {
|
||||
Io(io::Error),
|
||||
|
|
@ -260,7 +260,7 @@ fn fmt_syms(update_mode: UpdateMode) {
|
|||
);
|
||||
}
|
||||
|
||||
fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) {
|
||||
fn run_rustfmt(update_mode: UpdateMode) {
|
||||
let mut rustfmt_path = String::from_utf8(run_with_output(
|
||||
"rustup which rustfmt",
|
||||
Command::new("rustup").args(["which", "rustfmt"]),
|
||||
|
|
@ -268,42 +268,19 @@ fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) {
|
|||
.expect("invalid rustfmt path");
|
||||
rustfmt_path.truncate(rustfmt_path.trim_end().len());
|
||||
|
||||
let mut cargo_path = String::from_utf8(run_with_output(
|
||||
"rustup which cargo",
|
||||
Command::new("rustup").args(["which", "cargo"]),
|
||||
))
|
||||
.expect("invalid cargo path");
|
||||
cargo_path.truncate(cargo_path.trim_end().len());
|
||||
let args: Vec<_> = walk_dir_no_dot_or_target()
|
||||
.filter_map(|e| {
|
||||
let e = expect_action(e, ErrAction::Read, ".");
|
||||
e.path()
|
||||
.as_os_str()
|
||||
.as_encoded_bytes()
|
||||
.ends_with(b".rs")
|
||||
.then(|| e.into_path().into_os_string())
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Start all format jobs first before waiting on the results.
|
||||
let mut children = Vec::with_capacity(16);
|
||||
for &path in &[
|
||||
".",
|
||||
"clippy_config",
|
||||
"clippy_dev",
|
||||
"clippy_lints",
|
||||
"clippy_lints_internal",
|
||||
"clippy_utils",
|
||||
"rustc_tools_util",
|
||||
"lintcheck",
|
||||
] {
|
||||
let mut cmd = Command::new(&cargo_path);
|
||||
cmd.current_dir(clippy.path.join(path))
|
||||
.args(["fmt"])
|
||||
.env("RUSTFMT", &rustfmt_path)
|
||||
.stdout(Stdio::null())
|
||||
.stdin(Stdio::null())
|
||||
.stderr(Stdio::piped());
|
||||
if update_mode.is_check() {
|
||||
cmd.arg("--check");
|
||||
}
|
||||
match cmd.spawn() {
|
||||
Ok(x) => children.push(("cargo fmt", x)),
|
||||
Err(ref e) => panic_action(&e, ErrAction::Run, "cargo fmt".as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
run_with_args_split(
|
||||
let mut children: Vec<_> = split_args_for_threads(
|
||||
32,
|
||||
|| {
|
||||
let mut cmd = Command::new(&rustfmt_path);
|
||||
if update_mode.is_check() {
|
||||
|
|
@ -312,66 +289,44 @@ fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) {
|
|||
cmd.stdout(Stdio::null())
|
||||
.stdin(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
.args(["--config", "show_parse_errors=false"]);
|
||||
.args(["--unstable-features", "--skip-children"]);
|
||||
cmd
|
||||
},
|
||||
|cmd| match cmd.spawn() {
|
||||
Ok(x) => children.push(("rustfmt", x)),
|
||||
Err(ref e) => panic_action(&e, ErrAction::Run, "rustfmt".as_ref()),
|
||||
},
|
||||
WalkDir::new("tests")
|
||||
.into_iter()
|
||||
.filter_entry(|p| p.path().file_name().is_none_or(|x| x != "skip_rustfmt"))
|
||||
.filter_map(|e| {
|
||||
let e = e.expect("error reading `tests`");
|
||||
e.path()
|
||||
.as_os_str()
|
||||
.as_encoded_bytes()
|
||||
.ends_with(b".rs")
|
||||
.then(|| e.into_path().into_os_string())
|
||||
}),
|
||||
);
|
||||
args.iter(),
|
||||
)
|
||||
.map(|mut cmd| expect_action(cmd.spawn(), ErrAction::Run, "rustfmt"))
|
||||
.collect();
|
||||
|
||||
for (name, child) in &mut children {
|
||||
match child.wait() {
|
||||
Ok(status) => match (update_mode, status.exit_ok()) {
|
||||
(UpdateMode::Check | UpdateMode::Change, Ok(())) => {},
|
||||
(UpdateMode::Check, Err(_)) => {
|
||||
let mut s = String::new();
|
||||
if let Some(mut stderr) = child.stderr.take()
|
||||
&& stderr.read_to_string(&mut s).is_ok()
|
||||
{
|
||||
eprintln!("{s}");
|
||||
}
|
||||
eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update.");
|
||||
process::exit(1);
|
||||
},
|
||||
(UpdateMode::Change, Err(e)) => {
|
||||
let mut s = String::new();
|
||||
if let Some(mut stderr) = child.stderr.take()
|
||||
&& stderr.read_to_string(&mut s).is_ok()
|
||||
{
|
||||
eprintln!("{s}");
|
||||
}
|
||||
panic_action(&e, ErrAction::Run, name.as_ref());
|
||||
},
|
||||
for child in &mut children {
|
||||
let status = expect_action(child.wait(), ErrAction::Run, "rustfmt");
|
||||
match (update_mode, status.exit_ok()) {
|
||||
(UpdateMode::Check | UpdateMode::Change, Ok(())) => {},
|
||||
(UpdateMode::Check, Err(_)) => {
|
||||
let mut s = String::new();
|
||||
if let Some(mut stderr) = child.stderr.take()
|
||||
&& stderr.read_to_string(&mut s).is_ok()
|
||||
{
|
||||
eprintln!("{s}");
|
||||
}
|
||||
eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update.");
|
||||
process::exit(1);
|
||||
},
|
||||
(UpdateMode::Change, e) => {
|
||||
let mut s = String::new();
|
||||
if let Some(mut stderr) = child.stderr.take()
|
||||
&& stderr.read_to_string(&mut s).is_ok()
|
||||
{
|
||||
eprintln!("{s}");
|
||||
}
|
||||
expect_action(e, ErrAction::Run, "rustfmt");
|
||||
},
|
||||
Err(ref e) => panic_action(e, ErrAction::Run, name.as_ref()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the "main" function of cargo dev fmt
|
||||
pub fn run(clippy: &ClippyInfo, update_mode: UpdateMode) {
|
||||
if clippy.has_intellij_hook {
|
||||
eprintln!(
|
||||
"error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\
|
||||
Not formatting because that would format the local repo as well!\n\
|
||||
Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`."
|
||||
);
|
||||
return;
|
||||
}
|
||||
run_rustfmt(clippy, update_mode);
|
||||
pub fn run(update_mode: UpdateMode) {
|
||||
run_rustfmt(update_mode);
|
||||
fmt_syms(update_mode);
|
||||
if let Err(e) = fmt_conf(update_mode.is_check()) {
|
||||
e.display();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ fn main() {
|
|||
allow_staged,
|
||||
allow_no_vcs,
|
||||
} => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs),
|
||||
DevCommand::Fmt { check } => fmt::run(&clippy, utils::UpdateMode::from_check(check)),
|
||||
DevCommand::Fmt { check } => fmt::run(utils::UpdateMode::from_check(check)),
|
||||
DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)),
|
||||
DevCommand::NewLint {
|
||||
pass,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints};
|
||||
use crate::utils::{
|
||||
FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists,
|
||||
try_rename_dir, try_rename_file,
|
||||
ErrAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists,
|
||||
delete_file_if_exists, expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target,
|
||||
};
|
||||
use rustc_lexer::TokenKind;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
/// Runs the `rename_lint` command.
|
||||
///
|
||||
|
|
@ -133,17 +132,10 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b
|
|||
}
|
||||
|
||||
let mut update_fn = file_update_fn(old_name, new_name, mod_edit);
|
||||
for file in WalkDir::new(".").into_iter().filter_entry(|e| {
|
||||
// Skip traversing some of the larger directories.
|
||||
e.path()
|
||||
.as_os_str()
|
||||
.as_encoded_bytes()
|
||||
.get(2..)
|
||||
.is_none_or(|x| x != "target".as_bytes() && x != ".git".as_bytes())
|
||||
}) {
|
||||
let file = file.expect("error reading clippy directory");
|
||||
if file.path().as_os_str().as_encoded_bytes().ends_with(b".rs") {
|
||||
updater.update_file(file.path(), &mut update_fn);
|
||||
for e in walk_dir_no_dot_or_target() {
|
||||
let e = expect_action(e, ErrAction::Read, ".");
|
||||
if e.path().as_os_str().as_encoded_bytes().ends_with(b".rs") {
|
||||
updater.update_file(e.path(), &mut update_fn);
|
||||
}
|
||||
}
|
||||
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::utils::{
|
||||
ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_action, update_text_region_fn,
|
||||
ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, expect_action, update_text_region_fn,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
|
|
@ -201,10 +201,7 @@ pub fn find_lint_decls() -> Vec<Lint> {
|
|||
/// Reads the source files from the given root directory
|
||||
fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirEntry, String)> {
|
||||
WalkDir::new(src_root).into_iter().filter_map(move |e| {
|
||||
let e = match e {
|
||||
Ok(e) => e,
|
||||
Err(ref e) => panic_action(e, ErrAction::Read, src_root),
|
||||
};
|
||||
let e = expect_action(e, ErrAction::Read, src_root);
|
||||
let path = e.path().as_os_str().as_encoded_bytes();
|
||||
if let Some(path) = path.strip_suffix(b".rs")
|
||||
&& let Some(path) = path.get("clippy_lints/src/".len()..)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
use core::fmt::{self, Display};
|
||||
use core::num::NonZero;
|
||||
use core::ops::Range;
|
||||
use core::slice;
|
||||
use core::str::FromStr;
|
||||
use rustc_lexer::{self as lexer, FrontmatterAllowed};
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{self, Read as _, Seek as _, SeekFrom, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{self, Command, ExitStatus, Stdio};
|
||||
use std::{env, thread};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
|
||||
|
|
@ -45,6 +47,14 @@ pub fn panic_action(err: &impl Display, action: ErrAction, path: &Path) -> ! {
|
|||
panic!("error {} `{}`: {}", action.as_str(), path.display(), *err)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn expect_action<T>(res: Result<T, impl Display>, action: ErrAction, path: impl AsRef<Path>) -> T {
|
||||
match res {
|
||||
Ok(x) => x,
|
||||
Err(ref e) => panic_action(e, action, path.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around `std::fs::File` which panics with a path on failure.
|
||||
pub struct File<'a> {
|
||||
pub inner: fs::File,
|
||||
|
|
@ -55,9 +65,9 @@ impl<'a> File<'a> {
|
|||
#[track_caller]
|
||||
pub fn open(path: &'a (impl AsRef<Path> + ?Sized), options: &mut OpenOptions) -> Self {
|
||||
let path = path.as_ref();
|
||||
match options.open(path) {
|
||||
Ok(inner) => Self { inner, path },
|
||||
Err(e) => panic_action(&e, ErrAction::Open, path),
|
||||
Self {
|
||||
inner: expect_action(options.open(path), ErrAction::Open, path),
|
||||
path,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,10 +94,7 @@ impl<'a> File<'a> {
|
|||
/// Read the entire contents of a file to the given buffer.
|
||||
#[track_caller]
|
||||
pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String {
|
||||
match self.inner.read_to_string(dst) {
|
||||
Ok(_) => {},
|
||||
Err(e) => panic_action(&e, ErrAction::Read, self.path),
|
||||
}
|
||||
expect_action(self.inner.read_to_string(dst), ErrAction::Read, self.path);
|
||||
dst
|
||||
}
|
||||
|
||||
|
|
@ -107,9 +114,7 @@ impl<'a> File<'a> {
|
|||
},
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
if let Err(e) = res {
|
||||
panic_action(&e, ErrAction::Write, self.path);
|
||||
}
|
||||
expect_action(res, ErrAction::Write, self.path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -660,47 +665,91 @@ pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool {
|
|||
}
|
||||
|
||||
pub fn write_file(path: &Path, contents: &str) {
|
||||
fs::write(path, contents).unwrap_or_else(|e| panic_action(&e, ErrAction::Write, path));
|
||||
expect_action(fs::write(path, contents), ErrAction::Write, path);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn run_with_output(path: &(impl AsRef<Path> + ?Sized), cmd: &mut Command) -> Vec<u8> {
|
||||
fn f(path: &Path, cmd: &mut Command) -> Vec<u8> {
|
||||
match cmd
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
{
|
||||
Ok(x) => match x.status.exit_ok() {
|
||||
Ok(()) => x.stdout,
|
||||
Err(ref e) => panic_action(e, ErrAction::Run, path),
|
||||
},
|
||||
Err(ref e) => panic_action(e, ErrAction::Run, path),
|
||||
}
|
||||
let output = expect_action(
|
||||
cmd.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::inherit())
|
||||
.output(),
|
||||
ErrAction::Run,
|
||||
path,
|
||||
);
|
||||
expect_action(output.status.exit_ok(), ErrAction::Run, path);
|
||||
output.stdout
|
||||
}
|
||||
f(path.as_ref(), cmd)
|
||||
}
|
||||
|
||||
pub fn run_with_args_split(
|
||||
mut make_cmd: impl FnMut() -> Command,
|
||||
mut run_cmd: impl FnMut(&mut Command),
|
||||
args: impl Iterator<Item: AsRef<OsStr>>,
|
||||
) {
|
||||
let mut cmd = make_cmd();
|
||||
let mut len = 0;
|
||||
for arg in args {
|
||||
len += arg.as_ref().len();
|
||||
cmd.arg(arg);
|
||||
// Very conservative limit
|
||||
if len > 10000 {
|
||||
run_cmd(&mut cmd);
|
||||
cmd = make_cmd();
|
||||
len = 0;
|
||||
/// Splits an argument list across multiple `Command` invocations.
|
||||
///
|
||||
/// The argument list will be split into a number of batches based on
|
||||
/// `thread::available_parallelism`, with `min_batch_size` setting a lower bound on the size of each
|
||||
/// batch.
|
||||
///
|
||||
/// If the size of the arguments would exceed the system limit additional batches will be created.
|
||||
pub fn split_args_for_threads(
|
||||
min_batch_size: usize,
|
||||
make_cmd: impl FnMut() -> Command,
|
||||
args: impl ExactSizeIterator<Item: AsRef<OsStr>>,
|
||||
) -> impl Iterator<Item = Command> {
|
||||
struct Iter<F, I> {
|
||||
make_cmd: F,
|
||||
args: I,
|
||||
min_batch_size: usize,
|
||||
batch_size: usize,
|
||||
thread_count: usize,
|
||||
}
|
||||
impl<F, I> Iterator for Iter<F, I>
|
||||
where
|
||||
F: FnMut() -> Command,
|
||||
I: ExactSizeIterator<Item: AsRef<OsStr>>,
|
||||
{
|
||||
type Item = Command;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.thread_count > 1 {
|
||||
self.thread_count -= 1;
|
||||
}
|
||||
let mut cmd = (self.make_cmd)();
|
||||
let mut cmd_len = 0usize;
|
||||
for arg in self.args.by_ref().take(self.batch_size) {
|
||||
cmd.arg(arg.as_ref());
|
||||
// `+ 8` to account for the `argv` pointer on unix.
|
||||
// Windows is complicated since the arguments are first converted to UTF-16ish,
|
||||
// but this needs to account for the space between arguments and whatever additional
|
||||
// is needed to escape within an argument.
|
||||
cmd_len += arg.as_ref().len() + 8;
|
||||
cmd_len += 8;
|
||||
|
||||
// Windows has a command length limit of 32767. For unix systems this is more
|
||||
// complicated since the limit includes environment variables and room needs to be
|
||||
// left to edit them once the program starts, but the total size comes from
|
||||
// `getconf ARG_MAX`.
|
||||
//
|
||||
// For simplicity we use 30000 here under a few assumptions.
|
||||
// * Individual arguments aren't super long (the final argument is still added)
|
||||
// * `ARG_MAX` is set to a reasonable amount. Basically every system will be configured way above
|
||||
// what windows supports, but POSIX only requires `4096`.
|
||||
if cmd_len > 30000 {
|
||||
self.batch_size = self.args.len().div_ceil(self.thread_count).max(self.min_batch_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
(cmd_len != 0).then_some(cmd)
|
||||
}
|
||||
}
|
||||
if len != 0 {
|
||||
run_cmd(&mut cmd);
|
||||
let thread_count = thread::available_parallelism().map_or(1, NonZero::get);
|
||||
let batch_size = args.len().div_ceil(thread_count).max(min_batch_size);
|
||||
Iter {
|
||||
make_cmd,
|
||||
args,
|
||||
min_batch_size,
|
||||
batch_size,
|
||||
thread_count,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -720,3 +769,12 @@ pub fn delete_dir_if_exists(path: &Path) {
|
|||
Err(ref e) => panic_action(e, ErrAction::Delete, path),
|
||||
}
|
||||
}
|
||||
|
||||
/// Walks all items excluding top-level dot files/directories and any target directories.
|
||||
pub fn walk_dir_no_dot_or_target() -> impl Iterator<Item = ::walkdir::Result<::walkdir::DirEntry>> {
|
||||
WalkDir::new(".").into_iter().filter_entry(|e| {
|
||||
e.path()
|
||||
.file_name()
|
||||
.is_none_or(|x| x != "target" && x.as_encoded_bytes().first().copied() != Some(b'.'))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::macros::{MacroCall, macro_backtrace};
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_hir::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{Span, SyntaxContext, sym};
|
||||
|
|
@ -60,6 +60,8 @@ impl LateLintPass<'_> for DbgMacro {
|
|||
if cur_syntax_ctxt != self.prev_ctxt &&
|
||||
let Some(macro_call) = first_dbg_macro_in_expansion(cx, expr.span) &&
|
||||
!macro_call.span.in_external_macro(cx.sess().source_map()) &&
|
||||
// avoids exprs generated by the desugaring of coroutines
|
||||
!is_coroutine_desugar(expr) &&
|
||||
self.checked_dbg_call_site.insert(macro_call.span) &&
|
||||
// allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
|
||||
!(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id))
|
||||
|
|
@ -73,50 +75,51 @@ impl LateLintPass<'_> for DbgMacro {
|
|||
"the `dbg!` macro is intended as a debugging tool",
|
||||
|diag| {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let (sugg_span, suggestion) = match expr.peel_drop_temps().kind {
|
||||
// dbg!()
|
||||
ExprKind::Block(..) => {
|
||||
// If the `dbg!` macro is a "free" statement and not contained within other expressions,
|
||||
// remove the whole statement.
|
||||
if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span)
|
||||
{
|
||||
(macro_call.span.to(semi_span), String::new())
|
||||
} else {
|
||||
(macro_call.span, String::from("()"))
|
||||
}
|
||||
},
|
||||
// dbg!(1)
|
||||
ExprKind::Match(val, ..) => (
|
||||
macro_call.span,
|
||||
snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability)
|
||||
.to_string(),
|
||||
),
|
||||
// dbg!(2, 3)
|
||||
ExprKind::Tup(
|
||||
[
|
||||
Expr {
|
||||
kind: ExprKind::Match(first, ..),
|
||||
..
|
||||
},
|
||||
..,
|
||||
Expr {
|
||||
kind: ExprKind::Match(last, ..),
|
||||
..
|
||||
},
|
||||
],
|
||||
) => {
|
||||
let snippet = snippet_with_applicability(
|
||||
cx,
|
||||
first.span.source_callsite().to(last.span.source_callsite()),
|
||||
"..",
|
||||
&mut applicability,
|
||||
);
|
||||
(macro_call.span, format!("({snippet})"))
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let (sugg_span, suggestion) =
|
||||
match is_async_move_desugar(expr).unwrap_or(expr).peel_drop_temps().kind {
|
||||
// dbg!()
|
||||
ExprKind::Block(..) => {
|
||||
// If the `dbg!` macro is a "free" statement and not contained within other expressions,
|
||||
// remove the whole statement.
|
||||
if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let Some(semi_span) =
|
||||
cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span)
|
||||
{
|
||||
(macro_call.span.to(semi_span), String::new())
|
||||
} else {
|
||||
(macro_call.span, String::from("()"))
|
||||
}
|
||||
},
|
||||
// dbg!(1)
|
||||
ExprKind::Match(val, ..) => (
|
||||
macro_call.span,
|
||||
snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability)
|
||||
.to_string(),
|
||||
),
|
||||
// dbg!(2, 3)
|
||||
ExprKind::Tup(
|
||||
[
|
||||
Expr {
|
||||
kind: ExprKind::Match(first, ..),
|
||||
..
|
||||
},
|
||||
..,
|
||||
Expr {
|
||||
kind: ExprKind::Match(last, ..),
|
||||
..
|
||||
},
|
||||
],
|
||||
) => {
|
||||
let snippet = snippet_with_applicability(
|
||||
cx,
|
||||
first.span.source_callsite().to(last.span.source_callsite()),
|
||||
"..",
|
||||
&mut applicability,
|
||||
);
|
||||
(macro_call.span, format!("({snippet})"))
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
diag.span_suggestion(
|
||||
sugg_span,
|
||||
|
|
@ -134,6 +137,35 @@ impl LateLintPass<'_> for DbgMacro {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_coroutine_desugar(expr: &Expr<'_>) -> bool {
|
||||
matches!(
|
||||
expr.kind,
|
||||
ExprKind::Closure(Closure {
|
||||
kind: ClosureKind::Coroutine(CoroutineKind::Desugared(..)) | ClosureKind::CoroutineClosure(..),
|
||||
..
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn is_async_move_desugar<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if let ExprKind::Block(block, _) = expr.kind
|
||||
&& let [
|
||||
Stmt {
|
||||
kind:
|
||||
StmtKind::Let(LetStmt {
|
||||
source: LocalSource::AsyncFn,
|
||||
..
|
||||
}),
|
||||
..
|
||||
},
|
||||
] = block.stmts
|
||||
{
|
||||
return block.expr;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> {
|
||||
macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
RefOp::Method { mutbl, is_ufcs }
|
||||
if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
|
||||
// Allow explicit deref in method chains. e.g. `foo.deref().bar()`
|
||||
&& (is_ufcs || !in_postfix_position(cx, expr)) =>
|
||||
&& (is_ufcs || !is_in_method_chain(cx, expr)) =>
|
||||
{
|
||||
let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)));
|
||||
self.state = Some((
|
||||
|
|
@ -728,7 +728,13 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||
fn is_in_method_chain<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||
if let ExprKind::MethodCall(_, recv, _, _) = e.kind
|
||||
&& matches!(recv.kind, ExprKind::MethodCall(..))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(parent) = get_parent_expr(cx, e)
|
||||
&& parent.span.eq_ctxt(e.span)
|
||||
{
|
||||
|
|
@ -986,6 +992,15 @@ fn report<'tcx>(
|
|||
);
|
||||
},
|
||||
State::DerefedBorrow(state) => {
|
||||
// Do not suggest removing a non-mandatory `&` in `&*rawptr` in an `unsafe` context,
|
||||
// as this may make rustc trigger its `dangerous_implicit_autorefs` lint.
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, subexpr) = data.first_expr.kind
|
||||
&& let ExprKind::Unary(UnOp::Deref, subsubexpr) = subexpr.kind
|
||||
&& cx.typeck_results().expr_ty_adjusted(subsubexpr).is_raw_ptr()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (snip, snip_is_macro) =
|
||||
snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::types::{DisallowedPath, create_disallowed_map};
|
||||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
|||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{BytePos, Span};
|
||||
use std::cmp::Ordering;
|
||||
use rustc_span::BytePos;
|
||||
use std::ops::Range;
|
||||
|
||||
use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS};
|
||||
use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS, Fragments};
|
||||
|
||||
fn map_container_to_text(c: &super::Container) -> &'static str {
|
||||
match c {
|
||||
|
|
@ -19,29 +18,27 @@ fn map_container_to_text(c: &super::Container) -> &'static str {
|
|||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
doc: &str,
|
||||
range: Range<usize>,
|
||||
mut span: Span,
|
||||
cooked_range: Range<usize>,
|
||||
fragments: &Fragments<'_>,
|
||||
containers: &[super::Container],
|
||||
) {
|
||||
if doc[range.clone()].contains('\t') {
|
||||
// We don't do tab stops correctly.
|
||||
return;
|
||||
}
|
||||
|
||||
// Blockquote
|
||||
let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count();
|
||||
// Get blockquotes
|
||||
let ccount = doc[cooked_range.clone()].chars().filter(|c| *c == '>').count();
|
||||
let blockquote_level = containers
|
||||
.iter()
|
||||
.filter(|c| matches!(c, super::Container::Blockquote))
|
||||
.count();
|
||||
if ccount < blockquote_level {
|
||||
|
||||
if ccount < blockquote_level
|
||||
&& let Some(mut span) = fragments.span(cx, cooked_range.clone())
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
span,
|
||||
"doc quote line without `>` marker",
|
||||
|diag| {
|
||||
let mut doc_start_range = &doc[range];
|
||||
let mut doc_start_range = &doc[cooked_range];
|
||||
let mut suggested = String::new();
|
||||
for c in containers {
|
||||
let text = map_container_to_text(c);
|
||||
|
|
@ -78,7 +75,7 @@ pub(super) fn check(
|
|||
}
|
||||
|
||||
// List
|
||||
let leading_spaces = doc[range].chars().filter(|c| *c == ' ').count();
|
||||
let leading_spaces = doc[cooked_range.clone()].chars().filter(|c| *c == ' ').count();
|
||||
let list_indentation = containers
|
||||
.iter()
|
||||
.map(|c| {
|
||||
|
|
@ -89,36 +86,41 @@ pub(super) fn check(
|
|||
}
|
||||
})
|
||||
.sum();
|
||||
match leading_spaces.cmp(&list_indentation) {
|
||||
Ordering::Less => span_lint_and_then(
|
||||
cx,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
span,
|
||||
"doc list item without indentation",
|
||||
|diag| {
|
||||
// simpler suggestion style for indentation
|
||||
let indent = list_indentation - leading_spaces;
|
||||
diag.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
"indent this line",
|
||||
std::iter::repeat_n(" ", indent).join(""),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.help("if this is supposed to be its own paragraph, add a blank line");
|
||||
},
|
||||
),
|
||||
Ordering::Greater => {
|
||||
let sugg = std::iter::repeat_n(" ", list_indentation).join("");
|
||||
span_lint_and_sugg(
|
||||
|
||||
if leading_spaces != list_indentation
|
||||
&& let Some(span) = fragments.span(cx, cooked_range.clone())
|
||||
{
|
||||
if leading_spaces < list_indentation {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
span,
|
||||
"doc list item overindented",
|
||||
format!("try using `{sugg}` ({list_indentation} spaces)"),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
"doc list item without indentation",
|
||||
|diag| {
|
||||
// simpler suggestion style for indentation
|
||||
let indent = list_indentation - leading_spaces;
|
||||
diag.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
"indent this line",
|
||||
std::iter::repeat_n(" ", indent).join(""),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.help("if this is supposed to be its own paragraph, add a blank line");
|
||||
},
|
||||
);
|
||||
},
|
||||
Ordering::Equal => {},
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let sugg = std::iter::repeat_n(" ", list_indentation).join("");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
span,
|
||||
"doc list item overindented",
|
||||
format!("try using `{sugg}` ({list_indentation} spaces)"),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,15 @@ use rustc_lint::LateContext;
|
|||
use rustc_span::{BytePos, Pos, Span};
|
||||
use url::Url;
|
||||
|
||||
use crate::doc::DOC_MARKDOWN;
|
||||
use crate::doc::{DOC_MARKDOWN, Fragments};
|
||||
use std::ops::Range;
|
||||
|
||||
pub fn check(
|
||||
cx: &LateContext<'_>,
|
||||
valid_idents: &FxHashSet<String>,
|
||||
text: &str,
|
||||
span: Span,
|
||||
fragments: &Fragments<'_>,
|
||||
fragment_range: Range<usize>,
|
||||
code_level: isize,
|
||||
blockquote_level: isize,
|
||||
) {
|
||||
|
|
@ -64,20 +66,31 @@ pub fn check(
|
|||
close_parens += 1;
|
||||
}
|
||||
|
||||
// Adjust for the current word
|
||||
let offset = word.as_ptr() as usize - text.as_ptr() as usize;
|
||||
let span = Span::new(
|
||||
span.lo() + BytePos::from_usize(offset),
|
||||
span.lo() + BytePos::from_usize(offset + word.len()),
|
||||
span.ctxt(),
|
||||
span.parent(),
|
||||
);
|
||||
// We'll use this offset to calculate the span to lint.
|
||||
let fragment_offset = word.as_ptr() as usize - text.as_ptr() as usize;
|
||||
|
||||
check_word(cx, word, span, code_level, blockquote_level);
|
||||
// Adjust for the current word
|
||||
check_word(
|
||||
cx,
|
||||
word,
|
||||
fragments,
|
||||
&fragment_range,
|
||||
fragment_offset,
|
||||
code_level,
|
||||
blockquote_level,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, blockquote_level: isize) {
|
||||
fn check_word(
|
||||
cx: &LateContext<'_>,
|
||||
word: &str,
|
||||
fragments: &Fragments<'_>,
|
||||
range: &Range<usize>,
|
||||
fragment_offset: usize,
|
||||
code_level: isize,
|
||||
blockquote_level: isize,
|
||||
) {
|
||||
/// Checks if a string is upper-camel-case, i.e., starts with an uppercase and
|
||||
/// contains at least two uppercase letters (`Clippy` is ok) and one lower-case
|
||||
/// letter (`NASA` is ok).
|
||||
|
|
@ -117,6 +130,16 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
|
|||
// try to get around the fact that `foo::bar` parses as a valid URL
|
||||
&& !url.cannot_be_a_base()
|
||||
{
|
||||
let Some(fragment_span) = fragments.span(cx, range.clone()) else {
|
||||
return;
|
||||
};
|
||||
let span = Span::new(
|
||||
fragment_span.lo() + BytePos::from_usize(fragment_offset),
|
||||
fragment_span.lo() + BytePos::from_usize(fragment_offset + word.len()),
|
||||
fragment_span.ctxt(),
|
||||
fragment_span.parent(),
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
|
|
@ -137,6 +160,17 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
|
|||
}
|
||||
|
||||
if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") {
|
||||
let Some(fragment_span) = fragments.span(cx, range.clone()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let span = Span::new(
|
||||
fragment_span.lo() + BytePos::from_usize(fragment_offset),
|
||||
fragment_span.lo() + BytePos::from_usize(fragment_offset + word.len()),
|
||||
fragment_span.ctxt(),
|
||||
fragment_span.parent(),
|
||||
);
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item};
|
||||
use pulldown_cmark::Event::{
|
||||
Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start,
|
||||
|
|
@ -730,7 +729,10 @@ struct Fragments<'a> {
|
|||
}
|
||||
|
||||
impl Fragments<'_> {
|
||||
fn span(self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> {
|
||||
/// get the span for the markdown range. Note that this function is not cheap, use it with
|
||||
/// caution.
|
||||
#[must_use]
|
||||
fn span(&self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> {
|
||||
source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments)
|
||||
}
|
||||
}
|
||||
|
|
@ -1068,9 +1070,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
);
|
||||
} else {
|
||||
for (text, range, assoc_code_level) in text_to_check {
|
||||
if let Some(span) = fragments.span(cx, range) {
|
||||
markdown::check(cx, valid_idents, &text, span, assoc_code_level, blockquote_level);
|
||||
}
|
||||
markdown::check(cx, valid_idents, &text, &fragments, range, assoc_code_level, blockquote_level);
|
||||
}
|
||||
}
|
||||
text_to_check = Vec::new();
|
||||
|
|
@ -1081,26 +1081,27 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
| TaskListMarker(_) | Code(_) | Rule | InlineMath(..) | DisplayMath(..) => (),
|
||||
SoftBreak | HardBreak => {
|
||||
if !containers.is_empty()
|
||||
&& let Some((next_event, next_range)) = events.peek()
|
||||
&& let Some(next_span) = fragments.span(cx, next_range.clone())
|
||||
&& let Some(span) = fragments.span(cx, range.clone())
|
||||
&& !in_footnote_definition
|
||||
// Tabs aren't handled correctly vvvv
|
||||
&& !doc[range.clone()].contains('\t')
|
||||
&& let Some((next_event, next_range)) = events.peek()
|
||||
&& !matches!(next_event, End(_))
|
||||
{
|
||||
lazy_continuation::check(
|
||||
cx,
|
||||
doc,
|
||||
range.end..next_range.start,
|
||||
Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()),
|
||||
&fragments,
|
||||
&containers[..],
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(span) = fragments.span(cx, range.clone())
|
||||
|
||||
if event == HardBreak
|
||||
&& !doc[range.clone()].trim().starts_with('\\')
|
||||
&& let Some(span) = fragments.span(cx, range.clone())
|
||||
&& !span.from_expansion()
|
||||
&& let Some(snippet) = snippet_opt(cx, span)
|
||||
&& !snippet.trim().starts_with('\\')
|
||||
&& event == HardBreak {
|
||||
{
|
||||
collected_breaks.push(span);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -83,6 +83,13 @@ pub(super) fn check<'tcx>(
|
|||
)[..],
|
||||
);
|
||||
}
|
||||
|
||||
// If the return type requires adjustments, we need to add a `.map` after the iterator
|
||||
let inner_ret_adjust = cx.typeck_results().expr_adjustments(inner_ret);
|
||||
if !inner_ret_adjust.is_empty() {
|
||||
snippet.push_str(".map(|v| v as _)");
|
||||
}
|
||||
|
||||
// Extends to `last_stmt` to include semicolon in case of `return None;`
|
||||
let lint_span = span.to(last_stmt.span).to(last_ret.span);
|
||||
span_lint_and_then(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use super::utils::make_iterator_snippet;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt};
|
||||
use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, Pat, PatKind};
|
||||
|
|
@ -28,7 +28,7 @@ pub(super) fn check<'tcx>(
|
|||
&& let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind
|
||||
&& path_to_local_id(let_expr, pat_hir_id)
|
||||
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
|
||||
&& let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind
|
||||
&& let PatKind::TupleStruct(ref qpath, [inner_pat], _) = let_pat.kind
|
||||
&& let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id)
|
||||
&& let Some(variant_id) = cx.tcx.opt_parent(ctor_id)
|
||||
&& let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id)
|
||||
|
|
@ -37,6 +37,7 @@ pub(super) fn check<'tcx>(
|
|||
// Ensure expr in `if let` is not used afterwards
|
||||
&& !is_local_used(cx, if_then, pat_hir_id)
|
||||
&& msrv.meets(cx, msrvs::ITER_FLATTEN)
|
||||
&& !is_refutable(cx, inner_pat)
|
||||
{
|
||||
let if_let_type = if some_ctor { "Some" } else { "Ok" };
|
||||
// Prepare the error message
|
||||
|
|
|
|||
|
|
@ -1,68 +1,65 @@
|
|||
use super::WHILE_LET_LOOP;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::source::{snippet, snippet_indent, snippet_opt};
|
||||
use clippy_utils::ty::needs_ordered_drop;
|
||||
use clippy_utils::visitors::any_temporaries_need_ordered_drop;
|
||||
use clippy_utils::{higher, peel_blocks};
|
||||
use rustc_ast::BindingMode;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, StmtKind};
|
||||
use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind, Path, QPath, StmtKind, Ty};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
|
||||
let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
|
||||
([stmt, stmts @ ..], expr) => {
|
||||
if let StmtKind::Let(&LetStmt {
|
||||
let (init, let_info) = match (loop_block.stmts, loop_block.expr) {
|
||||
([stmt, ..], _) => match stmt.kind {
|
||||
StmtKind::Let(LetStmt {
|
||||
init: Some(e),
|
||||
els: None,
|
||||
pat,
|
||||
ty,
|
||||
..
|
||||
})
|
||||
| StmtKind::Semi(e)
|
||||
| StmtKind::Expr(e) = stmt.kind
|
||||
{
|
||||
(e, !stmts.is_empty() || expr.is_some())
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}) => (*e, Some((*pat, *ty))),
|
||||
StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None),
|
||||
_ => return,
|
||||
},
|
||||
([], Some(e)) => (e, false),
|
||||
([], Some(e)) => (e, None),
|
||||
_ => return,
|
||||
};
|
||||
let has_trailing_exprs = loop_block.stmts.len() + usize::from(loop_block.expr.is_some()) > 1;
|
||||
|
||||
if let Some(if_let) = higher::IfLet::hir(cx, init)
|
||||
&& let Some(else_expr) = if_let.if_else
|
||||
&& is_simple_break_expr(else_expr)
|
||||
{
|
||||
could_be_while_let(cx, expr, if_let.let_pat, if_let.let_expr, has_trailing_exprs);
|
||||
could_be_while_let(
|
||||
cx,
|
||||
expr,
|
||||
if_let.let_pat,
|
||||
if_let.let_expr,
|
||||
has_trailing_exprs,
|
||||
let_info,
|
||||
if_let.if_then,
|
||||
);
|
||||
} else if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal) = init.kind
|
||||
&& arm1.guard.is_none()
|
||||
&& arm2.guard.is_none()
|
||||
&& is_simple_break_expr(arm2.body)
|
||||
{
|
||||
could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs);
|
||||
could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs, let_info, arm1.body);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if expr contains a single break expression without a label or eub-expression.
|
||||
/// Returns `true` if expr contains a single break expression without a label or sub-expression,
|
||||
/// possibly embedded in blocks.
|
||||
fn is_simple_break_expr(e: &Expr<'_>) -> bool {
|
||||
matches!(peel_blocks(e).kind, ExprKind::Break(dest, None) if dest.label.is_none())
|
||||
}
|
||||
|
||||
/// Removes any blocks containing only a single expression.
|
||||
fn peel_blocks<'tcx>(e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
|
||||
if let ExprKind::Block(b, _) = e.kind {
|
||||
match (b.stmts, b.expr) {
|
||||
([s], None) => {
|
||||
if let StmtKind::Expr(e) | StmtKind::Semi(e) = s.kind {
|
||||
peel_blocks(e)
|
||||
} else {
|
||||
e
|
||||
}
|
||||
},
|
||||
([], Some(e)) => peel_blocks(e),
|
||||
_ => e,
|
||||
([s], None) => matches!(s.kind, StmtKind::Expr(e) | StmtKind::Semi(e) if is_simple_break_expr(e)),
|
||||
([], Some(e)) => is_simple_break_expr(e),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
e
|
||||
matches!(e.kind, ExprKind::Break(dest, None) if dest.label.is_none())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,6 +69,8 @@ fn could_be_while_let<'tcx>(
|
|||
let_pat: &'tcx Pat<'_>,
|
||||
let_expr: &'tcx Expr<'_>,
|
||||
has_trailing_exprs: bool,
|
||||
let_info: Option<(&Pat<'_>, Option<&Ty<'_>>)>,
|
||||
inner_expr: &Expr<'_>,
|
||||
) {
|
||||
if has_trailing_exprs
|
||||
&& (needs_ordered_drop(cx, cx.typeck_results().expr_ty(let_expr))
|
||||
|
|
@ -86,7 +85,24 @@ fn could_be_while_let<'tcx>(
|
|||
// 1) it was ugly with big bodies;
|
||||
// 2) it was not indented properly;
|
||||
// 3) it wasn’t very smart (see #675).
|
||||
let mut applicability = Applicability::HasPlaceholders;
|
||||
let inner_content = if let Some((pat, ty)) = let_info
|
||||
// Prevent trivial reassignments such as `let x = x;` or `let _ = …;`, but
|
||||
// keep them if the type has been explicitly specified.
|
||||
&& (!is_trivial_assignment(pat, peel_blocks(inner_expr)) || ty.is_some())
|
||||
&& let Some(pat_str) = snippet_opt(cx, pat.span)
|
||||
&& let Some(init_str) = snippet_opt(cx, peel_blocks(inner_expr).span)
|
||||
{
|
||||
let ty_str = ty
|
||||
.map(|ty| format!(": {}", snippet(cx, ty.span, "_")))
|
||||
.unwrap_or_default();
|
||||
format!(
|
||||
"\n{indent} let {pat_str}{ty_str} = {init_str};\n{indent} ..\n{indent}",
|
||||
indent = snippet_indent(cx, expr.span).unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
" .. ".into()
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
WHILE_LET_LOOP,
|
||||
|
|
@ -94,10 +110,21 @@ fn could_be_while_let<'tcx>(
|
|||
"this loop could be written as a `while let` loop",
|
||||
"try",
|
||||
format!(
|
||||
"while let {} = {} {{ .. }}",
|
||||
snippet_with_applicability(cx, let_pat.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, let_expr.span, "..", &mut applicability),
|
||||
"while let {} = {} {{{inner_content}}}",
|
||||
snippet(cx, let_pat.span, ".."),
|
||||
snippet(cx, let_expr.span, ".."),
|
||||
),
|
||||
applicability,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
|
||||
fn is_trivial_assignment(pat: &Pat<'_>, init: &Expr<'_>) -> bool {
|
||||
match (pat.kind, init.kind) {
|
||||
(PatKind::Wild, _) => true,
|
||||
(
|
||||
PatKind::Binding(BindingMode::NONE, _, pat_ident, None),
|
||||
ExprKind::Path(QPath::Resolved(None, Path { segments: [init], .. })),
|
||||
) => pat_ident.name == init.ident.name,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{BytePos, Span, sym};
|
||||
|
||||
use super::MANUAL_IS_VARIANT_AND;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &'tcx rustc_hir::Expr<'_>,
|
||||
map_recv: &'tcx rustc_hir::Expr<'_>,
|
||||
map_arg: &'tcx rustc_hir::Expr<'_>,
|
||||
expr: &Expr<'_>,
|
||||
map_recv: &Expr<'_>,
|
||||
map_arg: &Expr<'_>,
|
||||
map_span: Span,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
|
|
@ -57,3 +61,57 @@ pub(super) fn check<'tcx>(
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn emit_lint(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, method_span: Span, is_option: bool) {
|
||||
if let Some(before_map_snippet) = snippet_opt(cx, parent.span.with_hi(method_span.lo()))
|
||||
&& let Some(after_map_snippet) = snippet_opt(cx, method_span.with_lo(method_span.lo() + BytePos(3)))
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_IS_VARIANT_AND,
|
||||
parent.span,
|
||||
format!(
|
||||
"called `.map() {}= {}()`",
|
||||
if op == BinOpKind::Eq { '=' } else { '!' },
|
||||
if is_option { "Some" } else { "Ok" },
|
||||
),
|
||||
"use",
|
||||
if is_option && op == BinOpKind::Ne {
|
||||
format!("{before_map_snippet}is_none_or{after_map_snippet}",)
|
||||
} else {
|
||||
format!(
|
||||
"{}{before_map_snippet}{}{after_map_snippet}",
|
||||
if op == BinOpKind::Eq { "" } else { "!" },
|
||||
if is_option { "is_some_and" } else { "is_ok_and" },
|
||||
)
|
||||
},
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::Binary(op, left, right) = parent_expr.kind
|
||||
&& matches!(op.node, BinOpKind::Eq | BinOpKind::Ne)
|
||||
&& op.span.eq_ctxt(expr.span)
|
||||
{
|
||||
// Check `left` and `right` expression in any order, and for `Option` and `Result`
|
||||
for (expr1, expr2) in [(left, right), (right, left)] {
|
||||
for item in [sym::Option, sym::Result] {
|
||||
if let ExprKind::Call(call, ..) = expr1.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = call.kind
|
||||
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = path.res
|
||||
&& let ty = cx.typeck_results().expr_ty(expr1)
|
||||
&& let ty::Adt(adt, args) = ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(item, adt.did())
|
||||
&& args.type_at(0).is_bool()
|
||||
&& let ExprKind::MethodCall(_, recv, _, span) = expr2.kind
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), item)
|
||||
{
|
||||
return emit_lint(cx, op.node, parent_expr, span, item == sym::Option);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4439,7 +4439,7 @@ declare_clippy_lint! {
|
|||
/// Checks for usage of `iter().any()` on slices when it can be replaced with `contains()` and suggests doing so.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `contains()` is more concise and idiomatic, sometimes more fast.
|
||||
/// `contains()` is more concise and idiomatic, while also being faster in some cases.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
|
|
@ -5203,6 +5203,7 @@ impl Methods {
|
|||
unused_enumerate_index::check(cx, expr, recv, m_arg);
|
||||
map_clone::check(cx, expr, recv, m_arg, self.msrv);
|
||||
map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span);
|
||||
manual_is_variant_and::check_map(cx, expr);
|
||||
match method_call(recv) {
|
||||
Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) => {
|
||||
iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ use clippy_utils::{
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use clippy_utils::{is_trait_method, std_or_core};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ pub fn check<'tcx>(
|
|||
return;
|
||||
}
|
||||
// At this point, we know the call is of a `to_owned`-like function. The functions
|
||||
// `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
|
||||
// `check_addr_of_expr` and `check_into_iter_call_arg` determine whether the call is unnecessary
|
||||
// based on its context, that is, whether it is a referent in an `AddrOf` expression, an
|
||||
// argument in a `into_iter` call, or an argument in the call of some other function.
|
||||
if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
|
||||
|
|
|
|||
|
|
@ -79,25 +79,19 @@ fn check_arguments<'tcx>(
|
|||
name: &str,
|
||||
fn_kind: &str,
|
||||
) {
|
||||
match type_definition.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(..) => {
|
||||
let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
|
||||
for (argument, parameter) in iter::zip(arguments, parameters) {
|
||||
match parameter.kind() {
|
||||
ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) => {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind {
|
||||
span_lint(
|
||||
cx,
|
||||
UNNECESSARY_MUT_PASSED,
|
||||
argument.span,
|
||||
format!("the {fn_kind} `{name}` doesn't need a mutable reference"),
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
if let ty::FnDef(..) | ty::FnPtr(..) = type_definition.kind() {
|
||||
let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
|
||||
for (argument, parameter) in iter::zip(arguments, parameters) {
|
||||
if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind()
|
||||
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
UNNECESSARY_MUT_PASSED,
|
||||
argument.span,
|
||||
format!("the {fn_kind} `{name}` doesn't need a mutable reference"),
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,10 +99,6 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id)
|
||||
&& adj
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
|
|||
&& let body = cx.tcx.hir_body(body)
|
||||
// Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
|
||||
// and suggesting `for … in … { unsafe { } }` is a little ugly.
|
||||
&& let ExprKind::Block(Block { rules: BlockCheckMode::DefaultBlock, .. }, ..) = body.value.kind
|
||||
&& !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..))
|
||||
{
|
||||
let mut ret_collector = RetCollector::default();
|
||||
ret_collector.visit_expr(body.value);
|
||||
|
|
@ -99,11 +99,21 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
|
|||
)
|
||||
};
|
||||
|
||||
let body_param_sugg = snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability);
|
||||
let for_each_rev_sugg = snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability);
|
||||
let body_value_sugg = snippet_with_applicability(cx, body.value.span, "..", &mut applicability);
|
||||
|
||||
let sugg = format!(
|
||||
"for {} in {} {}",
|
||||
snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, body.value.span, "..", &mut applicability),
|
||||
body_param_sugg,
|
||||
for_each_rev_sugg,
|
||||
match body.value.kind {
|
||||
ExprKind::Block(block, _) if is_let_desugar(block) => {
|
||||
format!("{{ {body_value_sugg} }}")
|
||||
},
|
||||
ExprKind::Block(_, _) => body_value_sugg.to_string(),
|
||||
_ => format!("{{ {body_value_sugg}; }}"),
|
||||
}
|
||||
);
|
||||
|
||||
span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| {
|
||||
|
|
@ -116,6 +126,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if the block is a desugared `_ = expr` statement.
|
||||
fn is_let_desugar(block: &Block<'_>) -> bool {
|
||||
matches!(
|
||||
block,
|
||||
Block {
|
||||
stmts: [Stmt {
|
||||
kind: StmtKind::Let(_),
|
||||
..
|
||||
},],
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// This type plays two roles.
|
||||
/// 1. Collect spans of `return` in the closure body.
|
||||
/// 2. Detect use of `return` in `Loop` in the closure body.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use clippy_utils::qualify_min_const_fn::is_stable_const_fn;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||
use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method};
|
||||
use clippy_utils::{binop_traits, eq_expr_value, is_in_const_context, trait_ref_of_method};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
|
@ -19,6 +21,7 @@ pub(super) fn check<'tcx>(
|
|||
expr: &'tcx hir::Expr<'_>,
|
||||
assignee: &'tcx hir::Expr<'_>,
|
||||
e: &'tcx hir::Expr<'_>,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
if let hir::ExprKind::Binary(op, l, r) = &e.kind {
|
||||
let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
|
||||
|
|
@ -40,6 +43,15 @@ pub(super) fn check<'tcx>(
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip if the trait is not stable in const contexts
|
||||
if is_in_const_context(cx)
|
||||
&& let Some(binop_id) = cx.tcx.associated_item_def_ids(trait_id).first()
|
||||
&& !is_stable_const_fn(cx, *binop_id, msrv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
ASSIGN_OP_PATTERN,
|
||||
|
|
|
|||
|
|
@ -919,7 +919,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
|
|||
modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false);
|
||||
},
|
||||
ExprKind::Assign(lhs, rhs, _) => {
|
||||
assign_op_pattern::check(cx, e, lhs, rhs);
|
||||
assign_op_pattern::check(cx, e, lhs, rhs, self.msrv);
|
||||
self_assignment::check(cx, e, lhs, rhs);
|
||||
},
|
||||
ExprKind::Unary(op, arg) => {
|
||||
|
|
|
|||
|
|
@ -34,14 +34,10 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id) else {
|
||||
return false;
|
||||
};
|
||||
let ExprKind::Binary(op, lhs, rhs) = parent_expr.kind else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if op.node == BinOpKind::Eq || op.node == BinOpKind::Ne {
|
||||
if let Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let ExprKind::Binary(op, lhs, rhs) = parent_expr.kind
|
||||
&& let BinOpKind::Eq | BinOpKind::Ne = op.node
|
||||
{
|
||||
let ecx = ConstEvalCtxt::new(cx);
|
||||
matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0)))
|
||||
} else {
|
||||
|
|
@ -56,35 +52,28 @@ struct OperandInfo {
|
|||
}
|
||||
|
||||
fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> {
|
||||
match ConstEvalCtxt::new(cx).eval(operand) {
|
||||
Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() {
|
||||
match ConstEvalCtxt::new(cx).eval(operand)? {
|
||||
Constant::Int(v) => match *cx.typeck_results().expr_ty(expr).kind() {
|
||||
ty::Int(ity) => {
|
||||
let value = sext(cx.tcx, v, ity);
|
||||
return Some(OperandInfo {
|
||||
Some(OperandInfo {
|
||||
string_representation: Some(value.to_string()),
|
||||
is_negative: value < 0,
|
||||
is_integral: true,
|
||||
});
|
||||
})
|
||||
},
|
||||
ty::Uint(_) => {
|
||||
return Some(OperandInfo {
|
||||
string_representation: None,
|
||||
is_negative: false,
|
||||
is_integral: true,
|
||||
});
|
||||
},
|
||||
_ => {},
|
||||
ty::Uint(_) => Some(OperandInfo {
|
||||
string_representation: None,
|
||||
is_negative: false,
|
||||
is_integral: true,
|
||||
}),
|
||||
_ => None,
|
||||
},
|
||||
// FIXME(f16_f128): add when casting is available on all platforms
|
||||
Some(Constant::F32(f)) => {
|
||||
return Some(floating_point_operand_info(&f));
|
||||
},
|
||||
Some(Constant::F64(f)) => {
|
||||
return Some(floating_point_operand_info(&f));
|
||||
},
|
||||
_ => {},
|
||||
Constant::F32(f) => Some(floating_point_operand_info(&f)),
|
||||
Constant::F64(f) => Some(floating_point_operand_info(&f)),
|
||||
_ => None,
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn floating_point_operand_info<T: Display + PartialOrd + From<f32>>(f: &T) -> OperandInfo {
|
||||
|
|
|
|||
|
|
@ -152,7 +152,6 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||
expr.span,
|
||||
"`panic_any` should not be present in production code",
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use std::{cmp, iter};
|
||||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
|
|
@ -20,6 +18,7 @@ use rustc_middle::ty::{self, RegionKind, TyCtxt};
|
|||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{Span, sym};
|
||||
use std::iter;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -33,10 +32,8 @@ declare_clippy_lint! {
|
|||
/// registers.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint is target register size dependent, it is
|
||||
/// limited to 32-bit to try and reduce portability problems between 32 and
|
||||
/// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
|
||||
/// will be different.
|
||||
/// This lint is target dependent, some cases will lint on 64-bit targets but
|
||||
/// not 32-bit or lower targets.
|
||||
///
|
||||
/// The configuration option `trivial_copy_size_limit` can be set to override
|
||||
/// this limit for a project.
|
||||
|
|
@ -112,16 +109,9 @@ pub struct PassByRefOrValue {
|
|||
|
||||
impl PassByRefOrValue {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
let ref_min_size = conf.trivial_copy_size_limit.unwrap_or_else(|| {
|
||||
let bit_width = u64::from(tcx.sess.target.pointer_width);
|
||||
// Cap the calculated bit width at 32-bits to reduce
|
||||
// portability problems between 32 and 64-bit targets
|
||||
let bit_width = cmp::min(bit_width, 32);
|
||||
#[expect(clippy::integer_division)]
|
||||
let byte_width = bit_width / 8;
|
||||
// Use a limit of 2 times the register byte width
|
||||
byte_width * 2
|
||||
});
|
||||
let ref_min_size = conf
|
||||
.trivial_copy_size_limit
|
||||
.unwrap_or_else(|| u64::from(tcx.sess.target.pointer_width / 8));
|
||||
|
||||
Self {
|
||||
ref_min_size,
|
||||
|
|
|
|||
|
|
@ -435,7 +435,9 @@ fn check_final_expr<'tcx>(
|
|||
ExprKind::If(_, then, else_clause_opt) => {
|
||||
check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
|
||||
if let Some(else_clause) = else_clause_opt {
|
||||
check_block_return(cx, &else_clause.kind, peeled_drop_expr.span, semi_spans);
|
||||
// The `RetReplacement` won't be used there as `else_clause` will be either a block or
|
||||
// a `if` expression.
|
||||
check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt);
|
||||
}
|
||||
},
|
||||
// a match expr, check all arms
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{ClauseKind, GenericPredicates, ProjectionPredicate, TraitPredicate};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{BytePos, Span, sym};
|
||||
use rustc_span::{BytePos, Span, Symbol, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -36,21 +36,26 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(UnitReturnExpectingOrd => [UNIT_RETURN_EXPECTING_ORD]);
|
||||
|
||||
fn get_trait_predicates_for_trait_id<'tcx>(
|
||||
// For each
|
||||
fn get_trait_predicates_for_trait_ids<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
generics: GenericPredicates<'tcx>,
|
||||
trait_id: Option<DefId>,
|
||||
) -> Vec<TraitPredicate<'tcx>> {
|
||||
let mut preds = Vec::new();
|
||||
trait_ids: &[Option<DefId>], // At least 2 ids
|
||||
) -> [Vec<TraitPredicate<'tcx>>; 3] {
|
||||
debug_assert!(trait_ids.len() >= 2);
|
||||
let mut preds = [Vec::new(), Vec::new(), Vec::new()];
|
||||
for (pred, _) in generics.predicates {
|
||||
if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder()
|
||||
&& let trait_pred = cx
|
||||
if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder() {
|
||||
let trait_pred = cx
|
||||
.tcx
|
||||
.instantiate_bound_regions_with_erased(pred.kind().rebind(poly_trait_pred))
|
||||
&& let Some(trait_def_id) = trait_id
|
||||
&& trait_def_id == trait_pred.trait_ref.def_id
|
||||
{
|
||||
preds.push(trait_pred);
|
||||
.instantiate_bound_regions_with_erased(pred.kind().rebind(poly_trait_pred));
|
||||
for (i, tid) in trait_ids.iter().enumerate() {
|
||||
if let Some(tid) = tid
|
||||
&& *tid == trait_pred.trait_ref.def_id
|
||||
{
|
||||
preds[i].push(trait_pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
preds
|
||||
|
|
@ -74,15 +79,24 @@ fn get_projection_pred<'tcx>(
|
|||
})
|
||||
}
|
||||
|
||||
fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> {
|
||||
fn get_args_to_check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
args_len: usize,
|
||||
fn_mut_trait: DefId,
|
||||
ord_trait: Option<DefId>,
|
||||
partial_ord_trait: Option<DefId>,
|
||||
) -> Vec<(usize, Symbol)> {
|
||||
let mut args_to_check = Vec::new();
|
||||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||
let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
|
||||
let generics = cx.tcx.predicates_of(def_id);
|
||||
let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait());
|
||||
let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord));
|
||||
let partial_ord_preds =
|
||||
get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait());
|
||||
let [fn_mut_preds, ord_preds, partial_ord_preds] =
|
||||
get_trait_predicates_for_trait_ids(cx, generics, &[Some(fn_mut_trait), ord_trait, partial_ord_trait]);
|
||||
if fn_mut_preds.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// Trying to call instantiate_bound_regions_with_erased on fn_sig.inputs() gives the following error
|
||||
// The trait `rustc::ty::TypeFoldable<'_>` is not implemented for
|
||||
// `&[rustc_middle::ty::Ty<'_>]`
|
||||
|
|
@ -102,12 +116,18 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve
|
|||
.iter()
|
||||
.any(|ord| Some(ord.self_ty()) == return_ty_pred.term.as_type())
|
||||
{
|
||||
args_to_check.push((i, "Ord".to_string()));
|
||||
args_to_check.push((i, sym::Ord));
|
||||
if args_to_check.len() == args_len - 1 {
|
||||
break;
|
||||
}
|
||||
} else if partial_ord_preds
|
||||
.iter()
|
||||
.any(|pord| pord.self_ty() == return_ty_pred.term.expect_type())
|
||||
{
|
||||
args_to_check.push((i, "PartialOrd".to_string()));
|
||||
args_to_check.push((i, sym::PartialOrd));
|
||||
if args_to_check.len() == args_len - 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -142,38 +162,50 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind {
|
||||
let arg_indices = get_args_to_check(cx, expr);
|
||||
if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind
|
||||
&& args.iter().any(|arg| {
|
||||
matches!(
|
||||
arg.peel_blocks().peel_borrows().peel_drop_temps().kind,
|
||||
ExprKind::Path(_) | ExprKind::Closure(_)
|
||||
)
|
||||
})
|
||||
&& let Some(fn_mut_trait) = cx.tcx.lang_items().fn_mut_trait()
|
||||
{
|
||||
let ord_trait = cx.tcx.get_diagnostic_item(sym::Ord);
|
||||
let partial_ord_trait = cx.tcx.lang_items().partial_ord_trait();
|
||||
if (ord_trait, partial_ord_trait) == (None, None) {
|
||||
return;
|
||||
}
|
||||
|
||||
let args = std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>();
|
||||
let arg_indices = get_args_to_check(cx, expr, args.len(), fn_mut_trait, ord_trait, partial_ord_trait);
|
||||
for (i, trait_name) in arg_indices {
|
||||
if i < args.len() {
|
||||
match check_arg(cx, args[i]) {
|
||||
Some((span, None)) => {
|
||||
span_lint(
|
||||
cx,
|
||||
UNIT_RETURN_EXPECTING_ORD,
|
||||
span,
|
||||
format!(
|
||||
"this closure returns \
|
||||
match check_arg(cx, args[i]) {
|
||||
Some((span, None)) => {
|
||||
span_lint(
|
||||
cx,
|
||||
UNIT_RETURN_EXPECTING_ORD,
|
||||
span,
|
||||
format!(
|
||||
"this closure returns \
|
||||
the unit type which also implements {trait_name}"
|
||||
),
|
||||
);
|
||||
},
|
||||
Some((span, Some(last_semi))) => {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNIT_RETURN_EXPECTING_ORD,
|
||||
span,
|
||||
format!(
|
||||
"this closure returns \
|
||||
),
|
||||
);
|
||||
},
|
||||
Some((span, Some(last_semi))) => {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNIT_RETURN_EXPECTING_ORD,
|
||||
span,
|
||||
format!(
|
||||
"this closure returns \
|
||||
the unit type which also implements {trait_name}"
|
||||
),
|
||||
Some(last_semi),
|
||||
"probably caused by this trailing semicolon",
|
||||
);
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
),
|
||||
Some(last_semi),
|
||||
"probably caused by this trailing semicolon",
|
||||
);
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
#![allow(clippy::missing_clippy_version_attribute)]
|
||||
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_attr_data_structures;
|
||||
extern crate rustc_attr_parsing;
|
||||
extern crate rustc_data_structures;
|
||||
extern crate rustc_errors;
|
||||
|
|
|
|||
|
|
@ -437,10 +437,10 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
|
|||
&& both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r))
|
||||
},
|
||||
(Enum(li, lg, le), Enum(ri, rg, re)) => {
|
||||
eq_id(*li, *ri) && eq_generics(lg, rg) && over(&le.variants, &re.variants, eq_variant)
|
||||
eq_id(*li, *ri) && eq_generics(lg, rg) && over(&le.variants, &re.variants, eq_variant)
|
||||
},
|
||||
(Struct(li, lg, lv), Struct(ri, rg, rv)) | (Union(li, lg, lv), Union(ri, rg, rv)) => {
|
||||
eq_id(*li, *ri) && eq_generics(lg, rg) && eq_variant_data(lv, rv)
|
||||
eq_id(*li, *ri) && eq_generics(lg, rg) && eq_variant_data(lv, rv)
|
||||
},
|
||||
(
|
||||
Trait(box ast::Trait {
|
||||
|
|
|
|||
|
|
@ -1565,10 +1565,10 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_
|
|||
/// Returns `true` if a pattern is refutable.
|
||||
// TODO: should be implemented using rustc/mir_build/thir machinery
|
||||
pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
||||
fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
|
||||
matches!(
|
||||
fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
|
||||
!matches!(
|
||||
cx.qpath_res(qpath, id),
|
||||
Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
|
||||
Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1585,16 +1585,18 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
|||
kind: PatExprKind::Path(qpath),
|
||||
hir_id,
|
||||
..
|
||||
}) => is_enum_variant(cx, qpath, *hir_id),
|
||||
}) => is_qpath_refutable(cx, qpath, *hir_id),
|
||||
PatKind::Or(pats) => {
|
||||
// TODO: should be the honest check, that pats is exhaustive set
|
||||
are_refutable(cx, pats)
|
||||
},
|
||||
PatKind::Tuple(pats, _) => are_refutable(cx, pats),
|
||||
PatKind::Struct(ref qpath, fields, _) => {
|
||||
is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
|
||||
is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
|
||||
},
|
||||
PatKind::TupleStruct(ref qpath, pats, _) => {
|
||||
is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
|
||||
},
|
||||
PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
|
||||
PatKind::Slice(head, middle, tail) => {
|
||||
match &cx.typeck_results().node_type(pat.hir_id).kind() {
|
||||
rustc_ty::Slice(..) => {
|
||||
|
|
|
|||
|
|
@ -393,7 +393,8 @@ fn check_terminator<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool {
|
||||
/// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV.
|
||||
pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool {
|
||||
cx.tcx.is_const_fn(def_id)
|
||||
&& cx
|
||||
.tcx
|
||||
|
|
|
|||
|
|
@ -940,7 +940,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
|
|||
// note: unable to trigger `Subslice` kind in tests
|
||||
ProjectionKind::Subslice |
|
||||
// Doesn't have surface syntax. Only occurs in patterns.
|
||||
ProjectionKind::OpaqueCast => (),
|
||||
ProjectionKind::OpaqueCast |
|
||||
// Only occurs in closure captures.
|
||||
ProjectionKind::UnwrapUnsafeBinder => (),
|
||||
ProjectionKind::Deref => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use std::time::Duration;
|
|||
use serde::Deserialize;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use crate::{Crate, LINTCHECK_DOWNLOADS, LINTCHECK_SOURCES};
|
||||
use crate::{Crate, lintcheck_sources, target_dir};
|
||||
|
||||
const DEFAULT_DOCS_LINK: &str = "https://docs.rs/{krate}/{version}/src/{krate_}/{file}.html#{line}";
|
||||
const DEFAULT_GITHUB_LINK: &str = "{url}/blob/{hash}/src/{file}#L{line}";
|
||||
|
|
@ -201,8 +201,10 @@ impl CrateWithSource {
|
|||
let file_link = &self.file_link;
|
||||
match &self.source {
|
||||
CrateSource::CratesIo { version } => {
|
||||
let extract_dir = PathBuf::from(LINTCHECK_SOURCES);
|
||||
let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS);
|
||||
let extract_dir = PathBuf::from(lintcheck_sources());
|
||||
// Keep constant downloads path to avoid repeating work and
|
||||
// filling up disk space unnecessarily.
|
||||
let krate_download_dir = PathBuf::from("target/lintcheck/downloads/");
|
||||
|
||||
// url to download the crate from crates.io
|
||||
let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download");
|
||||
|
|
@ -211,7 +213,7 @@ impl CrateWithSource {
|
|||
|
||||
let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz"));
|
||||
// don't download/extract if we already have done so
|
||||
if !krate_file_path.is_file() {
|
||||
if !krate_file_path.is_file() || !extract_dir.join(format!("{name}-{version}")).exists() {
|
||||
// create a file path to download and write the crate data into
|
||||
let mut krate_dest = fs::File::create(&krate_file_path).unwrap();
|
||||
let mut krate_req = get(&url).unwrap().into_reader();
|
||||
|
|
@ -236,7 +238,7 @@ impl CrateWithSource {
|
|||
},
|
||||
CrateSource::Git { url, commit } => {
|
||||
let repo_path = {
|
||||
let mut repo_path = PathBuf::from(LINTCHECK_SOURCES);
|
||||
let mut repo_path = PathBuf::from(lintcheck_sources());
|
||||
// add a -git suffix in case we have the same crate from crates.io and a git repo
|
||||
repo_path.push(format!("{name}-git"));
|
||||
repo_path
|
||||
|
|
@ -286,7 +288,7 @@ impl CrateWithSource {
|
|||
// copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
|
||||
// The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory
|
||||
// as a result of this filter.
|
||||
let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name);
|
||||
let dest_crate_root = PathBuf::from(lintcheck_sources()).join(name);
|
||||
if dest_crate_root.exists() {
|
||||
println!("Deleting existing directory at `{}`", dest_crate_root.display());
|
||||
fs::remove_dir_all(&dest_crate_root).unwrap();
|
||||
|
|
@ -326,15 +328,16 @@ impl CrateWithSource {
|
|||
///
|
||||
/// This function panics if creating one of the dirs fails.
|
||||
fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
|
||||
fs::create_dir("target/lintcheck/").unwrap_or_else(|err| {
|
||||
fs::create_dir(format!("{}/lintcheck/", target_dir())).unwrap_or_else(|err| {
|
||||
assert_eq!(
|
||||
err.kind(),
|
||||
ErrorKind::AlreadyExists,
|
||||
"cannot create lintcheck target dir"
|
||||
);
|
||||
});
|
||||
fs::create_dir(krate_download_dir).unwrap_or_else(|err| {
|
||||
assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir");
|
||||
fs::create_dir_all(krate_download_dir).unwrap_or_else(|err| {
|
||||
// We are allowed to reuse download dirs
|
||||
assert_ne!(err.kind(), ErrorKind::AlreadyExists);
|
||||
});
|
||||
fs::create_dir(extract_dir).unwrap_or_else(|err| {
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -43,8 +43,14 @@ use input::read_crates;
|
|||
use output::{ClippyCheckOutput, ClippyWarning, RustcIce};
|
||||
use rayon::prelude::*;
|
||||
|
||||
const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
|
||||
const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
|
||||
#[must_use]
|
||||
pub fn target_dir() -> String {
|
||||
env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned())
|
||||
}
|
||||
|
||||
fn lintcheck_sources() -> String {
|
||||
format!("{}/lintcheck/sources", target_dir())
|
||||
}
|
||||
|
||||
/// Represents the actual source code of a crate that we ran "cargo clippy" on
|
||||
#[derive(Debug)]
|
||||
|
|
@ -307,7 +313,8 @@ fn main() {
|
|||
fn lintcheck(config: LintcheckConfig) {
|
||||
let clippy_ver = build_clippy(config.perf);
|
||||
let clippy_driver_path = fs::canonicalize(format!(
|
||||
"target/{}/clippy-driver{EXE_SUFFIX}",
|
||||
"{}/{}/clippy-driver{EXE_SUFFIX}",
|
||||
target_dir(),
|
||||
if config.perf { "release" } else { "debug" }
|
||||
))
|
||||
.unwrap();
|
||||
|
|
@ -315,7 +322,8 @@ fn lintcheck(config: LintcheckConfig) {
|
|||
// assert that clippy is found
|
||||
assert!(
|
||||
clippy_driver_path.is_file(),
|
||||
"target/{}/clippy-driver binary not found! {}",
|
||||
"{}/{}/clippy-driver binary not found! {}",
|
||||
target_dir(),
|
||||
if config.perf { "release" } else { "debug" },
|
||||
clippy_driver_path.display()
|
||||
);
|
||||
|
|
@ -386,7 +394,7 @@ fn lintcheck(config: LintcheckConfig) {
|
|||
.unwrap();
|
||||
|
||||
let server = config.recursive.then(|| {
|
||||
let _: io::Result<()> = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive");
|
||||
let _: io::Result<()> = fs::remove_dir_all(format!("{}/lintcheck/shared_target_dir/recursive", target_dir()));
|
||||
|
||||
LintcheckServer::spawn(recursive_options)
|
||||
});
|
||||
|
|
@ -488,7 +496,7 @@ fn clippy_project_root() -> &'static Path {
|
|||
#[must_use]
|
||||
fn shared_target_dir(qualifier: &str) -> PathBuf {
|
||||
clippy_project_root()
|
||||
.join("target/lintcheck/shared_target_dir")
|
||||
.join(format!("{}/lintcheck/shared_target_dir", target_dir()))
|
||||
.join(qualifier)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,9 +162,9 @@ pub fn summarize_and_print_changes(
|
|||
fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
|
||||
// count lint type occurrences
|
||||
let mut counter: HashMap<&String, usize> = HashMap::new();
|
||||
warnings
|
||||
.iter()
|
||||
.for_each(|wrn| *counter.entry(&wrn.name).or_insert(0) += 1);
|
||||
for wrn in warnings {
|
||||
*counter.entry(&wrn.name).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
// collect into a tupled list for sorting
|
||||
let mut stats: Vec<(&&String, &usize)> = counter.iter().collect();
|
||||
|
|
|
|||
|
|
@ -6,4 +6,8 @@ edition = "2024"
|
|||
error_on_line_overflow = true
|
||||
imports_granularity = "Module"
|
||||
style_edition = "2024"
|
||||
ignore = ["tests/ui/crashes/ice-10912.rs"]
|
||||
ignore = [
|
||||
"tests/ui/crashes/ice-9405.rs",
|
||||
"tests/ui/crashes/ice-10912.rs",
|
||||
"tests/ui/non_expressive_names_error_recovery.rs",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
use regex::Regex;
|
||||
use std::fs;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[test]
|
||||
fn old_test_headers() {
|
||||
let old_headers = Regex::new(
|
||||
r"^//( ?\[\w+\])? ?((check|build|run|ignore|aux|only|needs|rustc|unset|no|normalize|run|compile)-|edition|incremental|revisions).*",
|
||||
)
|
||||
.unwrap();
|
||||
let mut failed = false;
|
||||
|
||||
for entry in WalkDir::new("tests") {
|
||||
let entry = entry.unwrap();
|
||||
let is_hidden_file = entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.expect("non-UTF-8 file name")
|
||||
.starts_with('.');
|
||||
if is_hidden_file || !entry.file_type().is_file() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file = fs::read_to_string(entry.path()).unwrap_or_else(|err| panic!("{}: {err}", entry.path().display()));
|
||||
|
||||
if let Some(header) = old_headers.find(&file) {
|
||||
println!("Found header `{}` in {}", header.as_str(), entry.path().display());
|
||||
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(!failed, "use `//@foo` style test headers instead");
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(clippy::useless_vec)]
|
||||
#![warn(clippy::assign_op_pattern)]
|
||||
#![feature(const_trait_impl, const_ops)]
|
||||
|
||||
use core::num::Wrapping;
|
||||
use std::ops::{Mul, MulAssign};
|
||||
|
|
@ -73,3 +74,33 @@ impl MulAssign<i64> for Wrap {
|
|||
*self = *self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
mod issue14871 {
|
||||
|
||||
use std::ops::{Add, AddAssign};
|
||||
|
||||
pub trait Number: Copy + Add<Self, Output = Self> + AddAssign {
|
||||
const ZERO: Self;
|
||||
const ONE: Self;
|
||||
}
|
||||
|
||||
#[const_trait]
|
||||
pub trait NumberConstants {
|
||||
fn constant(value: usize) -> Self;
|
||||
}
|
||||
|
||||
impl<T> const NumberConstants for T
|
||||
where
|
||||
T: Number + ~const core::ops::Add,
|
||||
{
|
||||
fn constant(value: usize) -> Self {
|
||||
let mut res = Self::ZERO;
|
||||
let mut count = 0;
|
||||
while count < value {
|
||||
res = res + Self::ONE;
|
||||
count += 1;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(clippy::useless_vec)]
|
||||
#![warn(clippy::assign_op_pattern)]
|
||||
#![feature(const_trait_impl, const_ops)]
|
||||
|
||||
use core::num::Wrapping;
|
||||
use std::ops::{Mul, MulAssign};
|
||||
|
|
@ -73,3 +74,33 @@ impl MulAssign<i64> for Wrap {
|
|||
*self = *self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
mod issue14871 {
|
||||
|
||||
use std::ops::{Add, AddAssign};
|
||||
|
||||
pub trait Number: Copy + Add<Self, Output = Self> + AddAssign {
|
||||
const ZERO: Self;
|
||||
const ONE: Self;
|
||||
}
|
||||
|
||||
#[const_trait]
|
||||
pub trait NumberConstants {
|
||||
fn constant(value: usize) -> Self;
|
||||
}
|
||||
|
||||
impl<T> const NumberConstants for T
|
||||
where
|
||||
T: Number + ~const core::ops::Add,
|
||||
{
|
||||
fn constant(value: usize) -> Self {
|
||||
let mut res = Self::ZERO;
|
||||
let mut count = 0;
|
||||
while count < value {
|
||||
res = res + Self::ONE;
|
||||
count += 1;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:9:5
|
||||
--> tests/ui/assign_ops.rs:10:5
|
||||
|
|
||||
LL | a = a + 1;
|
||||
| ^^^^^^^^^ help: replace it with: `a += 1`
|
||||
|
|
@ -8,67 +8,67 @@ LL | a = a + 1;
|
|||
= help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:11:5
|
||||
--> tests/ui/assign_ops.rs:12:5
|
||||
|
|
||||
LL | a = 1 + a;
|
||||
| ^^^^^^^^^ help: replace it with: `a += 1`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:13:5
|
||||
--> tests/ui/assign_ops.rs:14:5
|
||||
|
|
||||
LL | a = a - 1;
|
||||
| ^^^^^^^^^ help: replace it with: `a -= 1`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:15:5
|
||||
--> tests/ui/assign_ops.rs:16:5
|
||||
|
|
||||
LL | a = a * 99;
|
||||
| ^^^^^^^^^^ help: replace it with: `a *= 99`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:17:5
|
||||
--> tests/ui/assign_ops.rs:18:5
|
||||
|
|
||||
LL | a = 42 * a;
|
||||
| ^^^^^^^^^^ help: replace it with: `a *= 42`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:19:5
|
||||
--> tests/ui/assign_ops.rs:20:5
|
||||
|
|
||||
LL | a = a / 2;
|
||||
| ^^^^^^^^^ help: replace it with: `a /= 2`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:21:5
|
||||
--> tests/ui/assign_ops.rs:22:5
|
||||
|
|
||||
LL | a = a % 5;
|
||||
| ^^^^^^^^^ help: replace it with: `a %= 5`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:23:5
|
||||
--> tests/ui/assign_ops.rs:24:5
|
||||
|
|
||||
LL | a = a & 1;
|
||||
| ^^^^^^^^^ help: replace it with: `a &= 1`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:30:5
|
||||
--> tests/ui/assign_ops.rs:31:5
|
||||
|
|
||||
LL | s = s + "bla";
|
||||
| ^^^^^^^^^^^^^ help: replace it with: `s += "bla"`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:35:5
|
||||
--> tests/ui/assign_ops.rs:36:5
|
||||
|
|
||||
LL | a = a + Wrapping(1u32);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:38:5
|
||||
--> tests/ui/assign_ops.rs:39:5
|
||||
|
|
||||
LL | v[0] = v[0] + v[1];
|
||||
| ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> tests/ui/assign_ops.rs:51:5
|
||||
--> tests/ui/assign_ops.rs:52:5
|
||||
|
|
||||
LL | buf = buf + cows.clone();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()`
|
||||
|
|
|
|||
|
|
@ -123,3 +123,19 @@ mod issue12131 {
|
|||
//~^ dbg_macro
|
||||
}
|
||||
}
|
||||
|
||||
mod issue14914 {
|
||||
use std::future::Future;
|
||||
|
||||
fn takes_async_fn<F, Fut>(_f: F)
|
||||
where
|
||||
F: FnOnce(i32) -> Fut,
|
||||
Fut: Future<Output = i32>,
|
||||
{
|
||||
}
|
||||
|
||||
fn should_not_panic() {
|
||||
takes_async_fn(async |val| val);
|
||||
//~^ dbg_macro
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,3 +123,19 @@ mod issue12131 {
|
|||
//~^ dbg_macro
|
||||
}
|
||||
}
|
||||
|
||||
mod issue14914 {
|
||||
use std::future::Future;
|
||||
|
||||
fn takes_async_fn<F, Fut>(_f: F)
|
||||
where
|
||||
F: FnOnce(i32) -> Fut,
|
||||
Fut: Future<Output = i32>,
|
||||
{
|
||||
}
|
||||
|
||||
fn should_not_panic() {
|
||||
takes_async_fn(async |val| dbg!(val));
|
||||
//~^ dbg_macro
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,5 +230,17 @@ LL - print!("{}", dbg!(s));
|
|||
LL + print!("{}", s);
|
||||
|
|
||||
|
||||
error: aborting due to 19 previous errors
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:138:36
|
||||
|
|
||||
LL | takes_async_fn(async |val| dbg!(val));
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL - takes_async_fn(async |val| dbg!(val));
|
||||
LL + takes_async_fn(async |val| val);
|
||||
|
|
||||
|
||||
error: aborting due to 20 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
#![expect(incomplete_features)] // `unsafe_fields` is incomplete for the time being
|
||||
#![feature(unsafe_fields)] // `clone()` cannot be derived automatically on unsafe fields
|
||||
|
||||
|
||||
#[derive(Copy)]
|
||||
struct Qux;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:16:1
|
||||
--> tests/ui/derive.rs:15:1
|
||||
|
|
||||
LL | / impl Clone for Qux {
|
||||
LL | |
|
||||
|
|
@ -10,7 +10,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:16:1
|
||||
--> tests/ui/derive.rs:15:1
|
||||
|
|
||||
LL | / impl Clone for Qux {
|
||||
LL | |
|
||||
|
|
@ -23,7 +23,7 @@ LL | | }
|
|||
= help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]`
|
||||
|
||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:42:1
|
||||
--> tests/ui/derive.rs:41:1
|
||||
|
|
||||
LL | / impl<'a> Clone for Lt<'a> {
|
||||
LL | |
|
||||
|
|
@ -34,7 +34,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:42:1
|
||||
--> tests/ui/derive.rs:41:1
|
||||
|
|
||||
LL | / impl<'a> Clone for Lt<'a> {
|
||||
LL | |
|
||||
|
|
@ -45,7 +45,7 @@ LL | | }
|
|||
| |_^
|
||||
|
||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:55:1
|
||||
--> tests/ui/derive.rs:54:1
|
||||
|
|
||||
LL | / impl Clone for BigArray {
|
||||
LL | |
|
||||
|
|
@ -56,7 +56,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:55:1
|
||||
--> tests/ui/derive.rs:54:1
|
||||
|
|
||||
LL | / impl Clone for BigArray {
|
||||
LL | |
|
||||
|
|
@ -67,7 +67,7 @@ LL | | }
|
|||
| |_^
|
||||
|
||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:68:1
|
||||
--> tests/ui/derive.rs:67:1
|
||||
|
|
||||
LL | / impl Clone for FnPtr {
|
||||
LL | |
|
||||
|
|
@ -78,7 +78,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:68:1
|
||||
--> tests/ui/derive.rs:67:1
|
||||
|
|
||||
LL | / impl Clone for FnPtr {
|
||||
LL | |
|
||||
|
|
@ -89,7 +89,7 @@ LL | | }
|
|||
| |_^
|
||||
|
||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||
--> tests/ui/derive.rs:90:1
|
||||
--> tests/ui/derive.rs:89:1
|
||||
|
|
||||
LL | / impl<T: Clone> Clone for Generic2<T> {
|
||||
LL | |
|
||||
|
|
@ -100,7 +100,7 @@ LL | | }
|
|||
| |_^
|
||||
|
|
||||
note: consider deriving `Clone` or removing `Copy`
|
||||
--> tests/ui/derive.rs:90:1
|
||||
--> tests/ui/derive.rs:89:1
|
||||
|
|
||||
LL | / impl<T: Clone> Clone for Generic2<T> {
|
||||
LL | |
|
||||
|
|
|
|||
|
|
@ -1,11 +1,37 @@
|
|||
// This test checks that words starting with capital letters and ending with "ified" don't
|
||||
// trigger the lint.
|
||||
|
||||
#![deny(clippy::doc_markdown)]
|
||||
#![allow(clippy::doc_lazy_continuation)]
|
||||
|
||||
mod issue13097 {
|
||||
// This test checks that words starting with capital letters and ending with "ified" don't
|
||||
// trigger the lint.
|
||||
pub enum OutputFormat {
|
||||
/// `HumaNified`
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
Plain,
|
||||
// Should not warn!
|
||||
/// JSONified console output
|
||||
Json,
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub enum OutputFormat {
|
||||
/// `HumaNified`
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
/**
|
||||
* `HumaNified`
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* Before \u{08888} `HumaNified` \{u08888} After
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* meow meow \[`meow_meow`\] meow meow?
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* \u{08888} `meow_meow` \[meow meow] meow?
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* Above
|
||||
* \u{08888}
|
||||
* \[hi\](<https://example.com>) `HumaNified` \[example](<https://example.com>)
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* \u{08888}
|
||||
* Below
|
||||
*/
|
||||
Plain,
|
||||
// Should not warn!
|
||||
/// JSONified console output
|
||||
|
|
|
|||
|
|
@ -1,11 +1,37 @@
|
|||
// This test checks that words starting with capital letters and ending with "ified" don't
|
||||
// trigger the lint.
|
||||
|
||||
#![deny(clippy::doc_markdown)]
|
||||
#![allow(clippy::doc_lazy_continuation)]
|
||||
|
||||
mod issue13097 {
|
||||
// This test checks that words starting with capital letters and ending with "ified" don't
|
||||
// trigger the lint.
|
||||
pub enum OutputFormat {
|
||||
/// HumaNified
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
Plain,
|
||||
// Should not warn!
|
||||
/// JSONified console output
|
||||
Json,
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub enum OutputFormat {
|
||||
/// HumaNified
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
/**
|
||||
* HumaNified
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* Before \u{08888} HumaNified \{u08888} After
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* meow meow \[meow_meow\] meow meow?
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* \u{08888} meow_meow \[meow meow] meow?
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* Above
|
||||
* \u{08888}
|
||||
* \[hi\](<https://example.com>) HumaNified \[example](<https://example.com>)
|
||||
//~^ ERROR: item in documentation is missing backticks
|
||||
* \u{08888}
|
||||
* Below
|
||||
*/
|
||||
Plain,
|
||||
// Should not warn!
|
||||
/// JSONified console output
|
||||
|
|
|
|||
|
|
@ -1,19 +1,79 @@
|
|||
error: item in documentation is missing backticks
|
||||
--> tests/ui/doc/doc_markdown-issue_13097.rs:7:9
|
||||
--> tests/ui/doc/doc_markdown-issue_13097.rs:8:13
|
||||
|
|
||||
LL | /// HumaNified
|
||||
| ^^^^^^^^^^
|
||||
LL | /// HumaNified
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> tests/ui/doc/doc_markdown-issue_13097.rs:4:9
|
||||
--> tests/ui/doc/doc_markdown-issue_13097.rs:1:9
|
||||
|
|
||||
LL | #![deny(clippy::doc_markdown)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: try
|
||||
|
|
||||
LL - /// HumaNified
|
||||
LL + /// `HumaNified`
|
||||
LL - /// HumaNified
|
||||
LL + /// `HumaNified`
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: item in documentation is missing backticks
|
||||
--> tests/ui/doc/doc_markdown-issue_13097.rs:20:8
|
||||
|
|
||||
LL | * HumaNified
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - * HumaNified
|
||||
LL + * `HumaNified`
|
||||
|
|
||||
|
||||
error: item in documentation is missing backticks
|
||||
--> tests/ui/doc/doc_markdown-issue_13097.rs:22:25
|
||||
|
|
||||
LL | * Before \u{08888} HumaNified \{u08888} After
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - * Before \u{08888} HumaNified \{u08888} After
|
||||
LL + * Before \u{08888} `HumaNified` \{u08888} After
|
||||
|
|
||||
|
||||
error: item in documentation is missing backticks
|
||||
--> tests/ui/doc/doc_markdown-issue_13097.rs:24:20
|
||||
|
|
||||
LL | * meow meow \[meow_meow\] meow meow?
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - * meow meow \[meow_meow\] meow meow?
|
||||
LL + * meow meow \[`meow_meow`\] meow meow?
|
||||
|
|
||||
|
||||
error: item in documentation is missing backticks
|
||||
--> tests/ui/doc/doc_markdown-issue_13097.rs:26:18
|
||||
|
|
||||
LL | * \u{08888} meow_meow \[meow meow] meow?
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - * \u{08888} meow_meow \[meow meow] meow?
|
||||
LL + * \u{08888} `meow_meow` \[meow meow] meow?
|
||||
|
|
||||
|
||||
error: item in documentation is missing backticks
|
||||
--> tests/ui/doc/doc_markdown-issue_13097.rs:30:38
|
||||
|
|
||||
LL | * \[hi\](<https://example.com>) HumaNified \[example](<https://example.com>)
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - * \[hi\](<https://example.com>) HumaNified \[example](<https://example.com>)
|
||||
LL + * \[hi\](<https://example.com>) `HumaNified` \[example](<https://example.com>)
|
||||
|
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -81,12 +81,10 @@ fn main() {
|
|||
let b: String = concat(just_return(a));
|
||||
//~^ explicit_deref_methods
|
||||
|
||||
let b: &str = &**a;
|
||||
//~^ explicit_deref_methods
|
||||
let b: &str = a.deref().deref();
|
||||
|
||||
let opt_a = Some(a.clone());
|
||||
let b = &*opt_a.unwrap();
|
||||
//~^ explicit_deref_methods
|
||||
let b = opt_a.unwrap().deref();
|
||||
|
||||
Aaa::deref(&Aaa);
|
||||
Aaa::deref_mut(&mut Aaa);
|
||||
|
|
|
|||
|
|
@ -82,11 +82,9 @@ fn main() {
|
|||
//~^ explicit_deref_methods
|
||||
|
||||
let b: &str = a.deref().deref();
|
||||
//~^ explicit_deref_methods
|
||||
|
||||
let opt_a = Some(a.clone());
|
||||
let b = opt_a.unwrap().deref();
|
||||
//~^ explicit_deref_methods
|
||||
|
||||
Aaa::deref(&Aaa);
|
||||
Aaa::deref_mut(&mut Aaa);
|
||||
|
|
|
|||
|
|
@ -56,40 +56,28 @@ LL | let b: String = concat(just_return(a).deref());
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)`
|
||||
|
||||
error: explicit `deref` method call
|
||||
--> tests/ui/explicit_deref_methods.rs:84:19
|
||||
|
|
||||
LL | let b: &str = a.deref().deref();
|
||||
| ^^^^^^^^^^^^^^^^^ help: try: `&**a`
|
||||
|
||||
error: explicit `deref` method call
|
||||
--> tests/ui/explicit_deref_methods.rs:88:13
|
||||
|
|
||||
LL | let b = opt_a.unwrap().deref();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*opt_a.unwrap()`
|
||||
|
||||
error: explicit `deref` method call
|
||||
--> tests/ui/explicit_deref_methods.rs:123:31
|
||||
--> tests/ui/explicit_deref_methods.rs:121:31
|
||||
|
|
||||
LL | let b: &str = expr_deref!(a.deref());
|
||||
| ^^^^^^^^^ help: try: `&*a`
|
||||
|
||||
error: explicit `deref` method call
|
||||
--> tests/ui/explicit_deref_methods.rs:141:14
|
||||
--> tests/ui/explicit_deref_methods.rs:139:14
|
||||
|
|
||||
LL | let _ = &Deref::deref(&"foo");
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"`
|
||||
|
||||
error: explicit `deref_mut` method call
|
||||
--> tests/ui/explicit_deref_methods.rs:143:14
|
||||
--> tests/ui/explicit_deref_methods.rs:141:14
|
||||
|
|
||||
LL | let _ = &DerefMut::deref_mut(&mut x);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x`
|
||||
|
||||
error: explicit `deref_mut` method call
|
||||
--> tests/ui/explicit_deref_methods.rs:144:14
|
||||
--> tests/ui/explicit_deref_methods.rs:142:14
|
||||
|
|
||||
LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)`
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -179,3 +179,13 @@ fn two_bindings(v: Vec<(u8, u8)>) -> Option<u8> {
|
|||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
mod issue14826 {
|
||||
fn adjust_fixable(needle: &str) -> Option<&'static str> {
|
||||
["foo", "bar"].iter().find(|&candidate| candidate.eq_ignore_ascii_case(needle)).map(|v| v as _)
|
||||
}
|
||||
|
||||
fn adjust_unfixable(needle: &str) -> Option<*const str> {
|
||||
["foo", "bar"].iter().find(|&&candidate| candidate.eq_ignore_ascii_case(needle)).copied().map(|v| v as _)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,3 +251,25 @@ fn two_bindings(v: Vec<(u8, u8)>) -> Option<u8> {
|
|||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
mod issue14826 {
|
||||
fn adjust_fixable(needle: &str) -> Option<&'static str> {
|
||||
for candidate in &["foo", "bar"] {
|
||||
//~^ manual_find
|
||||
if candidate.eq_ignore_ascii_case(needle) {
|
||||
return Some(candidate);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn adjust_unfixable(needle: &str) -> Option<*const str> {
|
||||
for &candidate in &["foo", "bar"] {
|
||||
//~^ manual_find
|
||||
if candidate.eq_ignore_ascii_case(needle) {
|
||||
return Some(candidate);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,5 +139,27 @@ LL | | return Some(x);
|
|||
LL | | None
|
||||
| |____________^ help: replace with an iterator: `arr.into_iter().find(|&x| x < 1)`
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error: manual implementation of `Iterator::find`
|
||||
--> tests/ui/manual_find_fixable.rs:257:9
|
||||
|
|
||||
LL | / for candidate in &["foo", "bar"] {
|
||||
LL | |
|
||||
LL | | if candidate.eq_ignore_ascii_case(needle) {
|
||||
LL | | return Some(candidate);
|
||||
... |
|
||||
LL | | None
|
||||
| |____________^ help: replace with an iterator: `["foo", "bar"].iter().find(|&candidate| candidate.eq_ignore_ascii_case(needle)).map(|v| v as _)`
|
||||
|
||||
error: manual implementation of `Iterator::find`
|
||||
--> tests/ui/manual_find_fixable.rs:267:9
|
||||
|
|
||||
LL | / for &candidate in &["foo", "bar"] {
|
||||
LL | |
|
||||
LL | | if candidate.eq_ignore_ascii_case(needle) {
|
||||
LL | | return Some(candidate);
|
||||
... |
|
||||
LL | | None
|
||||
| |____________^ help: replace with an iterator: `["foo", "bar"].iter().find(|&&candidate| candidate.eq_ignore_ascii_case(needle)).copied().map(|v| v as _)`
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,13 @@ fn main() {
|
|||
println!("{}", n);
|
||||
}
|
||||
|
||||
// Using nested `Some` pattern should not trigger the lint
|
||||
for n in vec![Some((1, Some(2)))] {
|
||||
if let Some((_, Some(n))) = n {
|
||||
println!("{}", n);
|
||||
}
|
||||
}
|
||||
|
||||
run_unformatted_tests();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ LL | | }
|
|||
| |_________^
|
||||
|
||||
error: unnecessary `if let` since only the `Some` variant of the iterator element is used
|
||||
--> tests/ui/manual_flatten.rs:132:5
|
||||
--> tests/ui/manual_flatten.rs:139:5
|
||||
|
|
||||
LL | / for n in vec![
|
||||
LL | |
|
||||
|
|
@ -189,7 +189,7 @@ LL | | }
|
|||
| |_____^
|
||||
|
|
||||
help: remove the `if let` statement in the for loop and then...
|
||||
--> tests/ui/manual_flatten.rs:139:9
|
||||
--> tests/ui/manual_flatten.rs:146:9
|
||||
|
|
||||
LL | / if let Some(n) = n {
|
||||
LL | | println!("{:?}", n);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,44 @@
|
|||
#[macro_use]
|
||||
extern crate option_helpers;
|
||||
|
||||
struct Foo<T>(T);
|
||||
|
||||
impl<T> Foo<T> {
|
||||
fn map<F: FnMut(T) -> bool>(self, mut f: F) -> Option<bool> {
|
||||
Some(f(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
fn foo() -> Option<bool> {
|
||||
Some(true)
|
||||
}
|
||||
|
||||
macro_rules! some_true {
|
||||
() => {
|
||||
Some(true)
|
||||
};
|
||||
}
|
||||
macro_rules! some_false {
|
||||
() => {
|
||||
Some(false)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mac {
|
||||
(some $e:expr) => {
|
||||
Some($e)
|
||||
};
|
||||
(some_map $e:expr) => {
|
||||
Some($e).map(|x| x % 2 == 0)
|
||||
};
|
||||
(map $e:expr) => {
|
||||
$e.map(|x| x % 2 == 0)
|
||||
};
|
||||
(eq $a:expr, $b:expr) => {
|
||||
$a == $b
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn option_methods() {
|
||||
let opt = Some(1);
|
||||
|
|
@ -21,6 +59,15 @@ fn option_methods() {
|
|||
let _ = opt
|
||||
.is_some_and(|x| x > 1);
|
||||
|
||||
let _ = Some(2).is_some_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).is_none_or(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).is_some_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).is_none_or(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
|
||||
// won't fix because the return type of the closure is not `bool`
|
||||
let _ = opt.map(|x| x + 1).unwrap_or_default();
|
||||
|
||||
|
|
@ -28,6 +75,14 @@ fn option_methods() {
|
|||
let _ = opt2.is_some_and(char::is_alphanumeric); // should lint
|
||||
//~^ manual_is_variant_and
|
||||
let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
|
||||
|
||||
// Should not lint.
|
||||
let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
|
||||
let _ = Some(2).map(|x| x % 2 == 0) != foo();
|
||||
let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
|
||||
let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
|
||||
let _ = mac!(some_map 2) == Some(true);
|
||||
let _ = mac!(map Some(2)) == Some(true);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -41,6 +96,13 @@ fn result_methods() {
|
|||
});
|
||||
let _ = res.is_ok_and(|x| x > 1);
|
||||
|
||||
let _ = Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
|
||||
//~^ manual_is_variant_and
|
||||
|
||||
// won't fix because the return type of the closure is not `bool`
|
||||
let _ = res.map(|x| x + 1).unwrap_or_default();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,44 @@
|
|||
#[macro_use]
|
||||
extern crate option_helpers;
|
||||
|
||||
struct Foo<T>(T);
|
||||
|
||||
impl<T> Foo<T> {
|
||||
fn map<F: FnMut(T) -> bool>(self, mut f: F) -> Option<bool> {
|
||||
Some(f(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
fn foo() -> Option<bool> {
|
||||
Some(true)
|
||||
}
|
||||
|
||||
macro_rules! some_true {
|
||||
() => {
|
||||
Some(true)
|
||||
};
|
||||
}
|
||||
macro_rules! some_false {
|
||||
() => {
|
||||
Some(false)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mac {
|
||||
(some $e:expr) => {
|
||||
Some($e)
|
||||
};
|
||||
(some_map $e:expr) => {
|
||||
Some($e).map(|x| x % 2 == 0)
|
||||
};
|
||||
(map $e:expr) => {
|
||||
$e.map(|x| x % 2 == 0)
|
||||
};
|
||||
(eq $a:expr, $b:expr) => {
|
||||
$a == $b
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn option_methods() {
|
||||
let opt = Some(1);
|
||||
|
|
@ -27,6 +65,15 @@ fn option_methods() {
|
|||
//~^ manual_is_variant_and
|
||||
.unwrap_or_default();
|
||||
|
||||
let _ = Some(2).map(|x| x % 2 == 0) == Some(true);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).map(|x| x % 2 == 0) != Some(true);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).map(|x| x % 2 == 0) == some_true!();
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Some(2).map(|x| x % 2 == 0) != some_false!();
|
||||
//~^ manual_is_variant_and
|
||||
|
||||
// won't fix because the return type of the closure is not `bool`
|
||||
let _ = opt.map(|x| x + 1).unwrap_or_default();
|
||||
|
||||
|
|
@ -34,6 +81,14 @@ fn option_methods() {
|
|||
let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint
|
||||
//~^ manual_is_variant_and
|
||||
let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
|
||||
|
||||
// Should not lint.
|
||||
let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
|
||||
let _ = Some(2).map(|x| x % 2 == 0) != foo();
|
||||
let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
|
||||
let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
|
||||
let _ = mac!(some_map 2) == Some(true);
|
||||
let _ = mac!(map Some(2)) == Some(true);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -50,6 +105,13 @@ fn result_methods() {
|
|||
//~^ manual_is_variant_and
|
||||
.unwrap_or_default();
|
||||
|
||||
let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
|
||||
//~^ manual_is_variant_and
|
||||
let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
|
||||
//~^ manual_is_variant_and
|
||||
|
||||
// won't fix because the return type of the closure is not `bool`
|
||||
let _ = res.map(|x| x + 1).unwrap_or_default();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:13:17
|
||||
--> tests/ui/manual_is_variant_and.rs:51:17
|
||||
|
|
||||
LL | let _ = opt.map(|x| x > 1)
|
||||
| _________________^
|
||||
|
|
@ -11,7 +11,7 @@ LL | | .unwrap_or_default();
|
|||
= help: to override `-D warnings` add `#[allow(clippy::manual_is_variant_and)]`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:18:17
|
||||
--> tests/ui/manual_is_variant_and.rs:56:17
|
||||
|
|
||||
LL | let _ = opt.map(|x| {
|
||||
| _________________^
|
||||
|
|
@ -30,13 +30,13 @@ LL ~ });
|
|||
|
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:23:17
|
||||
--> tests/ui/manual_is_variant_and.rs:61:17
|
||||
|
|
||||
LL | let _ = opt.map(|x| x > 1).unwrap_or_default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(|x| x > 1)`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:26:10
|
||||
--> tests/ui/manual_is_variant_and.rs:64:10
|
||||
|
|
||||
LL | .map(|x| x > 1)
|
||||
| __________^
|
||||
|
|
@ -44,14 +44,38 @@ LL | |
|
|||
LL | | .unwrap_or_default();
|
||||
| |____________________________^ help: use: `is_some_and(|x| x > 1)`
|
||||
|
||||
error: called `.map() == Some()`
|
||||
--> tests/ui/manual_is_variant_and.rs:68:13
|
||||
|
|
||||
LL | let _ = Some(2).map(|x| x % 2 == 0) == Some(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_some_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() != Some()`
|
||||
--> tests/ui/manual_is_variant_and.rs:70:13
|
||||
|
|
||||
LL | let _ = Some(2).map(|x| x % 2 == 0) != Some(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() == Some()`
|
||||
--> tests/ui/manual_is_variant_and.rs:72:13
|
||||
|
|
||||
LL | let _ = Some(2).map(|x| x % 2 == 0) == some_true!();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_some_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() != Some()`
|
||||
--> tests/ui/manual_is_variant_and.rs:74:13
|
||||
|
|
||||
LL | let _ = Some(2).map(|x| x % 2 == 0) != some_false!();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on an `Option` value
|
||||
--> tests/ui/manual_is_variant_and.rs:34:18
|
||||
--> tests/ui/manual_is_variant_and.rs:81:18
|
||||
|
|
||||
LL | let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(char::is_alphanumeric)`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on a `Result` value
|
||||
--> tests/ui/manual_is_variant_and.rs:44:17
|
||||
--> tests/ui/manual_is_variant_and.rs:99:17
|
||||
|
|
||||
LL | let _ = res.map(|x| {
|
||||
| _________________^
|
||||
|
|
@ -70,7 +94,7 @@ LL ~ });
|
|||
|
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on a `Result` value
|
||||
--> tests/ui/manual_is_variant_and.rs:49:17
|
||||
--> tests/ui/manual_is_variant_and.rs:104:17
|
||||
|
|
||||
LL | let _ = res.map(|x| x > 1)
|
||||
| _________________^
|
||||
|
|
@ -78,11 +102,29 @@ LL | |
|
|||
LL | | .unwrap_or_default();
|
||||
| |____________________________^ help: use: `is_ok_and(|x| x > 1)`
|
||||
|
||||
error: called `.map() == Ok()`
|
||||
--> tests/ui/manual_is_variant_and.rs:108:13
|
||||
|
|
||||
LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() != Ok()`
|
||||
--> tests/ui/manual_is_variant_and.rs:110:13
|
||||
|
|
||||
LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `.map() != Ok()`
|
||||
--> tests/ui/manual_is_variant_and.rs:112:13
|
||||
|
|
||||
LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
|
||||
|
||||
error: called `map(<f>).unwrap_or_default()` on a `Result` value
|
||||
--> tests/ui/manual_is_variant_and.rs:57:18
|
||||
--> tests/ui/manual_is_variant_and.rs:119:18
|
||||
|
|
||||
LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: aborting due to 15 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -107,9 +107,6 @@ fn main() {
|
|||
let x = (1, 2);
|
||||
let _ = x.0;
|
||||
//~^ needless_borrow
|
||||
let x = &x as *const (i32, i32);
|
||||
let _ = unsafe { (*x).0 };
|
||||
//~^ needless_borrow
|
||||
|
||||
// Issue #8367
|
||||
trait Foo {
|
||||
|
|
@ -289,3 +286,15 @@ fn issue_12268() {
|
|||
|
||||
// compiler
|
||||
}
|
||||
|
||||
fn issue_14743<T>(slice: &[T]) {
|
||||
let _ = slice.len();
|
||||
//~^ needless_borrow
|
||||
|
||||
let slice = slice as *const [T];
|
||||
let _ = unsafe { (&*slice).len() };
|
||||
|
||||
// Check that rustc would actually warn if Clippy had suggested removing the reference
|
||||
#[expect(dangerous_implicit_autorefs)]
|
||||
let _ = unsafe { (*slice).len() };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,9 +107,6 @@ fn main() {
|
|||
let x = (1, 2);
|
||||
let _ = (&x).0;
|
||||
//~^ needless_borrow
|
||||
let x = &x as *const (i32, i32);
|
||||
let _ = unsafe { (&*x).0 };
|
||||
//~^ needless_borrow
|
||||
|
||||
// Issue #8367
|
||||
trait Foo {
|
||||
|
|
@ -289,3 +286,15 @@ fn issue_12268() {
|
|||
|
||||
// compiler
|
||||
}
|
||||
|
||||
fn issue_14743<T>(slice: &[T]) {
|
||||
let _ = (&slice).len();
|
||||
//~^ needless_borrow
|
||||
|
||||
let slice = slice as *const [T];
|
||||
let _ = unsafe { (&*slice).len() };
|
||||
|
||||
// Check that rustc would actually warn if Clippy had suggested removing the reference
|
||||
#[expect(dangerous_implicit_autorefs)]
|
||||
let _ = unsafe { (*slice).len() };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,71 +103,71 @@ error: this expression borrows a value the compiler would automatically borrow
|
|||
LL | let _ = (&x).0;
|
||||
| ^^^^ help: change this to: `x`
|
||||
|
||||
error: this expression borrows a value the compiler would automatically borrow
|
||||
--> tests/ui/needless_borrow.rs:111:22
|
||||
|
|
||||
LL | let _ = unsafe { (&*x).0 };
|
||||
| ^^^^^ help: change this to: `(*x)`
|
||||
|
||||
error: this expression creates a reference which is immediately dereferenced by the compiler
|
||||
--> tests/ui/needless_borrow.rs:122:5
|
||||
--> tests/ui/needless_borrow.rs:119:5
|
||||
|
|
||||
LL | (&&()).foo();
|
||||
| ^^^^^^ help: change this to: `(&())`
|
||||
|
||||
error: this expression creates a reference which is immediately dereferenced by the compiler
|
||||
--> tests/ui/needless_borrow.rs:132:5
|
||||
--> tests/ui/needless_borrow.rs:129:5
|
||||
|
|
||||
LL | (&&5).foo();
|
||||
| ^^^^^ help: change this to: `(&5)`
|
||||
|
||||
error: this expression creates a reference which is immediately dereferenced by the compiler
|
||||
--> tests/ui/needless_borrow.rs:159:23
|
||||
--> tests/ui/needless_borrow.rs:156:23
|
||||
|
|
||||
LL | let x: (&str,) = (&"",);
|
||||
| ^^^ help: change this to: `""`
|
||||
|
||||
error: this expression borrows a value the compiler would automatically borrow
|
||||
--> tests/ui/needless_borrow.rs:202:13
|
||||
--> tests/ui/needless_borrow.rs:199:13
|
||||
|
|
||||
LL | (&self.f)()
|
||||
| ^^^^^^^^^ help: change this to: `(self.f)`
|
||||
|
||||
error: this expression borrows a value the compiler would automatically borrow
|
||||
--> tests/ui/needless_borrow.rs:212:13
|
||||
--> tests/ui/needless_borrow.rs:209:13
|
||||
|
|
||||
LL | (&mut self.f)()
|
||||
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
|
||||
|
||||
error: this expression borrows a value the compiler would automatically borrow
|
||||
--> tests/ui/needless_borrow.rs:250:22
|
||||
--> tests/ui/needless_borrow.rs:247:22
|
||||
|
|
||||
LL | let _ = &mut (&mut { x.u }).x;
|
||||
| ^^^^^^^^^^^^^^ help: change this to: `{ x.u }`
|
||||
|
||||
error: this expression borrows a value the compiler would automatically borrow
|
||||
--> tests/ui/needless_borrow.rs:258:22
|
||||
--> tests/ui/needless_borrow.rs:255:22
|
||||
|
|
||||
LL | let _ = &mut (&mut { x.u }).x;
|
||||
| ^^^^^^^^^^^^^^ help: change this to: `{ x.u }`
|
||||
|
||||
error: this expression borrows a value the compiler would automatically borrow
|
||||
--> tests/ui/needless_borrow.rs:263:22
|
||||
--> tests/ui/needless_borrow.rs:260:22
|
||||
|
|
||||
LL | let _ = &mut (&mut x.u).x;
|
||||
| ^^^^^^^^^^ help: change this to: `x.u`
|
||||
|
||||
error: this expression borrows a value the compiler would automatically borrow
|
||||
--> tests/ui/needless_borrow.rs:265:22
|
||||
--> tests/ui/needless_borrow.rs:262:22
|
||||
|
|
||||
LL | let _ = &mut (&mut { x.u }).x;
|
||||
| ^^^^^^^^^^^^^^ help: change this to: `{ x.u }`
|
||||
|
||||
error: this expression creates a reference which is immediately dereferenced by the compiler
|
||||
--> tests/ui/needless_borrow.rs:287:23
|
||||
--> tests/ui/needless_borrow.rs:284:23
|
||||
|
|
||||
LL | option.unwrap_or((&x.0,));
|
||||
| ^^^^ help: change this to: `x.0`
|
||||
|
||||
error: this expression creates a reference which is immediately dereferenced by the compiler
|
||||
--> tests/ui/needless_borrow.rs:291:13
|
||||
|
|
||||
LL | let _ = (&slice).len();
|
||||
| ^^^^^^^^ help: change this to: `slice`
|
||||
|
||||
error: aborting due to 28 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// With an explicit return type it should lint too
|
||||
/// ```edition2015
|
||||
/// fn main() -> () {
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// This should, too.
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// This one too.
|
||||
/// ```no_run
|
||||
/// // the fn is not always the first line
|
||||
|
|
|
|||
|
|
@ -128,3 +128,18 @@ fn should_not_lint() {
|
|||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
mod issue14734 {
|
||||
fn let_desugar(rows: &[u8]) {
|
||||
let mut v = vec![];
|
||||
for x in rows.iter() { _ = v.push(x) }
|
||||
//~^ needless_for_each
|
||||
}
|
||||
|
||||
fn do_something(_: &u8, _: u8) {}
|
||||
|
||||
fn single_expr(rows: &[u8]) {
|
||||
for x in rows.iter() { do_something(x, 1u8); }
|
||||
//~^ needless_for_each
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,3 +128,18 @@ fn should_not_lint() {
|
|||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
mod issue14734 {
|
||||
fn let_desugar(rows: &[u8]) {
|
||||
let mut v = vec![];
|
||||
rows.iter().for_each(|x| _ = v.push(x));
|
||||
//~^ needless_for_each
|
||||
}
|
||||
|
||||
fn do_something(_: &u8, _: u8) {}
|
||||
|
||||
fn single_expr(rows: &[u8]) {
|
||||
rows.iter().for_each(|x| do_something(x, 1u8));
|
||||
//~^ needless_for_each
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,5 +136,17 @@ LL + acc += elem;
|
|||
LL + }
|
||||
|
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: needless use of `for_each`
|
||||
--> tests/ui/needless_for_each_fixable.rs:135:9
|
||||
|
|
||||
LL | rows.iter().for_each(|x| _ = v.push(x));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in rows.iter() { _ = v.push(x) }`
|
||||
|
||||
error: needless use of `for_each`
|
||||
--> tests/ui/needless_for_each_fixable.rs:142:9
|
||||
|
|
||||
LL | rows.iter().for_each(|x| do_something(x, 1u8));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in rows.iter() { do_something(x, 1u8); }`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -452,3 +452,68 @@ pub unsafe fn issue_12157() -> *const i32 {
|
|||
(unsafe { todo() } as *const i32)
|
||||
//~^ needless_return
|
||||
}
|
||||
|
||||
mod else_ifs {
|
||||
fn test1(a: i32) -> u32 {
|
||||
if a == 0 {
|
||||
1
|
||||
//~^ needless_return
|
||||
} else if a < 10 {
|
||||
2
|
||||
//~^ needless_return
|
||||
} else {
|
||||
3
|
||||
//~^ needless_return
|
||||
}
|
||||
}
|
||||
|
||||
fn test2(a: i32) -> u32 {
|
||||
if a == 0 {
|
||||
1
|
||||
//~^ needless_return
|
||||
} else if a < 10 {
|
||||
2
|
||||
} else {
|
||||
3
|
||||
//~^ needless_return
|
||||
}
|
||||
}
|
||||
|
||||
fn test3(a: i32) -> u32 {
|
||||
if a == 0 {
|
||||
1
|
||||
//~^ needless_return
|
||||
} else if a < 10 {
|
||||
2
|
||||
} else {
|
||||
3
|
||||
//~^ needless_return
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::match_single_binding, clippy::redundant_pattern)]
|
||||
fn test4(a: i32) -> u32 {
|
||||
if a == 0 {
|
||||
1
|
||||
//~^ needless_return
|
||||
} else if if if a > 0x1_1 {
|
||||
return 2;
|
||||
} else {
|
||||
return 5;
|
||||
} {
|
||||
true
|
||||
} else {
|
||||
true
|
||||
} {
|
||||
0xDEADC0DE
|
||||
} else if match a {
|
||||
b @ _ => {
|
||||
return 1;
|
||||
},
|
||||
} {
|
||||
0xDEADBEEF
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -461,3 +461,68 @@ pub unsafe fn issue_12157() -> *const i32 {
|
|||
return unsafe { todo() } as *const i32;
|
||||
//~^ needless_return
|
||||
}
|
||||
|
||||
mod else_ifs {
|
||||
fn test1(a: i32) -> u32 {
|
||||
if a == 0 {
|
||||
return 1;
|
||||
//~^ needless_return
|
||||
} else if a < 10 {
|
||||
return 2;
|
||||
//~^ needless_return
|
||||
} else {
|
||||
return 3;
|
||||
//~^ needless_return
|
||||
}
|
||||
}
|
||||
|
||||
fn test2(a: i32) -> u32 {
|
||||
if a == 0 {
|
||||
return 1;
|
||||
//~^ needless_return
|
||||
} else if a < 10 {
|
||||
2
|
||||
} else {
|
||||
return 3;
|
||||
//~^ needless_return
|
||||
}
|
||||
}
|
||||
|
||||
fn test3(a: i32) -> u32 {
|
||||
if a == 0 {
|
||||
return 1;
|
||||
//~^ needless_return
|
||||
} else if a < 10 {
|
||||
2
|
||||
} else {
|
||||
return 3;
|
||||
//~^ needless_return
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::match_single_binding, clippy::redundant_pattern)]
|
||||
fn test4(a: i32) -> u32 {
|
||||
if a == 0 {
|
||||
return 1;
|
||||
//~^ needless_return
|
||||
} else if if if a > 0x1_1 {
|
||||
return 2;
|
||||
} else {
|
||||
return 5;
|
||||
} {
|
||||
true
|
||||
} else {
|
||||
true
|
||||
} {
|
||||
0xDEADC0DE
|
||||
} else if match a {
|
||||
b @ _ => {
|
||||
return 1;
|
||||
},
|
||||
} {
|
||||
0xDEADBEEF
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -685,5 +685,101 @@ LL - return unsafe { todo() } as *const i32;
|
|||
LL + (unsafe { todo() } as *const i32)
|
||||
|
|
||||
|
||||
error: aborting due to 55 previous errors
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:468:13
|
||||
|
|
||||
LL | return 1;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return 1;
|
||||
LL + 1
|
||||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:471:13
|
||||
|
|
||||
LL | return 2;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return 2;
|
||||
LL + 2
|
||||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:474:13
|
||||
|
|
||||
LL | return 3;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return 3;
|
||||
LL + 3
|
||||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:481:13
|
||||
|
|
||||
LL | return 1;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return 1;
|
||||
LL + 1
|
||||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:486:13
|
||||
|
|
||||
LL | return 3;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return 3;
|
||||
LL + 3
|
||||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:493:13
|
||||
|
|
||||
LL | return 1;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return 1;
|
||||
LL + 1
|
||||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:498:13
|
||||
|
|
||||
LL | return 3;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return 3;
|
||||
LL + 3
|
||||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:506:13
|
||||
|
|
||||
LL | return 1;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return 1;
|
||||
LL + 1
|
||||
|
|
||||
|
||||
error: aborting due to 63 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)`
|
||||
--> tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs:6:19
|
||||
--> tests/ui/non_expressive_names_error_recovery.rs:6:19
|
||||
|
|
||||
LL | fn aa(a: Aa<String) {
|
||||
| ^ expected one of 7 possible tokens
|
||||
|
|
@ -443,3 +443,13 @@ fn issue_14615(a: MutexGuard<Option<u32>>) -> Option<String> {
|
|||
//~^^^ question_mark
|
||||
Some(format!("{a}"))
|
||||
}
|
||||
|
||||
fn const_in_pattern(x: Option<(i32, i32)>) -> Option<()> {
|
||||
const N: i32 = 0;
|
||||
|
||||
let Some((x, N)) = x else {
|
||||
return None;
|
||||
};
|
||||
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -539,3 +539,13 @@ fn issue_14615(a: MutexGuard<Option<u32>>) -> Option<String> {
|
|||
//~^^^ question_mark
|
||||
Some(format!("{a}"))
|
||||
}
|
||||
|
||||
fn const_in_pattern(x: Option<(i32, i32)>) -> Option<()> {
|
||||
const N: i32 = 0;
|
||||
|
||||
let Some((x, N)) = x else {
|
||||
return None;
|
||||
};
|
||||
|
||||
None
|
||||
}
|
||||
|
|
|
|||
11
tests/ui/to_string_in_format_args_incremental.fixed
Normal file
11
tests/ui/to_string_in_format_args_incremental.fixed
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
//@compile-flags: -C incremental=target/debug/test/incr
|
||||
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
// see https://github.com/rust-lang/rust-clippy/issues/10969
|
||||
|
||||
fn main() {
|
||||
let s = "Hello, world!";
|
||||
println!("{}", s);
|
||||
//~^ to_string_in_format_args
|
||||
}
|
||||
11
tests/ui/to_string_in_format_args_incremental.rs
Normal file
11
tests/ui/to_string_in_format_args_incremental.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
//@compile-flags: -C incremental=target/debug/test/incr
|
||||
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
// see https://github.com/rust-lang/rust-clippy/issues/10969
|
||||
|
||||
fn main() {
|
||||
let s = "Hello, world!";
|
||||
println!("{}", s.to_string());
|
||||
//~^ to_string_in_format_args
|
||||
}
|
||||
11
tests/ui/to_string_in_format_args_incremental.stderr
Normal file
11
tests/ui/to_string_in_format_args_incremental.stderr
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
error: `to_string` applied to a type that implements `Display` in `println!` args
|
||||
--> tests/ui/to_string_in_format_args_incremental.rs:9:21
|
||||
|
|
||||
LL | println!("{}", s.to_string());
|
||||
| ^^^^^^^^^^^^ help: remove this
|
||||
|
|
||||
= note: `-D clippy::to-string-in-format-args` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::to_string_in_format_args)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
179
tests/ui/trivially_copy_pass_by_ref.fixed
Normal file
179
tests/ui/trivially_copy_pass_by_ref.fixed
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
//@normalize-stderr-test: "\(\d+ byte\)" -> "(N byte)"
|
||||
//@normalize-stderr-test: "\(limit: \d+ byte\)" -> "(limit: N byte)"
|
||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
#![allow(
|
||||
clippy::disallowed_names,
|
||||
clippy::extra_unused_lifetimes,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::needless_pass_by_ref_mut,
|
||||
clippy::redundant_field_names,
|
||||
clippy::uninlined_format_args
|
||||
)]
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Foo(u32);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Bar([u8; 24]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Color {
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
pub a: u8,
|
||||
}
|
||||
|
||||
struct FooRef<'a> {
|
||||
foo: &'a Foo,
|
||||
}
|
||||
|
||||
type Baz = u32;
|
||||
|
||||
fn good(a: &mut u32, b: u32, c: &Bar) {}
|
||||
|
||||
fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 {
|
||||
&foo.0
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
|
||||
&foo.0
|
||||
}
|
||||
|
||||
fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
|
||||
FooRef { foo }
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn good_return_explicit_lt_struct<'a>(foo: &'a Foo) -> FooRef<'a> {
|
||||
FooRef { foo }
|
||||
}
|
||||
|
||||
fn bad(x: u32, y: Foo, z: Baz) {}
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
|
||||
impl Foo {
|
||||
fn good(self, a: &mut u32, b: u32, c: &Bar) {}
|
||||
|
||||
fn good2(&mut self) {}
|
||||
|
||||
fn bad(self, x: u32, y: Foo, z: Baz) {}
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
|
||||
fn bad2(x: u32, y: Foo, z: Baz) {}
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
|
||||
fn bad_issue7518(self, other: Self) {}
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if
|
||||
}
|
||||
|
||||
impl AsRef<u32> for Foo {
|
||||
fn as_ref(&self) -> &u32 {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Bar {
|
||||
fn good(&self, a: &mut u32, b: u32, c: &Bar) {}
|
||||
|
||||
fn bad2(x: u32, y: Foo, z: Baz) {}
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if
|
||||
//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if
|
||||
//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if
|
||||
}
|
||||
|
||||
trait MyTrait {
|
||||
fn trait_method(&self, foo: Foo);
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if
|
||||
}
|
||||
|
||||
pub trait MyTrait2 {
|
||||
fn trait_method2(&self, color: &Color);
|
||||
}
|
||||
|
||||
trait MyTrait3 {
|
||||
#[expect(clippy::trivially_copy_pass_by_ref)]
|
||||
fn trait_method(&self, foo: &Foo);
|
||||
}
|
||||
|
||||
// Trait impls should not warn
|
||||
impl MyTrait3 for Foo {
|
||||
fn trait_method(&self, foo: &Foo) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
mod issue3992 {
|
||||
pub trait A {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
fn a(b: &u16) {}
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub fn c(d: &u16) {}
|
||||
}
|
||||
|
||||
mod issue5876 {
|
||||
// Don't lint here as it is always inlined
|
||||
#[inline(always)]
|
||||
fn foo_always(x: &i32) {
|
||||
println!("{}", x);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn foo_never(x: i32) {
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
println!("{}", x);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn foo(x: i32) {
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
println!("{}", x);
|
||||
}
|
||||
}
|
||||
|
||||
fn ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> {
|
||||
Some(x)
|
||||
}
|
||||
|
||||
fn ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> {
|
||||
Some(x)
|
||||
}
|
||||
|
||||
fn with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 {
|
||||
if true { x } else { y }
|
||||
}
|
||||
|
||||
async fn async_implicit(x: &u32) -> &u32 {
|
||||
x
|
||||
}
|
||||
|
||||
async fn async_explicit<'a>(x: &'a u32) -> &'a u32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn unrelated_lifetimes<'a, 'b>(_x: u32, y: &'b u32) -> &'b u32 {
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
y
|
||||
}
|
||||
|
||||
fn return_ptr(x: &u32) -> *const u32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn return_field_ptr(x: &(u32, u32)) -> *const u32 {
|
||||
&x.0
|
||||
}
|
||||
|
||||
fn return_field_ptr_addr_of(x: &(u32, u32)) -> *const u32 {
|
||||
core::ptr::addr_of!(x.0)
|
||||
}
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
//@normalize-stderr-test: "\(\d+ byte\)" -> "(N byte)"
|
||||
//@normalize-stderr-test: "\(limit: \d+ byte\)" -> "(limit: 8 byte)"
|
||||
//@normalize-stderr-test: "\(limit: \d+ byte\)" -> "(limit: N byte)"
|
||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
#![allow(
|
||||
clippy::disallowed_names,
|
||||
clippy::extra_unused_lifetimes,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::needless_pass_by_ref_mut,
|
||||
clippy::redundant_field_names,
|
||||
clippy::uninlined_format_args,
|
||||
clippy::needless_pass_by_ref_mut
|
||||
clippy::uninlined_format_args
|
||||
)]
|
||||
//@no-rustfix
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Foo(u32);
|
||||
|
||||
|
|
@ -90,21 +91,26 @@ impl Bar {
|
|||
}
|
||||
|
||||
trait MyTrait {
|
||||
fn trait_method(&self, _foo: &Foo);
|
||||
fn trait_method(&self, foo: &Foo);
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if
|
||||
}
|
||||
|
||||
pub trait MyTrait2 {
|
||||
fn trait_method2(&self, _color: &Color);
|
||||
fn trait_method2(&self, color: &Color);
|
||||
}
|
||||
|
||||
impl MyTrait for Foo {
|
||||
fn trait_method(&self, _foo: &Foo) {
|
||||
trait MyTrait3 {
|
||||
#[expect(clippy::trivially_copy_pass_by_ref)]
|
||||
fn trait_method(&self, foo: &Foo);
|
||||
}
|
||||
|
||||
// Trait impls should not warn
|
||||
impl MyTrait3 for Foo {
|
||||
fn trait_method(&self, foo: &Foo) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
mod issue3992 {
|
||||
pub trait A {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
|
|
@ -135,57 +141,39 @@ mod issue5876 {
|
|||
}
|
||||
}
|
||||
|
||||
fn _ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> {
|
||||
fn ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> {
|
||||
Some(x)
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn _ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> {
|
||||
fn ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> {
|
||||
Some(x)
|
||||
}
|
||||
|
||||
fn _with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 {
|
||||
fn with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 {
|
||||
if true { x } else { y }
|
||||
}
|
||||
|
||||
async fn _async_implicit(x: &u32) -> &u32 {
|
||||
async fn async_implicit(x: &u32) -> &u32 {
|
||||
x
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
async fn _async_explicit<'a>(x: &'a u32) -> &'a u32 {
|
||||
async fn async_explicit<'a>(x: &'a u32) -> &'a u32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
|
||||
fn unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
|
||||
//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by
|
||||
y
|
||||
}
|
||||
|
||||
fn _return_ptr(x: &u32) -> *const u32 {
|
||||
fn return_ptr(x: &u32) -> *const u32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn _return_field_ptr(x: &(u32, u32)) -> *const u32 {
|
||||
fn return_field_ptr(x: &(u32, u32)) -> *const u32 {
|
||||
&x.0
|
||||
}
|
||||
|
||||
fn _return_field_ptr_addr_of(x: &(u32, u32)) -> *const u32 {
|
||||
fn return_field_ptr_addr_of(x: &(u32, u32)) -> *const u32 {
|
||||
core::ptr::addr_of!(x.0)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (mut foo, bar) = (Foo(0), Bar([0; 24]));
|
||||
let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
|
||||
good(&mut a, b, &c);
|
||||
good_return_implicit_lt_ref(&y);
|
||||
good_return_explicit_lt_ref(&y);
|
||||
bad(&x, &y, &z);
|
||||
foo.good(&mut a, b, &c);
|
||||
foo.good2();
|
||||
foo.bad(&x, &y, &z);
|
||||
Foo::bad2(&x, &y, &z);
|
||||
bar.good(&mut a, b, &c);
|
||||
Bar::bad2(&x, &y, &z);
|
||||
foo.as_ref();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:52:11
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:53:11
|
||||
|
|
||||
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `u32`
|
||||
|
|
@ -10,107 +10,107 @@ note: the lint level is defined here
|
|||
LL | #![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:52:20
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:53:20
|
||||
|
|
||||
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `Foo`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:52:29
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:53:29
|
||||
|
|
||||
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `Baz`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:62:12
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:63:12
|
||||
|
|
||||
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^^ help: consider passing by value instead: `self`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:62:22
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:63:22
|
||||
|
|
||||
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `u32`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:62:31
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:63:31
|
||||
|
|
||||
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `Foo`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:62:40
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:63:40
|
||||
|
|
||||
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `Baz`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:68:16
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:69:16
|
||||
|
|
||||
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `u32`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:68:25
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:69:25
|
||||
|
|
||||
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `Foo`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:68:34
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:69:34
|
||||
|
|
||||
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `Baz`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:73:35
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:74:35
|
||||
|
|
||||
LL | fn bad_issue7518(self, other: &Self) {}
|
||||
| ^^^^^ help: consider passing by value instead: `Self`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:86:16
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:87:16
|
||||
|
|
||||
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `u32`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:86:25
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:87:25
|
||||
|
|
||||
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `Foo`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:86:34
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:87:34
|
||||
|
|
||||
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
|
||||
| ^^^^ help: consider passing by value instead: `Baz`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:93:34
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:94:33
|
||||
|
|
||||
LL | fn trait_method(&self, _foo: &Foo);
|
||||
| ^^^^ help: consider passing by value instead: `Foo`
|
||||
LL | fn trait_method(&self, foo: &Foo);
|
||||
| ^^^^ help: consider passing by value instead: `Foo`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:126:21
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:132:21
|
||||
|
|
||||
LL | fn foo_never(x: &i32) {
|
||||
| ^^^^ help: consider passing by value instead: `i32`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:132:15
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:138:15
|
||||
|
|
||||
LL | fn foo(x: &i32) {
|
||||
| ^^^^ help: consider passing by value instead: `i32`
|
||||
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:160:37
|
||||
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||
--> tests/ui/trivially_copy_pass_by_ref.rs:164:36
|
||||
|
|
||||
LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
|
||||
| ^^^^^^^ help: consider passing by value instead: `u32`
|
||||
LL | fn unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
|
||||
| ^^^^^^^ help: consider passing by value instead: `u32`
|
||||
|
||||
error: aborting due to 18 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -154,3 +154,89 @@ fn issue_5715(mut m: core::cell::RefCell<Option<u32>>) {
|
|||
m = core::cell::RefCell::new(Some(x + 1));
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_362 {
|
||||
pub fn merge_sorted<T>(xs: Vec<T>, ys: Vec<T>) -> Vec<T>
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
let total_len = xs.len() + ys.len();
|
||||
let mut res = Vec::with_capacity(total_len);
|
||||
let mut ix = xs.into_iter().peekable();
|
||||
let mut iy = ys.into_iter().peekable();
|
||||
loop {
|
||||
//~^ while_let_loop
|
||||
let lt = match (ix.peek(), iy.peek()) {
|
||||
(Some(x), Some(y)) => x < y,
|
||||
_ => break,
|
||||
};
|
||||
res.push(if lt { &mut ix } else { &mut iy }.next().unwrap());
|
||||
}
|
||||
res.extend(ix);
|
||||
res.extend(iy);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn let_assign() {
|
||||
loop {
|
||||
//~^ while_let_loop
|
||||
let x = if let Some(y) = Some(3) {
|
||||
y
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
if x == 3 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
//~^ while_let_loop
|
||||
let x: u32 = if let Some(y) = Some(3) {
|
||||
y
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
if x == 3 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
//~^ while_let_loop
|
||||
let x = if let Some(x) = Some(3) {
|
||||
x
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
if x == 3 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
//~^ while_let_loop
|
||||
let x: u32 = if let Some(x) = Some(3) {
|
||||
x
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
if x == 3 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
//~^ while_let_loop
|
||||
let x = if let Some(x) = Some(2) {
|
||||
let t = 1;
|
||||
t + x
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
if x == 3 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,125 @@ LL | | let (e, l) = match "".split_whitespace().next() {
|
|||
... |
|
||||
LL | | let _ = (e, l);
|
||||
LL | | }
|
||||
| |_____^ help: try: `while let Some(word) = "".split_whitespace().next() { .. }`
|
||||
| |_____^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL ~ while let Some(word) = "".split_whitespace().next() {
|
||||
LL + let (e, l) = (word.is_empty(), word.len());
|
||||
LL + ..
|
||||
LL + }
|
||||
|
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
error: this loop could be written as a `while let` loop
|
||||
--> tests/ui/while_let_loop.rs:167:9
|
||||
|
|
||||
LL | / loop {
|
||||
LL | |
|
||||
LL | | let lt = match (ix.peek(), iy.peek()) {
|
||||
LL | | (Some(x), Some(y)) => x < y,
|
||||
... |
|
||||
LL | | res.push(if lt { &mut ix } else { &mut iy }.next().unwrap());
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL ~ while let (Some(x), Some(y)) = (ix.peek(), iy.peek()) {
|
||||
LL + let lt = x < y;
|
||||
LL + ..
|
||||
LL + }
|
||||
|
|
||||
|
||||
error: this loop could be written as a `while let` loop
|
||||
--> tests/ui/while_let_loop.rs:182:5
|
||||
|
|
||||
LL | / loop {
|
||||
LL | |
|
||||
LL | | let x = if let Some(y) = Some(3) {
|
||||
LL | | y
|
||||
... |
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL ~ while let Some(y) = Some(3) {
|
||||
LL + let x = y;
|
||||
LL + ..
|
||||
LL + }
|
||||
|
|
||||
|
||||
error: this loop could be written as a `while let` loop
|
||||
--> tests/ui/while_let_loop.rs:194:5
|
||||
|
|
||||
LL | / loop {
|
||||
LL | |
|
||||
LL | | let x: u32 = if let Some(y) = Some(3) {
|
||||
LL | | y
|
||||
... |
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL ~ while let Some(y) = Some(3) {
|
||||
LL + let x: u32 = y;
|
||||
LL + ..
|
||||
LL + }
|
||||
|
|
||||
|
||||
error: this loop could be written as a `while let` loop
|
||||
--> tests/ui/while_let_loop.rs:206:5
|
||||
|
|
||||
LL | / loop {
|
||||
LL | |
|
||||
LL | | let x = if let Some(x) = Some(3) {
|
||||
LL | | x
|
||||
... |
|
||||
LL | | }
|
||||
| |_____^ help: try: `while let Some(x) = Some(3) { .. }`
|
||||
|
||||
error: this loop could be written as a `while let` loop
|
||||
--> tests/ui/while_let_loop.rs:218:5
|
||||
|
|
||||
LL | / loop {
|
||||
LL | |
|
||||
LL | | let x: u32 = if let Some(x) = Some(3) {
|
||||
LL | | x
|
||||
... |
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL ~ while let Some(x) = Some(3) {
|
||||
LL + let x: u32 = x;
|
||||
LL + ..
|
||||
LL + }
|
||||
|
|
||||
|
||||
error: this loop could be written as a `while let` loop
|
||||
--> tests/ui/while_let_loop.rs:230:5
|
||||
|
|
||||
LL | / loop {
|
||||
LL | |
|
||||
LL | | let x = if let Some(x) = Some(2) {
|
||||
LL | | let t = 1;
|
||||
... |
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL ~ while let Some(x) = Some(2) {
|
||||
LL + let x = {
|
||||
LL + let t = 1;
|
||||
LL + t + x
|
||||
LL + };
|
||||
LL + ..
|
||||
LL + }
|
||||
|
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB
|
|||
users_on_vacation = [
|
||||
"matthiaskrgr",
|
||||
"Manishearth",
|
||||
"blyxyas",
|
||||
]
|
||||
|
||||
[assign.owners]
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ Otherwise, have a great day =^.^=
|
|||
|
||||
<div class="container"> {# #}
|
||||
<div class="page-header"> {# #}
|
||||
<h1>Clippy Lints</h1> {# #}
|
||||
<h1>Clippy Lints <span id="lint-count" class="badge"></span></h1> {# #}
|
||||
</div> {# #}
|
||||
|
||||
<noscript> {# #}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ window.searchState = {
|
|||
} else {
|
||||
window.location.hash = '';
|
||||
}
|
||||
updateLintCount();
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -598,3 +599,14 @@ generateSearch();
|
|||
parseURLFilters();
|
||||
scrollToLintByURL();
|
||||
filters.filterLints();
|
||||
updateLintCount();
|
||||
|
||||
function updateLintCount() {
|
||||
const allLints = filters.getAllLints();
|
||||
const totalLints = allLints.length;
|
||||
|
||||
const countElement = document.getElementById("lint-count");
|
||||
if (countElement) {
|
||||
countElement.innerText = `Total number: ${totalLints}`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue