Auto merge of #151501 - flip1995:clippy-subtree-update, r=Manishearth
Clippy subtree update r? Manishearth `Cargo.lock` update due to Clippy version bump.
This commit is contained in:
commit
d222ddc4d9
150 changed files with 6339 additions and 2115 deletions
10
Cargo.lock
10
Cargo.lock
|
|
@ -630,7 +630,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
|||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"askama",
|
||||
|
|
@ -657,7 +657,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_config"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
dependencies = [
|
||||
"clippy_utils",
|
||||
"itertools",
|
||||
|
|
@ -681,7 +681,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cargo_metadata 0.18.1",
|
||||
|
|
@ -713,7 +713,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"itertools",
|
||||
|
|
@ -1117,7 +1117,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "declare_clippy_lint"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
|
||||
[[package]]
|
||||
name = "derive-where"
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ impl CommandProfiler {
|
|||
})
|
||||
.collect();
|
||||
|
||||
entries.sort_by(|a, b| b.2.cmp(&a.2));
|
||||
entries.sort_by_key(|e| std::cmp::Reverse(e.2));
|
||||
|
||||
let total_bootstrap_duration = start_time.elapsed();
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,66 @@ document.
|
|||
|
||||
## Unreleased / Beta / In Rust Nightly
|
||||
|
||||
[d9fb15c...master](https://github.com/rust-lang/rust-clippy/compare/d9fb15c...master)
|
||||
[92b4b68...master](https://github.com/rust-lang/rust-clippy/compare/92b4b68...master)
|
||||
|
||||
## Rust 1.93
|
||||
|
||||
Current stable, released 2026-01-22
|
||||
|
||||
[View all 96 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-10-17T15%3A48%3A11Z..2025-11-28T19%3A22%3A54Z+base%3Amaster)
|
||||
|
||||
### New Lints
|
||||
|
||||
* Added [`doc_paragraphs_missing_punctuation`] to `restriction`
|
||||
[#15758](https://github.com/rust-lang/rust-clippy/pull/15758)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Renamed [`needless_if`] to [`needless_ifs`]
|
||||
[#15961](https://github.com/rust-lang/rust-clippy/pull/15961)
|
||||
* Renamed [`empty_enum`] to [`empty_enums`]
|
||||
[#15912](https://github.com/rust-lang/rust-clippy/pull/15912)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [`result_large_err`] added `large_error_ignored` configuration
|
||||
[#15697](https://github.com/rust-lang/rust-clippy/pull/15697)
|
||||
* [`explicit_deref_methods`] don't lint in `impl Deref(Mut)`
|
||||
[#16113](https://github.com/rust-lang/rust-clippy/pull/16113)
|
||||
* [`missing_docs_in_private_items`] don't lint items in bodies and automatically derived impls;
|
||||
better detect when things are accessible from the crate root; lint unnameable items which are
|
||||
accessible outside the crate
|
||||
[#14741](https://github.com/rust-lang/rust-clippy/pull/14741)
|
||||
* [`unnecessary_unwrap`] and [`panicking_unwrap`] lint field accesses
|
||||
[#15949](https://github.com/rust-lang/rust-clippy/pull/15949)
|
||||
* [`ok_expect`] add autofix
|
||||
[#15867](https://github.com/rust-lang/rust-clippy/pull/15867)
|
||||
* [`let_and_return`] disallow _any_ text between let and return
|
||||
[#16006](https://github.com/rust-lang/rust-clippy/pull/16006)
|
||||
* [`needless_collect`] extend to lint more cases
|
||||
[#14361](https://github.com/rust-lang/rust-clippy/pull/14361)
|
||||
* [`needless_doctest_main`] and [`test_attr_in_doctest`] now handle whitespace in language tags
|
||||
[#15967](https://github.com/rust-lang/rust-clippy/pull/15967)
|
||||
* [`search_is_some`] now fixes code spanning multiple lines
|
||||
[#15902](https://github.com/rust-lang/rust-clippy/pull/15902)
|
||||
* [`unnecessary_find_map`] and [`unnecessary_filter_map`] make diagnostic spans more precise
|
||||
[#15929](https://github.com/rust-lang/rust-clippy/pull/15929)
|
||||
* [`precedence`] warn about ambiguity when a closure is used as a method call receiver
|
||||
[#14421](https://github.com/rust-lang/rust-clippy/pull/14421)
|
||||
* [`match_as_ref`] suggest `as_ref` when the reference needs to be cast; improve diagnostics
|
||||
[#15934](https://github.com/rust-lang/rust-clippy/pull/15934)
|
||||
[#15928](https://github.com/rust-lang/rust-clippy/pull/15928)
|
||||
|
||||
### False Positive Fixes
|
||||
|
||||
* [`single_range_in_vec_init`] fix FP for explicit `Range`
|
||||
[#16043](https://github.com/rust-lang/rust-clippy/pull/16043)
|
||||
* [`mod_module_files`] fix false positive for integration tests in workspace crates
|
||||
[#16048](https://github.com/rust-lang/rust-clippy/pull/16048)
|
||||
* [`replace_box`] fix FP when the box is moved
|
||||
[#15984](https://github.com/rust-lang/rust-clippy/pull/15984)
|
||||
* [`len_zero`] fix FP on unstable methods
|
||||
[#15894](https://github.com/rust-lang/rust-clippy/pull/15894)
|
||||
|
||||
## Rust 1.92
|
||||
|
||||
|
|
@ -6406,6 +6465,7 @@ Released 2018-09-13
|
|||
[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
|
||||
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
|
||||
[`duplicated_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes
|
||||
[`duration_suboptimal_units`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_suboptimal_units
|
||||
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
|
||||
[`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute
|
||||
[`elidable_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#elidable_lifetime_names
|
||||
|
|
@ -6607,6 +6667,7 @@ Released 2018-09-13
|
|||
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
|
||||
[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
|
||||
[`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
|
||||
[`manual_checked_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_checked_ops
|
||||
[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
|
||||
[`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains
|
||||
[`manual_dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_dangling_ptr
|
||||
|
|
@ -6652,6 +6713,7 @@ Released 2018-09-13
|
|||
[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
|
||||
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
|
||||
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
|
||||
[`manual_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_take
|
||||
[`manual_try_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold
|
||||
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
|
||||
[`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
|
|
|||
|
|
@ -905,6 +905,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once)
|
||||
* [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat)
|
||||
* [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip)
|
||||
* [`manual_take`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_take)
|
||||
* [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold)
|
||||
* [`map_clone`](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone)
|
||||
* [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_config"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
|
|
|
|||
|
|
@ -780,6 +780,7 @@ define_Conf! {
|
|||
manual_split_once,
|
||||
manual_str_repeat,
|
||||
manual_strip,
|
||||
manual_take,
|
||||
manual_try_fold,
|
||||
map_clone,
|
||||
map_unwrap_or,
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@
|
|||
unused_lifetimes,
|
||||
unused_qualifications
|
||||
)]
|
||||
#![allow(
|
||||
clippy::must_use_candidate,
|
||||
clippy::missing_panics_doc,
|
||||
)]
|
||||
#![allow(clippy::must_use_candidate, clippy::missing_panics_doc)]
|
||||
#![deny(clippy::derive_deserialize_allowing_unknown)]
|
||||
|
||||
extern crate rustc_data_structures;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::parse::{DeprecatedLint, Lint, ParseCx};
|
||||
use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint};
|
||||
use crate::update_lints::generate_lint_files;
|
||||
use crate::utils::{UpdateMode, Version};
|
||||
use std::ffi::OsStr;
|
||||
|
|
@ -61,6 +61,58 @@ pub fn deprecate<'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'cx str,
|
|||
}
|
||||
}
|
||||
|
||||
pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) {
|
||||
let mut lints = cx.find_lint_decls();
|
||||
let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints();
|
||||
|
||||
let Some(lint) = lints.iter().find(|l| l.name == old_name) else {
|
||||
eprintln!("error: failed to find lint `{old_name}`");
|
||||
return;
|
||||
};
|
||||
|
||||
let old_name_prefixed = cx.str_buf.with(|buf| {
|
||||
buf.extend(["clippy::", old_name]);
|
||||
cx.arena.alloc_str(buf)
|
||||
});
|
||||
for lint in &mut renamed_lints {
|
||||
if lint.new_name == old_name_prefixed {
|
||||
lint.new_name = new_name;
|
||||
}
|
||||
}
|
||||
match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) {
|
||||
Ok(_) => {
|
||||
println!("`{old_name}` is already deprecated");
|
||||
return;
|
||||
},
|
||||
Err(idx) => renamed_lints.insert(
|
||||
idx,
|
||||
RenamedLint {
|
||||
old_name: old_name_prefixed,
|
||||
new_name,
|
||||
version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
let mod_path = {
|
||||
let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
|
||||
if mod_path.is_dir() {
|
||||
mod_path = mod_path.join("mod");
|
||||
}
|
||||
|
||||
mod_path.set_extension("rs");
|
||||
mod_path
|
||||
};
|
||||
|
||||
if remove_lint_declaration(old_name, &mod_path, &mut lints).unwrap_or(false) {
|
||||
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
|
||||
println!("info: `{old_name}` has successfully been uplifted");
|
||||
println!("note: you must run `cargo uitest` to update the test results");
|
||||
} else {
|
||||
eprintln!("error: lint not found");
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint<'_>>) -> io::Result<bool> {
|
||||
fn remove_lint(name: &str, lints: &mut Vec<Lint<'_>>) {
|
||||
lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::parse::cursor::{self, Capture, Cursor};
|
||||
use crate::parse::{ParseCx, RenamedLint};
|
||||
use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint};
|
||||
use crate::update_lints::generate_lint_files;
|
||||
use crate::utils::{
|
||||
ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists,
|
||||
|
|
@ -10,6 +10,96 @@ use std::ffi::OsString;
|
|||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
/// Runs the `deprecate` command
|
||||
///
|
||||
/// This does the following:
|
||||
/// * Adds an entry to `deprecated_lints.rs`.
|
||||
/// * Removes the lint declaration (and the entire file if applicable)
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If a file path could not read from or written to
|
||||
pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'env str, reason: &'env str) {
|
||||
let mut lints = cx.find_lint_decls();
|
||||
let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints();
|
||||
|
||||
let Some(lint_idx) = lints.iter().position(|l| l.name == name) else {
|
||||
eprintln!("error: failed to find lint `{name}`");
|
||||
return;
|
||||
};
|
||||
|
||||
let prefixed_name = cx.str_buf.with(|buf| {
|
||||
buf.extend(["clippy::", name]);
|
||||
cx.arena.alloc_str(buf)
|
||||
});
|
||||
match deprecated_lints.binary_search_by(|x| x.name.cmp(prefixed_name)) {
|
||||
Ok(_) => {
|
||||
println!("`{name}` is already deprecated");
|
||||
return;
|
||||
},
|
||||
Err(idx) => deprecated_lints.insert(
|
||||
idx,
|
||||
DeprecatedLint {
|
||||
name: prefixed_name,
|
||||
reason,
|
||||
version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
remove_lint_declaration(lint_idx, &mut lints, &mut FileUpdater::default());
|
||||
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
|
||||
println!("info: `{name}` has successfully been deprecated");
|
||||
println!("note: you must run `cargo uitest` to update the test results");
|
||||
}
|
||||
|
||||
pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) {
|
||||
let mut lints = cx.find_lint_decls();
|
||||
let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints();
|
||||
|
||||
let Some(lint_idx) = lints.iter().position(|l| l.name == old_name) else {
|
||||
eprintln!("error: failed to find lint `{old_name}`");
|
||||
return;
|
||||
};
|
||||
|
||||
let old_name_prefixed = cx.str_buf.with(|buf| {
|
||||
buf.extend(["clippy::", old_name]);
|
||||
cx.arena.alloc_str(buf)
|
||||
});
|
||||
for lint in &mut renamed_lints {
|
||||
if lint.new_name == old_name_prefixed {
|
||||
lint.new_name = new_name;
|
||||
}
|
||||
}
|
||||
match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) {
|
||||
Ok(_) => {
|
||||
println!("`{old_name}` is already deprecated");
|
||||
return;
|
||||
},
|
||||
Err(idx) => renamed_lints.insert(
|
||||
idx,
|
||||
RenamedLint {
|
||||
old_name: old_name_prefixed,
|
||||
new_name,
|
||||
version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
let mut updater = FileUpdater::default();
|
||||
let remove_mod = remove_lint_declaration(lint_idx, &mut lints, &mut updater);
|
||||
let mut update_fn = uplift_update_fn(old_name, new_name, remove_mod);
|
||||
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);
|
||||
println!("info: `{old_name}` has successfully been uplifted as `{new_name}`");
|
||||
println!("note: you must run `cargo uitest` to update the test results");
|
||||
}
|
||||
|
||||
/// Runs the `rename_lint` command.
|
||||
///
|
||||
/// This does the following:
|
||||
|
|
@ -25,8 +115,7 @@ use std::path::Path;
|
|||
/// * If either lint name has a prefix
|
||||
/// * If `old_name` doesn't name an existing lint.
|
||||
/// * If `old_name` names a deprecated or renamed lint.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str, new_name: &'cx str, uplift: bool) {
|
||||
pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) {
|
||||
let mut updater = FileUpdater::default();
|
||||
let mut lints = cx.find_lint_decls();
|
||||
let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints();
|
||||
|
|
@ -34,20 +123,15 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str
|
|||
let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else {
|
||||
panic!("could not find lint `{old_name}`");
|
||||
};
|
||||
let lint = &lints[lint_idx];
|
||||
|
||||
let old_name_prefixed = cx.str_buf.with(|buf| {
|
||||
buf.extend(["clippy::", old_name]);
|
||||
cx.arena.alloc_str(buf)
|
||||
});
|
||||
let new_name_prefixed = if uplift {
|
||||
new_name
|
||||
} else {
|
||||
cx.str_buf.with(|buf| {
|
||||
buf.extend(["clippy::", new_name]);
|
||||
cx.arena.alloc_str(buf)
|
||||
})
|
||||
};
|
||||
let new_name_prefixed = cx.str_buf.with(|buf| {
|
||||
buf.extend(["clippy::", new_name]);
|
||||
cx.arena.alloc_str(buf)
|
||||
});
|
||||
|
||||
for lint in &mut renamed_lints {
|
||||
if lint.new_name == old_name_prefixed {
|
||||
|
|
@ -71,37 +155,8 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str
|
|||
},
|
||||
}
|
||||
|
||||
// Some tests are named `lint_name_suffix` which should also be renamed,
|
||||
// but we can't do that if the renamed lint's name overlaps with another
|
||||
// lint. e.g. renaming 'foo' to 'bar' when a lint 'foo_bar' also exists.
|
||||
let change_prefixed_tests = lints.get(lint_idx + 1).is_none_or(|l| !l.name.starts_with(old_name));
|
||||
|
||||
let mut mod_edit = ModEdit::None;
|
||||
if uplift {
|
||||
let is_unique_mod = lints[..lint_idx].iter().any(|l| l.module == lint.module)
|
||||
|| lints[lint_idx + 1..].iter().any(|l| l.module == lint.module);
|
||||
if is_unique_mod {
|
||||
if delete_file_if_exists(lint.path.as_ref()) {
|
||||
mod_edit = ModEdit::Delete;
|
||||
}
|
||||
} else {
|
||||
updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus {
|
||||
let mut start = &src[..lint.declaration_range.start];
|
||||
if start.ends_with("\n\n") {
|
||||
start = &start[..start.len() - 1];
|
||||
}
|
||||
let mut end = &src[lint.declaration_range.end..];
|
||||
if end.starts_with("\n\n") {
|
||||
end = &end[1..];
|
||||
}
|
||||
dst.push_str(start);
|
||||
dst.push_str(end);
|
||||
UpdateStatus::Changed
|
||||
});
|
||||
}
|
||||
delete_test_files(old_name, change_prefixed_tests);
|
||||
lints.remove(lint_idx);
|
||||
} else if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() {
|
||||
let mut rename_mod = false;
|
||||
if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() {
|
||||
let lint = &mut lints[lint_idx];
|
||||
if lint.module.ends_with(old_name)
|
||||
&& lint
|
||||
|
|
@ -112,7 +167,7 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str
|
|||
let mut new_path = lint.path.with_file_name(new_name).into_os_string();
|
||||
new_path.push(".rs");
|
||||
if try_rename_file(lint.path.as_ref(), new_path.as_ref()) {
|
||||
mod_edit = ModEdit::Rename;
|
||||
rename_mod = true;
|
||||
}
|
||||
|
||||
lint.module = cx.str_buf.with(|buf| {
|
||||
|
|
@ -121,7 +176,16 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str
|
|||
cx.arena.alloc_str(buf)
|
||||
});
|
||||
}
|
||||
rename_test_files(old_name, new_name, change_prefixed_tests);
|
||||
|
||||
rename_test_files(
|
||||
old_name,
|
||||
new_name,
|
||||
&lints[lint_idx + 1..]
|
||||
.iter()
|
||||
.map(|l| l.name)
|
||||
.take_while(|&n| n.starts_with(old_name))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
lints[lint_idx].name = new_name;
|
||||
lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name));
|
||||
} else {
|
||||
|
|
@ -130,7 +194,7 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str
|
|||
return;
|
||||
}
|
||||
|
||||
let mut update_fn = file_update_fn(old_name, new_name, mod_edit);
|
||||
let mut update_fn = rename_update_fn(old_name, new_name, rename_mod);
|
||||
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") {
|
||||
|
|
@ -139,67 +203,85 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str
|
|||
}
|
||||
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
|
||||
|
||||
if uplift {
|
||||
println!("Uplifted `clippy::{old_name}` as `{new_name}`");
|
||||
if matches!(mod_edit, ModEdit::None) {
|
||||
println!("Only the rename has been registered, the code will need to be edited manually");
|
||||
} else {
|
||||
println!("All the lint's code has been deleted");
|
||||
println!("Make sure to inspect the results as some things may have been missed");
|
||||
}
|
||||
} else {
|
||||
println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`");
|
||||
println!("All code referencing the old name has been updated");
|
||||
println!("Make sure to inspect the results as some things may have been missed");
|
||||
}
|
||||
println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`");
|
||||
println!("All code referencing the old name has been updated");
|
||||
println!("Make sure to inspect the results as some things may have been missed");
|
||||
println!("note: `cargo uibless` still needs to be run to update the test results");
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum ModEdit {
|
||||
None,
|
||||
Delete,
|
||||
Rename,
|
||||
/// Removes a lint's declaration and test files. Returns whether the module containing the
|
||||
/// lint was deleted.
|
||||
fn remove_lint_declaration(lint_idx: usize, lints: &mut Vec<Lint<'_>>, updater: &mut FileUpdater) -> bool {
|
||||
let lint = lints.remove(lint_idx);
|
||||
let delete_mod = if lints.iter().all(|l| l.module != lint.module) {
|
||||
delete_file_if_exists(lint.path.as_ref())
|
||||
} else {
|
||||
updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus {
|
||||
let mut start = &src[..lint.declaration_range.start];
|
||||
if start.ends_with("\n\n") {
|
||||
start = &start[..start.len() - 1];
|
||||
}
|
||||
let mut end = &src[lint.declaration_range.end..];
|
||||
if end.starts_with("\n\n") {
|
||||
end = &end[1..];
|
||||
}
|
||||
dst.push_str(start);
|
||||
dst.push_str(end);
|
||||
UpdateStatus::Changed
|
||||
});
|
||||
false
|
||||
};
|
||||
delete_test_files(
|
||||
lint.name,
|
||||
&lints[lint_idx..]
|
||||
.iter()
|
||||
.map(|l| l.name)
|
||||
.take_while(|&n| n.starts_with(lint.name))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
delete_mod
|
||||
}
|
||||
|
||||
fn collect_ui_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) {
|
||||
fn collect_ui_test_names(lint: &str, ignored_prefixes: &[&str], dst: &mut Vec<(OsString, bool)>) {
|
||||
for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") {
|
||||
let e = e.expect("error reading `tests/ui`");
|
||||
let name = e.file_name();
|
||||
if let Some((name_only, _)) = name.as_encoded_bytes().split_once(|&x| x == b'.') {
|
||||
if name_only.starts_with(lint.as_bytes()) && (rename_prefixed || name_only.len() == lint.len()) {
|
||||
dst.push((name, true));
|
||||
}
|
||||
} else if name.as_encoded_bytes().starts_with(lint.as_bytes()) && (rename_prefixed || name.len() == lint.len())
|
||||
if name.as_encoded_bytes().starts_with(lint.as_bytes())
|
||||
&& !ignored_prefixes
|
||||
.iter()
|
||||
.any(|&pre| name.as_encoded_bytes().starts_with(pre.as_bytes()))
|
||||
&& let Ok(ty) = e.file_type()
|
||||
&& (ty.is_file() || ty.is_dir())
|
||||
{
|
||||
dst.push((name, ty.is_file()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_ui_toml_test_names(lint: &str, ignored_prefixes: &[&str], dst: &mut Vec<(OsString, bool)>) {
|
||||
for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") {
|
||||
let e = e.expect("error reading `tests/ui-toml`");
|
||||
let name = e.file_name();
|
||||
if name.as_encoded_bytes().starts_with(lint.as_bytes())
|
||||
&& !ignored_prefixes
|
||||
.iter()
|
||||
.any(|&pre| name.as_encoded_bytes().starts_with(pre.as_bytes()))
|
||||
&& e.file_type().is_ok_and(|ty| ty.is_dir())
|
||||
{
|
||||
dst.push((name, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_ui_toml_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) {
|
||||
if rename_prefixed {
|
||||
for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") {
|
||||
let e = e.expect("error reading `tests/ui-toml`");
|
||||
let name = e.file_name();
|
||||
if name.as_encoded_bytes().starts_with(lint.as_bytes()) && e.file_type().is_ok_and(|ty| ty.is_dir()) {
|
||||
dst.push((name, false));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dst.push((lint.into(), false));
|
||||
}
|
||||
}
|
||||
|
||||
/// Renames all test files for the given lint.
|
||||
///
|
||||
/// If `rename_prefixed` is `true` this will also rename tests which have the lint name as a prefix.
|
||||
fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) {
|
||||
let mut tests = Vec::new();
|
||||
/// Renames all test files for the given lint where the file name does not start with any
|
||||
/// of the given prefixes.
|
||||
fn rename_test_files(old_name: &str, new_name: &str, ignored_prefixes: &[&str]) {
|
||||
let mut tests: Vec<(OsString, bool)> = Vec::new();
|
||||
|
||||
let mut old_buf = OsString::from("tests/ui/");
|
||||
let mut new_buf = OsString::from("tests/ui/");
|
||||
collect_ui_test_names(old_name, rename_prefixed, &mut tests);
|
||||
collect_ui_test_names(old_name, ignored_prefixes, &mut tests);
|
||||
for &(ref name, is_file) in &tests {
|
||||
old_buf.push(name);
|
||||
new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]);
|
||||
|
|
@ -217,7 +299,7 @@ fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) {
|
|||
new_buf.truncate("tests/ui".len());
|
||||
old_buf.push("-toml/");
|
||||
new_buf.push("-toml/");
|
||||
collect_ui_toml_test_names(old_name, rename_prefixed, &mut tests);
|
||||
collect_ui_toml_test_names(old_name, ignored_prefixes, &mut tests);
|
||||
for (name, _) in &tests {
|
||||
old_buf.push(name);
|
||||
new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]);
|
||||
|
|
@ -227,11 +309,13 @@ fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) {
|
|||
}
|
||||
}
|
||||
|
||||
fn delete_test_files(lint: &str, rename_prefixed: bool) {
|
||||
/// Deletes all test files for the given lint where the file name does not start with any
|
||||
/// of the given prefixes.
|
||||
fn delete_test_files(lint: &str, ignored_prefixes: &[&str]) {
|
||||
let mut tests = Vec::new();
|
||||
|
||||
let mut buf = OsString::from("tests/ui/");
|
||||
collect_ui_test_names(lint, rename_prefixed, &mut tests);
|
||||
collect_ui_test_names(lint, ignored_prefixes, &mut tests);
|
||||
for &(ref name, is_file) in &tests {
|
||||
buf.push(name);
|
||||
if is_file {
|
||||
|
|
@ -246,7 +330,7 @@ fn delete_test_files(lint: &str, rename_prefixed: bool) {
|
|||
buf.push("-toml/");
|
||||
|
||||
tests.clear();
|
||||
collect_ui_toml_test_names(lint, rename_prefixed, &mut tests);
|
||||
collect_ui_toml_test_names(lint, ignored_prefixes, &mut tests);
|
||||
for (name, _) in &tests {
|
||||
buf.push(name);
|
||||
delete_dir_if_exists(buf.as_ref());
|
||||
|
|
@ -271,12 +355,50 @@ fn snake_to_pascal(s: &str) -> String {
|
|||
String::from_utf8(dst).unwrap()
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn file_update_fn<'a, 'b>(
|
||||
/// Creates an update function which replaces all instances of `clippy::old_name` with
|
||||
/// `new_name`.
|
||||
fn uplift_update_fn<'a>(
|
||||
old_name: &'a str,
|
||||
new_name: &'b str,
|
||||
mod_edit: ModEdit,
|
||||
) -> impl use<'a, 'b> + FnMut(&Path, &str, &mut String) -> UpdateStatus {
|
||||
new_name: &'a str,
|
||||
remove_mod: bool,
|
||||
) -> impl use<'a> + FnMut(&Path, &str, &mut String) -> UpdateStatus {
|
||||
move |_, src, dst| {
|
||||
let mut copy_pos = 0u32;
|
||||
let mut changed = false;
|
||||
let mut cursor = Cursor::new(src);
|
||||
while let Some(ident) = cursor.find_any_ident() {
|
||||
match cursor.get_text(ident) {
|
||||
"mod"
|
||||
if remove_mod && cursor.match_all(&[cursor::Pat::Ident(old_name), cursor::Pat::Semi], &mut []) =>
|
||||
{
|
||||
dst.push_str(&src[copy_pos as usize..ident.pos as usize]);
|
||||
dst.push_str(new_name);
|
||||
copy_pos = cursor.pos();
|
||||
if src[copy_pos as usize..].starts_with('\n') {
|
||||
copy_pos += 1;
|
||||
}
|
||||
changed = true;
|
||||
},
|
||||
"clippy" if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::Ident(old_name)], &mut []) => {
|
||||
dst.push_str(&src[copy_pos as usize..ident.pos as usize]);
|
||||
dst.push_str(new_name);
|
||||
copy_pos = cursor.pos();
|
||||
changed = true;
|
||||
},
|
||||
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
dst.push_str(&src[copy_pos as usize..]);
|
||||
UpdateStatus::from_changed(changed)
|
||||
}
|
||||
}
|
||||
|
||||
fn rename_update_fn<'a>(
|
||||
old_name: &'a str,
|
||||
new_name: &'a str,
|
||||
rename_mod: bool,
|
||||
) -> impl use<'a> + FnMut(&Path, &str, &mut String) -> UpdateStatus {
|
||||
let old_name_pascal = snake_to_pascal(old_name);
|
||||
let new_name_pascal = snake_to_pascal(new_name);
|
||||
let old_name_upper = old_name.to_ascii_uppercase();
|
||||
|
|
@ -307,34 +429,15 @@ fn file_update_fn<'a, 'b>(
|
|||
},
|
||||
// mod lint_name
|
||||
"mod" => {
|
||||
if !matches!(mod_edit, ModEdit::None)
|
||||
&& let Some(pos) = cursor.find_ident(old_name)
|
||||
{
|
||||
match mod_edit {
|
||||
ModEdit::Rename => {
|
||||
dst.push_str(&src[copy_pos as usize..pos as usize]);
|
||||
dst.push_str(new_name);
|
||||
copy_pos = cursor.pos();
|
||||
changed = true;
|
||||
},
|
||||
ModEdit::Delete if cursor.match_pat(cursor::Pat::Semi) => {
|
||||
let mut start = &src[copy_pos as usize..match_start as usize];
|
||||
if start.ends_with("\n\n") {
|
||||
start = &start[..start.len() - 1];
|
||||
}
|
||||
dst.push_str(start);
|
||||
copy_pos = cursor.pos();
|
||||
if src[copy_pos as usize..].starts_with("\n\n") {
|
||||
copy_pos += 1;
|
||||
}
|
||||
changed = true;
|
||||
},
|
||||
ModEdit::Delete | ModEdit::None => {},
|
||||
}
|
||||
if rename_mod && let Some(pos) = cursor.match_ident(old_name) {
|
||||
dst.push_str(&src[copy_pos as usize..pos as usize]);
|
||||
dst.push_str(new_name);
|
||||
copy_pos = cursor.pos();
|
||||
changed = true;
|
||||
}
|
||||
},
|
||||
// lint_name::
|
||||
name if matches!(mod_edit, ModEdit::Rename) && name == old_name => {
|
||||
name if rename_mod && name == old_name => {
|
||||
let name_end = cursor.pos();
|
||||
if cursor.match_pat(cursor::Pat::DoubleColon) {
|
||||
dst.push_str(&src[copy_pos as usize..match_start as usize]);
|
||||
|
|
@ -23,13 +23,12 @@ extern crate rustc_arena;
|
|||
extern crate rustc_driver;
|
||||
extern crate rustc_lexer;
|
||||
|
||||
pub mod deprecate_lint;
|
||||
pub mod dogfood;
|
||||
pub mod edit_lints;
|
||||
pub mod fmt;
|
||||
pub mod lint;
|
||||
pub mod new_lint;
|
||||
pub mod release;
|
||||
pub mod rename_lint;
|
||||
pub mod serve;
|
||||
pub mod setup;
|
||||
pub mod sync;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use clippy_dev::{
|
||||
ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, new_parse_cx, release, rename_lint, serve,
|
||||
setup, sync, update_lints,
|
||||
ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, serve, setup, sync,
|
||||
update_lints,
|
||||
};
|
||||
use std::env;
|
||||
|
||||
|
|
@ -74,21 +74,14 @@ fn main() {
|
|||
},
|
||||
DevCommand::Serve { port, lint } => serve::run(port, lint),
|
||||
DevCommand::Lint { path, edition, args } => lint::run(&path, &edition, args.iter()),
|
||||
DevCommand::RenameLint {
|
||||
old_name,
|
||||
new_name,
|
||||
uplift,
|
||||
} => new_parse_cx(|cx| {
|
||||
rename_lint::rename(
|
||||
cx,
|
||||
clippy.version,
|
||||
&old_name,
|
||||
new_name.as_ref().unwrap_or(&old_name),
|
||||
uplift,
|
||||
);
|
||||
DevCommand::RenameLint { old_name, new_name } => new_parse_cx(|cx| {
|
||||
edit_lints::rename(cx, clippy.version, &old_name, &new_name);
|
||||
}),
|
||||
DevCommand::Uplift { old_name, new_name } => new_parse_cx(|cx| {
|
||||
edit_lints::uplift(cx, clippy.version, &old_name, new_name.as_deref().unwrap_or(&old_name));
|
||||
}),
|
||||
DevCommand::Deprecate { name, reason } => {
|
||||
new_parse_cx(|cx| deprecate_lint::deprecate(cx, clippy.version, &name, &reason));
|
||||
new_parse_cx(|cx| edit_lints::deprecate(cx, clippy.version, &name, &reason));
|
||||
},
|
||||
DevCommand::Sync(SyncCommand { subcommand }) => match subcommand {
|
||||
SyncSubcommand::UpdateNightly => sync::update_nightly(),
|
||||
|
|
@ -243,15 +236,9 @@ enum DevCommand {
|
|||
/// The name of the lint to rename
|
||||
#[arg(value_parser = lint_name)]
|
||||
old_name: String,
|
||||
#[arg(
|
||||
required_unless_present = "uplift",
|
||||
value_parser = lint_name,
|
||||
)]
|
||||
#[arg(value_parser = lint_name)]
|
||||
/// The new name of the lint
|
||||
new_name: Option<String>,
|
||||
#[arg(long)]
|
||||
/// This lint will be uplifted into rustc
|
||||
uplift: bool,
|
||||
new_name: String,
|
||||
},
|
||||
/// Deprecate the given lint
|
||||
Deprecate {
|
||||
|
|
@ -266,6 +253,15 @@ enum DevCommand {
|
|||
Sync(SyncCommand),
|
||||
/// Manage Clippy releases
|
||||
Release(ReleaseCommand),
|
||||
/// Marks a lint as uplifted into rustc and removes its code
|
||||
Uplift {
|
||||
/// The name of the lint to uplift
|
||||
#[arg(value_parser = lint_name)]
|
||||
old_name: String,
|
||||
/// The name of the lint in rustc
|
||||
#[arg(value_parser = lint_name)]
|
||||
new_name: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
|
|
|||
|
|
@ -219,6 +219,22 @@ impl<'txt> Cursor<'txt> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Consume the returns the position of the next non-whitespace token if it's an
|
||||
/// identifier. Returns `None` otherwise.
|
||||
pub fn match_ident(&mut self, s: &str) -> Option<u32> {
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
TokenKind::Ident if s == self.peek_text() => {
|
||||
let pos = self.pos;
|
||||
self.step();
|
||||
return Some(pos);
|
||||
},
|
||||
TokenKind::Whitespace => self.step(),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Continually attempt to match the pattern on subsequent tokens until a match is
|
||||
/// found. Returns whether the pattern was successfully matched.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
|
|||
}
|
||||
|
||||
// Delay to avoid updating the metadata too aggressively.
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
|
|
|||
|
|
@ -468,6 +468,10 @@ declare_clippy_lint! {
|
|||
/// #[ignore = "Some good reason"]
|
||||
/// fn test() {}
|
||||
/// ```
|
||||
///
|
||||
/// ### Note
|
||||
/// Clippy can only lint compiled code. For this lint to trigger, you must configure `cargo clippy`
|
||||
/// to include test compilation, for instance, by using flags such as `--tests` or `--all-targets`.
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub IGNORE_WITHOUT_REASON,
|
||||
pedantic,
|
||||
|
|
|
|||
|
|
@ -145,6 +145,12 @@ declare_clippy_lint! {
|
|||
/// let _ = i32::try_from(u32::MAX).ok();
|
||||
/// ```
|
||||
///
|
||||
/// If the wrapping is intended, you can use:
|
||||
/// ```no_run
|
||||
/// let _ = u32::MAX.cast_signed();
|
||||
/// let _ = (-1i32).cast_unsigned();
|
||||
/// ```
|
||||
///
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_POSSIBLE_WRAP,
|
||||
pedantic,
|
||||
|
|
|
|||
|
|
@ -17,10 +17,6 @@ declare_clippy_lint! {
|
|||
/// `if` is not guaranteed to be exhaustive and conditionals can get
|
||||
/// repetitive
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The match statement may be slower due to the compiler
|
||||
/// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// # fn a() {}
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::drop_forget_ref::FORGET_NON_DROP_INFO,
|
||||
crate::drop_forget_ref::MEM_FORGET_INFO,
|
||||
crate::duplicate_mod::DUPLICATE_MOD_INFO,
|
||||
crate::duration_suboptimal_units::DURATION_SUBOPTIMAL_UNITS_INFO,
|
||||
crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
|
||||
crate::empty_drop::EMPTY_DROP_INFO,
|
||||
crate::empty_enums::EMPTY_ENUMS_INFO,
|
||||
|
|
@ -296,6 +297,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::manual_assert::MANUAL_ASSERT_INFO,
|
||||
crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
|
||||
crate::manual_bits::MANUAL_BITS_INFO,
|
||||
crate::manual_checked_ops::MANUAL_CHECKED_OPS_INFO,
|
||||
crate::manual_clamp::MANUAL_CLAMP_INFO,
|
||||
crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
|
||||
crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
|
||||
|
|
@ -315,6 +317,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
|
||||
crate::manual_string_new::MANUAL_STRING_NEW_INFO,
|
||||
crate::manual_strip::MANUAL_STRIP_INFO,
|
||||
crate::manual_take::MANUAL_TAKE_INFO,
|
||||
crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO,
|
||||
crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO,
|
||||
crate::match_result_ok::MATCH_RESULT_OK_INFO,
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ fn check_struct<'tcx>(
|
|||
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind
|
||||
&& let Some(PathSegment { args, .. }) = p.segments.last()
|
||||
{
|
||||
let args = args.map(|a| a.args).unwrap_or(&[]);
|
||||
let args = args.map(|a| a.args).unwrap_or_default();
|
||||
|
||||
// ty_args contains the generic parameters of the type declaration, while args contains the
|
||||
// arguments used at instantiation time. If both len are not equal, it means that some
|
||||
|
|
|
|||
|
|
@ -692,6 +692,12 @@ declare_clippy_lint! {
|
|||
/// ///
|
||||
/// /// It was chosen by a fair dice roll.
|
||||
/// ```
|
||||
///
|
||||
/// ### Terminal punctuation marks
|
||||
/// This lint treats these characters as end markers: '.', '?', '!', '…' and ':'.
|
||||
///
|
||||
/// The colon is not exactly a terminal punctuation mark, but this is required for paragraphs that
|
||||
/// introduce a table or a list for example.
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub DOC_PARAGRAPHS_MISSING_PUNCTUATION,
|
||||
restriction,
|
||||
|
|
|
|||
204
src/tools/clippy/clippy_lints/src/duration_suboptimal_units.rs
Normal file
204
src/tools/clippy/clippy_lints/src/duration_suboptimal_units.rs
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::sym;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, QPath, RustcVersion};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for instances where a `std::time::Duration` is constructed using a smaller time unit
|
||||
/// when the value could be expressed more clearly using a larger unit.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Using a smaller unit for a duration that is evenly divisible by a larger unit reduces
|
||||
/// readability. Readers have to mentally convert values, which can be error-prone and makes
|
||||
/// the code less clear.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let dur = Duration::from_millis(5_000);
|
||||
/// let dur = Duration::from_secs(180);
|
||||
/// let dur = Duration::from_mins(10 * 60);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let dur = Duration::from_secs(5);
|
||||
/// let dur = Duration::from_mins(3);
|
||||
/// let dur = Duration::from_hours(10);
|
||||
/// ```
|
||||
#[clippy::version = "1.95.0"]
|
||||
pub DURATION_SUBOPTIMAL_UNITS,
|
||||
pedantic,
|
||||
"constructing a `Duration` using a smaller unit when a larger unit would be more readable"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DurationSuboptimalUnits => [DURATION_SUBOPTIMAL_UNITS]);
|
||||
|
||||
pub struct DurationSuboptimalUnits {
|
||||
msrv: Msrv,
|
||||
units: Vec<Unit>,
|
||||
}
|
||||
|
||||
impl DurationSuboptimalUnits {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
// The order of the units matters, as they are walked top to bottom
|
||||
let mut units = UNITS.to_vec();
|
||||
if tcx.features().enabled(sym::duration_constructors) {
|
||||
units.extend(EXTENDED_UNITS);
|
||||
}
|
||||
Self { msrv: conf.msrv, units }
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for DurationSuboptimalUnits {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
if !expr.span.in_external_macro(cx.sess().source_map())
|
||||
// Check if a function on std::time::Duration is called
|
||||
&& let ExprKind::Call(func, [arg]) = expr.kind
|
||||
&& let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind
|
||||
&& cx
|
||||
.typeck_results()
|
||||
.node_type(func_ty.hir_id)
|
||||
.is_diag_item(cx, sym::Duration)
|
||||
// We intentionally don't want to evaluate referenced constants, as we don't want to
|
||||
// recommend a literal value over using constants:
|
||||
//
|
||||
// let dur = Duration::from_secs(SIXTY);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_mins(1)`
|
||||
&& let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt())
|
||||
&& let value = u64::try_from(value).expect("All Duration::from_<time-unit> constructors take a u64")
|
||||
// There is no need to promote e.g. 0 seconds to 0 hours
|
||||
&& value != 0
|
||||
&& let Some((promoted_constructor, promoted_value)) = self.promote(cx, func_name.ident.name, value)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DURATION_SUBOPTIMAL_UNITS,
|
||||
expr.span,
|
||||
"constructing a `Duration` using a smaller unit when a larger unit would be more readable",
|
||||
|diag| {
|
||||
let suggestions = vec![
|
||||
(func_name.ident.span, promoted_constructor.to_string()),
|
||||
(arg.span, promoted_value.to_string()),
|
||||
];
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("try using {promoted_constructor}"),
|
||||
suggestions,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DurationSuboptimalUnits {
|
||||
/// Tries to promote the given constructor and value to a bigger time unit and returns the
|
||||
/// promoted constructor name and value.
|
||||
///
|
||||
/// Returns [`None`] in case no promotion could be done.
|
||||
fn promote(&self, cx: &LateContext<'_>, constructor_name: Symbol, value: u64) -> Option<(Symbol, u64)> {
|
||||
let (best_unit, best_value) = self
|
||||
.units
|
||||
.iter()
|
||||
.skip_while(|unit| unit.constructor_name != constructor_name)
|
||||
.skip(1)
|
||||
.try_fold(
|
||||
(constructor_name, value),
|
||||
|(current_unit, current_value), bigger_unit| {
|
||||
if let Some(bigger_value) = current_value.div_exact(u64::from(bigger_unit.factor))
|
||||
&& bigger_unit.stable_since.is_none_or(|v| self.msrv.meets(cx, v))
|
||||
{
|
||||
ControlFlow::Continue((bigger_unit.constructor_name, bigger_value))
|
||||
} else {
|
||||
// We have to break early, as we can't skip versions, as they are needed to
|
||||
// correctly calculate the promoted value.
|
||||
ControlFlow::Break((current_unit, current_value))
|
||||
}
|
||||
},
|
||||
)
|
||||
.into_value();
|
||||
(best_unit != constructor_name).then_some((best_unit, best_value))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Unit {
|
||||
/// Name of the constructor on [`Duration`](std::time::Duration) to construct it from the given
|
||||
/// unit, e.g. [`Duration::from_secs`](std::time::Duration::from_secs)
|
||||
constructor_name: Symbol,
|
||||
|
||||
/// The increase factor over the previous (smaller) unit
|
||||
factor: u16,
|
||||
|
||||
/// In what rustc version stable support for this constructor was added.
|
||||
/// We do not need to track the version stable support in const contexts was added, as the const
|
||||
/// stabilization was done in an ascending order of the time unites, so it's always valid to
|
||||
/// promote a const constructor.
|
||||
stable_since: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
/// Time unit constructors available on stable. The order matters!
|
||||
const UNITS: [Unit; 6] = [
|
||||
Unit {
|
||||
constructor_name: sym::from_nanos,
|
||||
// The value doesn't matter, as there is no previous unit
|
||||
factor: 0,
|
||||
stable_since: Some(msrvs::DURATION_FROM_NANOS_MICROS),
|
||||
},
|
||||
Unit {
|
||||
constructor_name: sym::from_micros,
|
||||
factor: 1_000,
|
||||
stable_since: Some(msrvs::DURATION_FROM_NANOS_MICROS),
|
||||
},
|
||||
Unit {
|
||||
constructor_name: sym::from_millis,
|
||||
factor: 1_000,
|
||||
stable_since: Some(msrvs::DURATION_FROM_MILLIS_SECS),
|
||||
},
|
||||
Unit {
|
||||
constructor_name: sym::from_secs,
|
||||
factor: 1_000,
|
||||
stable_since: Some(msrvs::DURATION_FROM_MILLIS_SECS),
|
||||
},
|
||||
Unit {
|
||||
constructor_name: sym::from_mins,
|
||||
factor: 60,
|
||||
stable_since: Some(msrvs::DURATION_FROM_MINUTES_HOURS),
|
||||
},
|
||||
Unit {
|
||||
constructor_name: sym::from_hours,
|
||||
factor: 60,
|
||||
stable_since: Some(msrvs::DURATION_FROM_MINUTES_HOURS),
|
||||
},
|
||||
];
|
||||
|
||||
/// Time unit constructors behind the `duration_constructors` feature. The order matters!
|
||||
const EXTENDED_UNITS: [Unit; 2] = [
|
||||
Unit {
|
||||
constructor_name: sym::from_days,
|
||||
factor: 24,
|
||||
stable_since: None,
|
||||
},
|
||||
Unit {
|
||||
constructor_name: sym::from_weeks,
|
||||
factor: 7,
|
||||
stable_since: None,
|
||||
},
|
||||
];
|
||||
|
|
@ -1,761 +0,0 @@
|
|||
use clippy_utils::consts::Constant::{F32, F64, Int};
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
|
||||
use clippy_utils::{
|
||||
eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, is_no_std_crate,
|
||||
numeric_literal, peel_blocks, sugg, sym,
|
||||
};
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::SyntaxContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use std::f32::consts as f32_consts;
|
||||
use std::f64::consts as f64_consts;
|
||||
use sugg::Sugg;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for floating-point expressions that
|
||||
/// can be expressed using built-in methods to improve accuracy
|
||||
/// at the cost of performance.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Negatively impacts accuracy.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let a = 3f32;
|
||||
/// let _ = a.powf(1.0 / 3.0);
|
||||
/// let _ = (1.0 + a).ln();
|
||||
/// let _ = a.exp() - 1.0;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let a = 3f32;
|
||||
/// let _ = a.cbrt();
|
||||
/// let _ = a.ln_1p();
|
||||
/// let _ = a.exp_m1();
|
||||
/// ```
|
||||
#[clippy::version = "1.43.0"]
|
||||
pub IMPRECISE_FLOPS,
|
||||
nursery,
|
||||
"usage of imprecise floating point operations"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for floating-point expressions that
|
||||
/// can be expressed using built-in methods to improve both
|
||||
/// accuracy and performance.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Negatively impacts accuracy and performance.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// use std::f32::consts::E;
|
||||
///
|
||||
/// let a = 3f32;
|
||||
/// let _ = (2f32).powf(a);
|
||||
/// let _ = E.powf(a);
|
||||
/// let _ = a.powf(1.0 / 2.0);
|
||||
/// let _ = a.log(2.0);
|
||||
/// let _ = a.log(10.0);
|
||||
/// let _ = a.log(E);
|
||||
/// let _ = a.powf(2.0);
|
||||
/// let _ = a * 2.0 + 4.0;
|
||||
/// let _ = if a < 0.0 {
|
||||
/// -a
|
||||
/// } else {
|
||||
/// a
|
||||
/// };
|
||||
/// let _ = if a < 0.0 {
|
||||
/// a
|
||||
/// } else {
|
||||
/// -a
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// is better expressed as
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::f32::consts::E;
|
||||
///
|
||||
/// let a = 3f32;
|
||||
/// let _ = a.exp2();
|
||||
/// let _ = a.exp();
|
||||
/// let _ = a.sqrt();
|
||||
/// let _ = a.log2();
|
||||
/// let _ = a.log10();
|
||||
/// let _ = a.ln();
|
||||
/// let _ = a.powi(2);
|
||||
/// let _ = a.mul_add(2.0, 4.0);
|
||||
/// let _ = a.abs();
|
||||
/// let _ = -a.abs();
|
||||
/// ```
|
||||
#[clippy::version = "1.43.0"]
|
||||
pub SUBOPTIMAL_FLOPS,
|
||||
nursery,
|
||||
"usage of sub-optimal floating point operations"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FloatingPointArithmetic => [
|
||||
IMPRECISE_FLOPS,
|
||||
SUBOPTIMAL_FLOPS
|
||||
]);
|
||||
|
||||
// Returns the specialized log method for a given base if base is constant
|
||||
// and is one of 2, 10 and e
|
||||
fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>, ctxt: SyntaxContext) -> Option<&'static str> {
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval_local(base, ctxt) {
|
||||
if F32(2.0) == value || F64(2.0) == value {
|
||||
return Some("log2");
|
||||
} else if F32(10.0) == value || F64(10.0) == value {
|
||||
return Some("log10");
|
||||
} else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
return Some("ln");
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// Adds type suffixes and parenthesis to method receivers if necessary
|
||||
fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Sugg<'a> {
|
||||
let mut suggestion = Sugg::hir(cx, expr, "..");
|
||||
|
||||
if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind {
|
||||
expr = inner_expr;
|
||||
}
|
||||
|
||||
if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind()
|
||||
// if the expression is a float literal and it is unsuffixed then
|
||||
// add a suffix so the suggestion is valid and unambiguous
|
||||
&& let ExprKind::Lit(lit) = &expr.kind
|
||||
&& let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node
|
||||
{
|
||||
let op = format!(
|
||||
"{suggestion}{}{}",
|
||||
// Check for float literals without numbers following the decimal
|
||||
// separator such as `2.` and adds a trailing zero
|
||||
if sym.as_str().ends_with('.') { "0" } else { "" },
|
||||
float_ty.name_str()
|
||||
)
|
||||
.into();
|
||||
|
||||
suggestion = match suggestion {
|
||||
Sugg::MaybeParen(_) | Sugg::UnOp(UnOp::Neg, _) => Sugg::MaybeParen(op),
|
||||
_ => Sugg::NonParen(op),
|
||||
};
|
||||
}
|
||||
|
||||
suggestion.maybe_paren()
|
||||
}
|
||||
|
||||
fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if let Some(method) = get_specialized_log_method(cx, &args[0], expr.span.ctxt()) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"logarithm for bases 2, 10 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_paren()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
|
||||
// suggest usage of `(x + (y - 1)).ln_1p()` instead
|
||||
fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Add, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = receiver.kind
|
||||
{
|
||||
let ecx = ConstEvalCtxt::new(cx);
|
||||
let recv = match (ecx.eval(lhs), ecx.eval(rhs)) {
|
||||
(Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs,
|
||||
(_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IMPRECISE_FLOPS,
|
||||
expr.span,
|
||||
"ln(1 + x) can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an integer if the float constant is a whole number and it can be
|
||||
// converted to an integer without loss of precision. For now we only check
|
||||
// ranges [-16777215, 16777216) for type f32 as whole number floats outside
|
||||
// this range are lossy and ambiguous.
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
|
||||
match value {
|
||||
F32(num) if num.fract() == 0.0 => {
|
||||
if (-16_777_215.0..16_777_216.0).contains(num) {
|
||||
Some(num.round() as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
F64(num) if num.fract() == 0.0 => {
|
||||
if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
|
||||
Some(num.round() as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
// Check receiver
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver)
|
||||
&& let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
Some("exp")
|
||||
} else if F32(2.0) == value || F64(2.0) == value {
|
||||
Some("exp2")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"exponent for bases 2 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
// Check argument
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) {
|
||||
let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
|
||||
(
|
||||
SUBOPTIMAL_FLOPS,
|
||||
"square-root of a number can be computed more efficiently and accurately",
|
||||
format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_paren()),
|
||||
)
|
||||
} else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
|
||||
(
|
||||
IMPRECISE_FLOPS,
|
||||
"cube-root of a number can be computed more accurately",
|
||||
format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_paren()),
|
||||
)
|
||||
} else if let Some(exponent) = get_integer_from_float_constant(&value) {
|
||||
(
|
||||
SUBOPTIMAL_FLOPS,
|
||||
"exponentiation with integer powers can be computed more efficiently",
|
||||
format!(
|
||||
"{}.powi({})",
|
||||
Sugg::hir(cx, receiver, "..").maybe_paren(),
|
||||
numeric_literal::format(&exponent.to_string(), None, false)
|
||||
),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
help,
|
||||
"consider using",
|
||||
suggestion,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0])
|
||||
&& value == Int(2)
|
||||
&& let Some(parent) = get_parent_expr(cx, expr)
|
||||
{
|
||||
if let Some(grandparent) = get_parent_expr(cx, parent)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = grandparent.kind
|
||||
&& method.name == sym::sqrt
|
||||
&& detect_hypot(cx, receiver).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = parent.kind
|
||||
{
|
||||
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
|
||||
|
||||
// Negate expr if original code has subtraction and expr is on the right side
|
||||
let maybe_neg_sugg = |expr, hir_id| {
|
||||
let sugg = Sugg::hir(cx, expr, "..");
|
||||
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
|
||||
-sugg
|
||||
} else {
|
||||
sugg
|
||||
}
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
parent.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
Sugg::hir(cx, receiver, "..").maybe_paren(),
|
||||
maybe_neg_sugg(receiver, expr.hir_id),
|
||||
maybe_neg_sugg(other_addend, other_addend.hir_id),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Add, ..
|
||||
},
|
||||
add_lhs,
|
||||
add_rhs,
|
||||
) = receiver.kind
|
||||
{
|
||||
// check if expression of the form x * x + y * y
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
lmul_lhs,
|
||||
lmul_rhs,
|
||||
) = add_lhs.kind
|
||||
&& let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
rmul_lhs,
|
||||
rmul_rhs,
|
||||
) = add_rhs.kind
|
||||
&& eq_expr_value(cx, lmul_lhs, lmul_rhs)
|
||||
&& eq_expr_value(cx, rmul_lhs, rmul_rhs)
|
||||
{
|
||||
return Some(format!(
|
||||
"{}.hypot({})",
|
||||
Sugg::hir(cx, lmul_lhs, "..").maybe_paren(),
|
||||
Sugg::hir(cx, rmul_lhs, "..")
|
||||
));
|
||||
}
|
||||
|
||||
// check if expression of the form x.powi(2) + y.powi(2)
|
||||
if let ExprKind::MethodCall(PathSegment { ident: lmethod, .. }, largs_0, [largs_1, ..], _) = &add_lhs.kind
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: rmethod, .. }, rargs_0, [rargs_1, ..], _) = &add_rhs.kind
|
||||
&& lmethod.name == sym::powi
|
||||
&& rmethod.name == sym::powi
|
||||
&& let ecx = ConstEvalCtxt::new(cx)
|
||||
&& let Some(lvalue) = ecx.eval(largs_1)
|
||||
&& let Some(rvalue) = ecx.eval(rargs_1)
|
||||
&& Int(2) == lvalue
|
||||
&& Int(2) == rvalue
|
||||
{
|
||||
return Some(format!(
|
||||
"{}.hypot({})",
|
||||
Sugg::hir(cx, largs_0, "..").maybe_paren(),
|
||||
Sugg::hir(cx, rargs_0, "..")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
|
||||
if let Some(message) = detect_hypot(cx, receiver) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IMPRECISE_FLOPS,
|
||||
expr.span,
|
||||
"hypotenuse can be computed more accurately",
|
||||
"consider using",
|
||||
message,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Lint expressions of the form `x.exp() - y` where y > 1
|
||||
// and suggest usage of `x.exp_m1() - (y - 1)` instead
|
||||
fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Sub, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = expr.kind
|
||||
&& let ExprKind::MethodCall(path, self_arg, [], _) = &lhs.kind
|
||||
&& path.ident.name == sym::exp
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
&& let Some(value) = ConstEvalCtxt::new(cx).eval(rhs)
|
||||
&& (F32(1.0) == value || F64(1.0) == value)
|
||||
&& cx.typeck_results().expr_ty(self_arg).is_floating_point()
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IMPRECISE_FLOPS,
|
||||
expr.span,
|
||||
"(e.pow(x) - 1) can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_paren()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = &expr.kind
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
|
||||
{
|
||||
return Some((lhs, rhs));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = &expr.kind
|
||||
{
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = parent.kind
|
||||
&& method.name == sym::sqrt
|
||||
&& detect_hypot(cx, receiver).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let maybe_neg_sugg = |expr| {
|
||||
let sugg = Sugg::hir(cx, expr, "..");
|
||||
if let BinOpKind::Sub = op { -sugg } else { sugg }
|
||||
};
|
||||
|
||||
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs)
|
||||
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
|
||||
{
|
||||
(inner_lhs, Sugg::hir(cx, inner_rhs, ".."), maybe_neg_sugg(rhs))
|
||||
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs)
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
{
|
||||
(inner_lhs, maybe_neg_sugg(inner_rhs), Sugg::hir(cx, lhs, ".."))
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Check if any variable in the expression has an ambiguous type (could be f32 or f64)
|
||||
// see: https://github.com/rust-lang/rust-clippy/issues/14897
|
||||
if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _)))
|
||||
&& has_ambiguous_literal_in_expr(cx, recv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
"consider using",
|
||||
format!("{}.mul_add({arg1}, {arg2})", prepare_receiver_sugg(cx, recv)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true iff expr is an expression which tests whether or not
|
||||
/// test is positive or an expression which tests whether or not test
|
||||
/// is nonnegative.
|
||||
/// Used for check-custom-abs function below
|
||||
fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
|
||||
match op {
|
||||
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test),
|
||||
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`is_testing_positive`]
|
||||
fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
|
||||
match op {
|
||||
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test),
|
||||
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true iff expr is some zero literal
|
||||
fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool {
|
||||
match ConstEvalCtxt::new(cx).eval_local(expr, ctxt) {
|
||||
Some(Int(i)) => i == 0,
|
||||
Some(F32(f)) => f == 0.0,
|
||||
Some(F64(f)) => f == 0.0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// If the two expressions are negations of each other, then it returns
|
||||
/// a tuple, in which the first element is true iff expr1 is the
|
||||
/// positive expressions, and the second element is the positive
|
||||
/// one of the two expressions
|
||||
/// If the two expressions are not negations of each other, then it
|
||||
/// returns None.
|
||||
fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
|
||||
if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind
|
||||
&& eq_expr_value(cx, expr1_negated, expr2)
|
||||
{
|
||||
return Some((false, expr2));
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind
|
||||
&& eq_expr_value(cx, expr1, expr2_negated)
|
||||
{
|
||||
return Some((true, expr1));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(higher::If {
|
||||
cond,
|
||||
then,
|
||||
r#else: Some(r#else),
|
||||
}) = higher::If::hir(expr)
|
||||
&& let if_body_expr = peel_blocks(then)
|
||||
&& let else_body_expr = peel_blocks(r#else)
|
||||
&& let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr)
|
||||
{
|
||||
let positive_abs_sugg = (
|
||||
"manual implementation of `abs` method",
|
||||
format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()),
|
||||
);
|
||||
let negative_abs_sugg = (
|
||||
"manual implementation of negation of `abs` method",
|
||||
format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()),
|
||||
);
|
||||
let sugg = if is_testing_positive(cx, cond, body) {
|
||||
if if_expr_positive {
|
||||
positive_abs_sugg
|
||||
} else {
|
||||
negative_abs_sugg
|
||||
}
|
||||
} else if is_testing_negative(cx, cond, body) {
|
||||
if if_expr_positive {
|
||||
negative_abs_sugg
|
||||
} else {
|
||||
positive_abs_sugg
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
sugg.0,
|
||||
"try",
|
||||
sugg.1,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_a, .. }, _, args_a, _) = expr_a.kind
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method_b, .. }, _, args_b, _) = expr_b.kind
|
||||
{
|
||||
return method_a.name == method_b.name
|
||||
&& args_a.len() == args_b.len()
|
||||
&& (matches!(method_a.name, sym::ln | sym::log2 | sym::log10)
|
||||
|| method_a.name == sym::log && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0]));
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
// check if expression of the form x.logN() / y.logN()
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Div, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = &expr.kind
|
||||
&& are_same_base_logs(cx, lhs, rhs)
|
||||
&& let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind
|
||||
&& let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"log base can be expressed more clearly",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.log({})",
|
||||
Sugg::hir(cx, largs_self, "..").maybe_paren(),
|
||||
Sugg::hir(cx, rargs_self, ".."),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Div, ..
|
||||
},
|
||||
div_lhs,
|
||||
div_rhs,
|
||||
) = &expr.kind
|
||||
&& let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
mul_lhs,
|
||||
mul_rhs,
|
||||
) = &div_lhs.kind
|
||||
&& let ecx = ConstEvalCtxt::new(cx)
|
||||
&& let Some(rvalue) = ecx.eval(div_rhs)
|
||||
&& let Some(lvalue) = ecx.eval(mul_rhs)
|
||||
{
|
||||
// TODO: also check for constant values near PI/180 or 180/PI
|
||||
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue)
|
||||
&& (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
|
||||
{
|
||||
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_paren());
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind
|
||||
&& let ast::LitKind::Float(ref value, float_type) = literal.node
|
||||
&& float_type == ast::LitFloatType::Unsuffixed
|
||||
{
|
||||
if value.as_str().ends_with('.') {
|
||||
proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
} else {
|
||||
proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
}
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"conversion to degrees can be done more accurately",
|
||||
"consider using",
|
||||
proposal,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue)
|
||||
&& (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
|
||||
{
|
||||
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_paren());
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind
|
||||
&& let ast::LitKind::Float(ref value, float_type) = literal.node
|
||||
&& float_type == ast::LitFloatType::Unsuffixed
|
||||
{
|
||||
if value.as_str().ends_with('.') {
|
||||
proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
} else {
|
||||
proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
}
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"conversion to radians can be done more accurately",
|
||||
"consider using",
|
||||
proposal,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
// All of these operations are currently not const and are in std.
|
||||
if is_in_const_context(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind {
|
||||
let recv_ty = cx.typeck_results().expr_ty(receiver);
|
||||
|
||||
if recv_ty.is_floating_point() && !is_no_std_crate(cx) && cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) {
|
||||
match path.ident.name {
|
||||
sym::ln => check_ln1p(cx, expr, receiver),
|
||||
sym::log => check_log_base(cx, expr, receiver, args),
|
||||
sym::powf => check_powf(cx, expr, receiver, args),
|
||||
sym::powi => check_powi(cx, expr, receiver, args),
|
||||
sym::sqrt => check_hypot(cx, expr, receiver),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !is_no_std_crate(cx) {
|
||||
check_expm1(cx, expr);
|
||||
check_mul_add(cx, expr);
|
||||
check_custom_abs(cx, expr);
|
||||
check_log_division(cx, expr);
|
||||
}
|
||||
check_radians(cx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
use clippy_utils::consts::ConstEvalCtxt;
|
||||
use clippy_utils::consts::Constant::{F32, F64, Int};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{eq_expr_value, higher, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::SyntaxContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::SUBOPTIMAL_FLOPS;
|
||||
|
||||
/// Returns true iff expr is an expression which tests whether or not
|
||||
/// test is positive or an expression which tests whether or not test
|
||||
/// is nonnegative.
|
||||
/// Used for check-custom-abs function below
|
||||
fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
|
||||
match op {
|
||||
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test),
|
||||
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`is_testing_positive`]
|
||||
fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
|
||||
match op {
|
||||
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test),
|
||||
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true iff expr is some zero literal
|
||||
fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool {
|
||||
match ConstEvalCtxt::new(cx).eval_local(expr, ctxt) {
|
||||
Some(Int(i)) => i == 0,
|
||||
Some(F32(f)) => f == 0.0,
|
||||
Some(F64(f)) => f == 0.0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// If the two expressions are negations of each other, then it returns
|
||||
/// a tuple, in which the first element is true iff expr1 is the
|
||||
/// positive expressions, and the second element is the positive
|
||||
/// one of the two expressions
|
||||
/// If the two expressions are not negations of each other, then it
|
||||
/// returns None.
|
||||
fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
|
||||
if let ExprKind::Unary(UnOp::Neg, expr1_negated) = expr1.kind
|
||||
&& eq_expr_value(cx, expr1_negated, expr2)
|
||||
{
|
||||
return Some((false, expr2));
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Neg, expr2_negated) = expr2.kind
|
||||
&& eq_expr_value(cx, expr1, expr2_negated)
|
||||
{
|
||||
return Some((true, expr1));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(higher::If {
|
||||
cond,
|
||||
then,
|
||||
r#else: Some(r#else),
|
||||
}) = higher::If::hir(expr)
|
||||
&& let if_body_expr = peel_blocks(then)
|
||||
&& let else_body_expr = peel_blocks(r#else)
|
||||
&& let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr)
|
||||
{
|
||||
let sugg_positive_abs = if is_testing_positive(cx, cond, body) {
|
||||
if_expr_positive
|
||||
} else if is_testing_negative(cx, cond, body) {
|
||||
!if_expr_positive
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let body = Sugg::hir_with_applicability(cx, body, "_", &mut app).maybe_paren();
|
||||
let sugg = if sugg_positive_abs {
|
||||
("manual implementation of `abs` method", format!("{body}.abs()"))
|
||||
} else {
|
||||
#[rustfmt::skip]
|
||||
("manual implementation of negation of `abs` method", format!("-{body}.abs()"))
|
||||
};
|
||||
span_lint_and_sugg(cx, SUBOPTIMAL_FLOPS, expr.span, sugg.0, "try", sugg.1, app);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
use clippy_utils::consts::ConstEvalCtxt;
|
||||
use clippy_utils::consts::Constant::{F32, F64};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::sym;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::IMPRECISE_FLOPS;
|
||||
|
||||
// TODO: Lint expressions of the form `x.exp() - y` where y > 1
|
||||
// and suggest usage of `x.exp_m1() - (y - 1)` instead
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Sub, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = expr.kind
|
||||
&& let ExprKind::MethodCall(path, self_arg, [], _) = lhs.kind
|
||||
&& path.ident.name == sym::exp
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
&& let Some(value) = ConstEvalCtxt::new(cx).eval(rhs)
|
||||
&& (F32(1.0) == value || F64(1.0) == value)
|
||||
&& cx.typeck_results().expr_ty(self_arg).is_floating_point()
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IMPRECISE_FLOPS,
|
||||
expr.span,
|
||||
"(e.pow(x) - 1) can be computed more accurately",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv = Sugg::hir_with_applicability(cx, self_arg, "_", &mut app).maybe_paren();
|
||||
diag.span_suggestion(expr.span, "consider using", format!("{recv}.exp_m1()"), app);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
use clippy_utils::consts::ConstEvalCtxt;
|
||||
use clippy_utils::consts::Constant::Int;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{eq_expr_value, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::IMPRECISE_FLOPS;
|
||||
|
||||
pub(super) fn detect(cx: &LateContext<'_>, receiver: &Expr<'_>, app: &mut Applicability) -> Option<String> {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Add, ..
|
||||
},
|
||||
add_lhs,
|
||||
add_rhs,
|
||||
) = receiver.kind
|
||||
{
|
||||
// check if expression of the form x * x + y * y
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
lmul_lhs,
|
||||
lmul_rhs,
|
||||
) = add_lhs.kind
|
||||
&& let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
rmul_lhs,
|
||||
rmul_rhs,
|
||||
) = add_rhs.kind
|
||||
&& eq_expr_value(cx, lmul_lhs, lmul_rhs)
|
||||
&& eq_expr_value(cx, rmul_lhs, rmul_rhs)
|
||||
{
|
||||
return Some(format!(
|
||||
"{}.hypot({})",
|
||||
Sugg::hir_with_applicability(cx, lmul_lhs, "_", app).maybe_paren(),
|
||||
Sugg::hir_with_applicability(cx, rmul_lhs, "_", app)
|
||||
));
|
||||
}
|
||||
|
||||
// check if expression of the form x.powi(2) + y.powi(2)
|
||||
if let ExprKind::MethodCall(PathSegment { ident: lmethod, .. }, largs_0, [largs_1, ..], _) = add_lhs.kind
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: rmethod, .. }, rargs_0, [rargs_1, ..], _) = add_rhs.kind
|
||||
&& lmethod.name == sym::powi
|
||||
&& rmethod.name == sym::powi
|
||||
&& let ecx = ConstEvalCtxt::new(cx)
|
||||
&& let Some(lvalue) = ecx.eval(largs_1)
|
||||
&& let Some(rvalue) = ecx.eval(rargs_1)
|
||||
&& Int(2) == lvalue
|
||||
&& Int(2) == rvalue
|
||||
{
|
||||
return Some(format!(
|
||||
"{}.hypot({})",
|
||||
Sugg::hir_with_applicability(cx, largs_0, "_", app).maybe_paren(),
|
||||
Sugg::hir_with_applicability(cx, rargs_0, "_", app)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
if let Some(message) = detect(cx, receiver, &mut app) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IMPRECISE_FLOPS,
|
||||
expr.span,
|
||||
"hypotenuse can be computed more accurately",
|
||||
"consider using",
|
||||
message,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
||||
// Adds type suffixes and parenthesis to method receivers if necessary
|
||||
pub(super) fn prepare_receiver_sugg<'a>(
|
||||
cx: &LateContext<'_>,
|
||||
mut expr: &'a Expr<'a>,
|
||||
app: &mut Applicability,
|
||||
) -> Sugg<'a> {
|
||||
let mut suggestion = Sugg::hir_with_applicability(cx, expr, "_", app);
|
||||
|
||||
if let ExprKind::Unary(UnOp::Neg, inner_expr) = expr.kind {
|
||||
expr = inner_expr;
|
||||
}
|
||||
|
||||
if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind()
|
||||
// if the expression is a float literal and it is unsuffixed then
|
||||
// add a suffix so the suggestion is valid and unambiguous
|
||||
&& let ExprKind::Lit(lit) = expr.kind
|
||||
&& let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node
|
||||
{
|
||||
let op = format!(
|
||||
"{suggestion}{}{}",
|
||||
// Check for float literals without numbers following the decimal
|
||||
// separator such as `2.` and adds a trailing zero
|
||||
if sym.as_str().ends_with('.') { "0" } else { "" },
|
||||
float_ty.name_str()
|
||||
)
|
||||
.into();
|
||||
|
||||
suggestion = match suggestion {
|
||||
Sugg::MaybeParen(_) | Sugg::UnOp(UnOp::Neg, _) => Sugg::MaybeParen(op),
|
||||
_ => Sugg::NonParen(op),
|
||||
};
|
||||
}
|
||||
|
||||
suggestion.maybe_paren()
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
use clippy_utils::consts::ConstEvalCtxt;
|
||||
use clippy_utils::consts::Constant::{F32, F64};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::IMPRECISE_FLOPS;
|
||||
|
||||
// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
|
||||
// suggest usage of `(x + (y - 1)).ln_1p()` instead
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Add, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = receiver.kind
|
||||
{
|
||||
let ecx = ConstEvalCtxt::new(cx);
|
||||
let recv = match (ecx.eval(lhs), ecx.eval(rhs)) {
|
||||
(Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs,
|
||||
(_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IMPRECISE_FLOPS,
|
||||
expr.span,
|
||||
"ln(1 + x) can be computed more accurately",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv = super::lib::prepare_receiver_sugg(cx, recv, &mut app);
|
||||
diag.span_suggestion(expr.span, "consider using", format!("{recv}.ln_1p()"), app);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
use clippy_utils::consts::ConstEvalCtxt;
|
||||
use clippy_utils::consts::Constant::{F32, F64};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::SyntaxContext;
|
||||
use std::f32::consts as f32_consts;
|
||||
use std::f64::consts as f64_consts;
|
||||
|
||||
use super::SUBOPTIMAL_FLOPS;
|
||||
|
||||
// Returns the specialized log method for a given base if base is constant
|
||||
// and is one of 2, 10 and e
|
||||
fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>, ctxt: SyntaxContext) -> Option<&'static str> {
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval_local(base, ctxt) {
|
||||
if F32(2.0) == value || F64(2.0) == value {
|
||||
return Some("log2");
|
||||
} else if F32(10.0) == value || F64(10.0) == value {
|
||||
return Some("log10");
|
||||
} else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
return Some("ln");
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if let Some(method) = get_specialized_log_method(cx, &args[0], expr.span.ctxt()) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"logarithm for bases 2, 10 and e can be computed more accurately",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv = Sugg::hir_with_applicability(cx, receiver, "_", &mut app).maybe_paren();
|
||||
diag.span_suggestion(expr.span, "consider using", format!("{recv}.{method}()"), app);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{eq_expr_value, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::SUBOPTIMAL_FLOPS;
|
||||
|
||||
fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_a, .. }, _, args_a, _) = expr_a.kind
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method_b, .. }, _, args_b, _) = expr_b.kind
|
||||
{
|
||||
return method_a.name == method_b.name
|
||||
&& args_a.len() == args_b.len()
|
||||
&& (matches!(method_a.name, sym::ln | sym::log2 | sym::log10)
|
||||
|| method_a.name == sym::log && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0]));
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
// check if expression of the form x.logN() / y.logN()
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Div, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = expr.kind
|
||||
&& are_same_base_logs(cx, lhs, rhs)
|
||||
&& let ExprKind::MethodCall(_, largs_self, ..) = lhs.kind
|
||||
&& let ExprKind::MethodCall(_, rargs_self, ..) = rhs.kind
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"log base can be expressed more clearly",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.log({})",
|
||||
Sugg::hir_with_applicability(cx, largs_self, "_", &mut app).maybe_paren(),
|
||||
Sugg::hir_with_applicability(cx, rargs_self, "_", &mut app),
|
||||
),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
|
||||
use clippy_utils::{is_in_const_context, is_no_std_crate, sym};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
mod custom_abs;
|
||||
mod expm1;
|
||||
mod hypot;
|
||||
mod lib;
|
||||
mod ln1p;
|
||||
mod log_base;
|
||||
mod log_division;
|
||||
mod mul_add;
|
||||
mod powf;
|
||||
mod powi;
|
||||
mod radians;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for floating-point expressions that
|
||||
/// can be expressed using built-in methods to improve accuracy
|
||||
/// at the cost of performance.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Negatively impacts accuracy.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let a = 3f32;
|
||||
/// let _ = a.powf(1.0 / 3.0);
|
||||
/// let _ = (1.0 + a).ln();
|
||||
/// let _ = a.exp() - 1.0;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let a = 3f32;
|
||||
/// let _ = a.cbrt();
|
||||
/// let _ = a.ln_1p();
|
||||
/// let _ = a.exp_m1();
|
||||
/// ```
|
||||
#[clippy::version = "1.43.0"]
|
||||
pub IMPRECISE_FLOPS,
|
||||
nursery,
|
||||
"usage of imprecise floating point operations"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for floating-point expressions that
|
||||
/// can be expressed using built-in methods to improve both
|
||||
/// accuracy and performance.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Negatively impacts accuracy and performance.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// use std::f32::consts::E;
|
||||
///
|
||||
/// let a = 3f32;
|
||||
/// let _ = (2f32).powf(a);
|
||||
/// let _ = E.powf(a);
|
||||
/// let _ = a.powf(1.0 / 2.0);
|
||||
/// let _ = a.log(2.0);
|
||||
/// let _ = a.log(10.0);
|
||||
/// let _ = a.log(E);
|
||||
/// let _ = a.powf(2.0);
|
||||
/// let _ = a * 2.0 + 4.0;
|
||||
/// let _ = if a < 0.0 {
|
||||
/// -a
|
||||
/// } else {
|
||||
/// a
|
||||
/// };
|
||||
/// let _ = if a < 0.0 {
|
||||
/// a
|
||||
/// } else {
|
||||
/// -a
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// is better expressed as
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::f32::consts::E;
|
||||
///
|
||||
/// let a = 3f32;
|
||||
/// let _ = a.exp2();
|
||||
/// let _ = a.exp();
|
||||
/// let _ = a.sqrt();
|
||||
/// let _ = a.log2();
|
||||
/// let _ = a.log10();
|
||||
/// let _ = a.ln();
|
||||
/// let _ = a.powi(2);
|
||||
/// let _ = a.mul_add(2.0, 4.0);
|
||||
/// let _ = a.abs();
|
||||
/// let _ = -a.abs();
|
||||
/// ```
|
||||
#[clippy::version = "1.43.0"]
|
||||
pub SUBOPTIMAL_FLOPS,
|
||||
nursery,
|
||||
"usage of sub-optimal floating point operations"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FloatingPointArithmetic => [
|
||||
IMPRECISE_FLOPS,
|
||||
SUBOPTIMAL_FLOPS
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
// All of these operations are currently not const and are in std.
|
||||
if is_in_const_context(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::MethodCall(path, receiver, args, _) = expr.kind {
|
||||
let recv_ty = cx.typeck_results().expr_ty(receiver);
|
||||
|
||||
if recv_ty.is_floating_point() && !is_no_std_crate(cx) && cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) {
|
||||
match path.ident.name {
|
||||
sym::ln => ln1p::check(cx, expr, receiver),
|
||||
sym::log => log_base::check(cx, expr, receiver, args),
|
||||
sym::powf => powf::check(cx, expr, receiver, args),
|
||||
sym::powi => powi::check(cx, expr, receiver, args),
|
||||
sym::sqrt => hypot::check(cx, expr, receiver),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !is_no_std_crate(cx) {
|
||||
expm1::check(cx, expr);
|
||||
mul_add::check(cx, expr);
|
||||
custom_abs::check(cx, expr);
|
||||
log_division::check(cx, expr);
|
||||
}
|
||||
radians::check(cx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, has_ambiguous_literal_in_expr, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::SUBOPTIMAL_FLOPS;
|
||||
|
||||
fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = expr.kind
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
|
||||
{
|
||||
return Some((lhs, rhs));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = &expr.kind
|
||||
{
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = parent.kind
|
||||
&& method.name == sym::sqrt
|
||||
// we don't care about the applicability as this is an early-return condition
|
||||
&& super::hypot::detect(cx, receiver, &mut Applicability::Unspecified).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let maybe_neg_sugg = |expr, app: &mut _| {
|
||||
let sugg = Sugg::hir_with_applicability(cx, expr, "_", app);
|
||||
if let BinOpKind::Sub = op { -sugg } else { sugg }
|
||||
};
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs)
|
||||
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
|
||||
{
|
||||
(
|
||||
inner_lhs,
|
||||
Sugg::hir_with_applicability(cx, inner_rhs, "_", &mut app),
|
||||
maybe_neg_sugg(rhs, &mut app),
|
||||
)
|
||||
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs)
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
{
|
||||
(
|
||||
inner_lhs,
|
||||
maybe_neg_sugg(inner_rhs, &mut app),
|
||||
Sugg::hir_with_applicability(cx, lhs, "_", &mut app),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Check if any variable in the expression has an ambiguous type (could be f32 or f64)
|
||||
// see: https://github.com/rust-lang/rust-clippy/issues/14897
|
||||
if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _)))
|
||||
&& has_ambiguous_literal_in_expr(cx, recv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({arg1}, {arg2})",
|
||||
super::lib::prepare_receiver_sugg(cx, recv, &mut app)
|
||||
),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
use clippy_utils::consts::Constant::{F32, F64};
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::numeric_literal;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use std::f32::consts as f32_consts;
|
||||
use std::f64::consts as f64_consts;
|
||||
|
||||
use super::{IMPRECISE_FLOPS, SUBOPTIMAL_FLOPS};
|
||||
|
||||
// Returns an integer if the float constant is a whole number and it can be
|
||||
// converted to an integer without loss of precision. For now we only check
|
||||
// ranges [-16777215, 16777216) for type f32 as whole number floats outside
|
||||
// this range are lossy and ambiguous.
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
|
||||
match value {
|
||||
F32(num) if num.fract() == 0.0 => {
|
||||
if (-16_777_215.0..16_777_216.0).contains(num) {
|
||||
Some(num.round() as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
F64(num) if num.fract() == 0.0 => {
|
||||
if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
|
||||
Some(num.round() as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
// Check receiver
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver)
|
||||
&& let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
Some("exp")
|
||||
} else if F32(2.0) == value || F64(2.0) == value {
|
||||
Some("exp2")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"exponent for bases 2 and e can be computed more accurately",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv = super::lib::prepare_receiver_sugg(cx, &args[0], &mut app);
|
||||
diag.span_suggestion(expr.span, "consider using", format!("{recv}.{method}()"), app);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Check argument
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv = Sugg::hir_with_applicability(cx, receiver, "_", &mut app).maybe_paren();
|
||||
let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
|
||||
(
|
||||
SUBOPTIMAL_FLOPS,
|
||||
"square-root of a number can be computed more efficiently and accurately",
|
||||
format!("{recv}.sqrt()"),
|
||||
)
|
||||
} else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
|
||||
(
|
||||
IMPRECISE_FLOPS,
|
||||
"cube-root of a number can be computed more accurately",
|
||||
format!("{recv}.cbrt()"),
|
||||
)
|
||||
} else if let Some(exponent) = get_integer_from_float_constant(&value) {
|
||||
(
|
||||
SUBOPTIMAL_FLOPS,
|
||||
"exponentiation with integer powers can be computed more efficiently",
|
||||
format!(
|
||||
"{recv}.powi({})",
|
||||
numeric_literal::format(&exponent.to_string(), None, false)
|
||||
),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_sugg(cx, lint, expr.span, help, "consider using", suggestion, app);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
use clippy_utils::consts::ConstEvalCtxt;
|
||||
use clippy_utils::consts::Constant::Int;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::SUBOPTIMAL_FLOPS;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0])
|
||||
&& value == Int(2)
|
||||
&& let Some(parent) = get_parent_expr(cx, expr)
|
||||
{
|
||||
if let Some(grandparent) = get_parent_expr(cx, parent)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = grandparent.kind
|
||||
&& method.name == sym::sqrt
|
||||
// we don't care about the applicability as this is an early-return condition
|
||||
&& super::hypot::detect(cx, receiver, &mut Applicability::Unspecified).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = parent.kind
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
parent.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
|diag| {
|
||||
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
|
||||
|
||||
// Negate expr if original code has subtraction and expr is on the right side
|
||||
let maybe_neg_sugg = |expr, hir_id, app: &mut _| {
|
||||
let sugg = Sugg::hir_with_applicability(cx, expr, "_", app);
|
||||
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
|
||||
-sugg
|
||||
} else {
|
||||
sugg
|
||||
}
|
||||
};
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
diag.span_suggestion(
|
||||
parent.span,
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
Sugg::hir_with_applicability(cx, receiver, "_", &mut app).maybe_paren(),
|
||||
maybe_neg_sugg(receiver, expr.hir_id, &mut app),
|
||||
maybe_neg_sugg(other_addend, other_addend.hir_id, &mut app),
|
||||
),
|
||||
app,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
use clippy_utils::consts::ConstEvalCtxt;
|
||||
use clippy_utils::consts::Constant::{F32, F64};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use std::f32::consts as f32_consts;
|
||||
use std::f64::consts as f64_consts;
|
||||
|
||||
use super::SUBOPTIMAL_FLOPS;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Div, ..
|
||||
},
|
||||
div_lhs,
|
||||
div_rhs,
|
||||
) = expr.kind
|
||||
&& let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
mul_lhs,
|
||||
mul_rhs,
|
||||
) = div_lhs.kind
|
||||
&& let ecx = ConstEvalCtxt::new(cx)
|
||||
&& let Some(rvalue) = ecx.eval(div_rhs)
|
||||
&& let Some(lvalue) = ecx.eval(mul_rhs)
|
||||
{
|
||||
// TODO: also check for constant values near PI/180 or 180/PI
|
||||
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue)
|
||||
&& (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"conversion to degrees can be done more accurately",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv = Sugg::hir_with_applicability(cx, mul_lhs, "num", &mut app);
|
||||
let proposal = if let ExprKind::Lit(literal) = mul_lhs.kind
|
||||
&& let ast::LitKind::Float(ref value, float_type) = literal.node
|
||||
&& float_type == ast::LitFloatType::Unsuffixed
|
||||
{
|
||||
if value.as_str().ends_with('.') {
|
||||
format!("{recv}0_f64.to_degrees()")
|
||||
} else {
|
||||
format!("{recv}_f64.to_degrees()")
|
||||
}
|
||||
} else {
|
||||
format!("{}.to_degrees()", recv.maybe_paren())
|
||||
};
|
||||
diag.span_suggestion(expr.span, "consider using", proposal, app);
|
||||
},
|
||||
);
|
||||
} else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue)
|
||||
&& (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"conversion to radians can be done more accurately",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv = Sugg::hir_with_applicability(cx, mul_lhs, "num", &mut app);
|
||||
let proposal = if let ExprKind::Lit(literal) = mul_lhs.kind
|
||||
&& let ast::LitKind::Float(ref value, float_type) = literal.node
|
||||
&& float_type == ast::LitFloatType::Unsuffixed
|
||||
{
|
||||
if value.as_str().ends_with('.') {
|
||||
format!("{recv}0_f64.to_radians()")
|
||||
} else {
|
||||
format!("{recv}_f64.to_radians()")
|
||||
}
|
||||
} else {
|
||||
format!("{}.to_radians()", recv.maybe_paren())
|
||||
};
|
||||
diag.span_suggestion(expr.span, "consider using", proposal, app);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind};
|
||||
use rustc_ast::token;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg;
|
||||
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp};
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
|
@ -35,132 +35,125 @@ declare_clippy_lint! {
|
|||
declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]);
|
||||
|
||||
// cases:
|
||||
// BinOpKind::Ge
|
||||
// LeOrGe::Ge
|
||||
// x >= y + 1
|
||||
// x - 1 >= y
|
||||
//
|
||||
// BinOpKind::Le
|
||||
// LeOrGe::Le
|
||||
// x + 1 <= y
|
||||
// x <= y - 1
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Side {
|
||||
Lhs,
|
||||
Rhs,
|
||||
enum LeOrGe {
|
||||
Le,
|
||||
Ge,
|
||||
}
|
||||
|
||||
impl TryFrom<BinOpKind> for LeOrGe {
|
||||
type Error = ();
|
||||
fn try_from(value: BinOpKind) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
BinOpKind::Le => Ok(Self::Le),
|
||||
BinOpKind::Ge => Ok(Self::Ge),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntPlusOne {
|
||||
#[expect(clippy::cast_sign_loss)]
|
||||
fn check_lit(token_lit: token::Lit, target_value: i128) -> bool {
|
||||
if let Ok(LitKind::Int(value, ..)) = LitKind::from_token_lit(token_lit) {
|
||||
return value == (target_value as u128);
|
||||
fn is_one(expr: &Expr) -> bool {
|
||||
if let ExprKind::Lit(token_lit) = expr.kind
|
||||
&& matches!(LitKind::from_token_lit(token_lit), Ok(LitKind::Int(Pu128(1), ..)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> {
|
||||
match (binop, &lhs.kind, &rhs.kind) {
|
||||
// case where `x - 1 >= ...` or `-1 + x >= ...`
|
||||
(BinOpKind::Ge, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) => {
|
||||
match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) {
|
||||
// `-1 + x`
|
||||
(BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => {
|
||||
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
|
||||
},
|
||||
// `x - 1`
|
||||
(BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
// case where `... >= y + 1` or `... >= 1 + y`
|
||||
(BinOpKind::Ge, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) if rhskind.node == BinOpKind::Add => {
|
||||
match (&rhslhs.kind, &rhsrhs.kind) {
|
||||
// `y + 1` and `1 + y`
|
||||
(ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
|
||||
},
|
||||
(_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
// case where `x + 1 <= ...` or `1 + x <= ...`
|
||||
(BinOpKind::Le, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) if lhskind.node == BinOpKind::Add => {
|
||||
match (&lhslhs.kind, &lhsrhs.kind) {
|
||||
// `1 + x` and `x + 1`
|
||||
(ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
|
||||
},
|
||||
(_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
// case where `... >= y - 1` or `... >= -1 + y`
|
||||
(BinOpKind::Le, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) => {
|
||||
match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) {
|
||||
// `-1 + y`
|
||||
(BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => {
|
||||
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
|
||||
},
|
||||
// `y - 1`
|
||||
(BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
fn is_neg_one(expr: &Expr) -> bool {
|
||||
if let ExprKind::Unary(UnOp::Neg, expr) = &expr.kind {
|
||||
Self::is_one(expr)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_recommendation(
|
||||
cx: &EarlyContext<'_>,
|
||||
binop: BinOpKind,
|
||||
node: &Expr,
|
||||
other_side: &Expr,
|
||||
side: Side,
|
||||
) -> Option<String> {
|
||||
let binop_string = match binop {
|
||||
BinOpKind::Ge => ">",
|
||||
BinOpKind::Le => "<",
|
||||
_ => return None,
|
||||
};
|
||||
if let Some(snippet) = node.span.get_source_text(cx)
|
||||
&& let Some(other_side_snippet) = other_side.span.get_source_text(cx)
|
||||
/// Checks whether `expr` is `x + 1` or `1 + x`, and if so, returns `x`
|
||||
fn as_x_plus_one(expr: &Expr) -> Option<&Expr> {
|
||||
if let ExprKind::Binary(op, lhs, rhs) = &expr.kind
|
||||
&& op.node == BinOpKind::Add
|
||||
{
|
||||
let rec = match side {
|
||||
Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
|
||||
Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
|
||||
};
|
||||
return rec;
|
||||
if Self::is_one(rhs) {
|
||||
// x + 1
|
||||
return Some(lhs);
|
||||
} else if Self::is_one(lhs) {
|
||||
// 1 + x
|
||||
return Some(rhs);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn emit_warning(cx: &EarlyContext<'_>, block: &Expr, recommendation: String) {
|
||||
span_lint_and_sugg(
|
||||
/// Checks whether `expr` is `x - 1` or `-1 + x`, and if so, returns `x`
|
||||
fn as_x_minus_one(expr: &Expr) -> Option<&Expr> {
|
||||
if let ExprKind::Binary(op, lhs, rhs) = &expr.kind {
|
||||
if op.node == BinOpKind::Sub && Self::is_one(rhs) {
|
||||
// x - 1
|
||||
return Some(lhs);
|
||||
} else if op.node == BinOpKind::Add && Self::is_neg_one(lhs) {
|
||||
// -1 + x
|
||||
return Some(rhs);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn check_binop<'tcx>(le_or_ge: LeOrGe, lhs: &'tcx Expr, rhs: &'tcx Expr) -> Option<(&'tcx Expr, &'tcx Expr)> {
|
||||
match le_or_ge {
|
||||
LeOrGe::Ge => {
|
||||
// case where `x - 1 >= ...` or `-1 + x >= ...`
|
||||
(Self::as_x_minus_one(lhs).map(|new_lhs| (new_lhs, rhs)))
|
||||
// case where `... >= y + 1` or `... >= 1 + y`
|
||||
.or_else(|| Self::as_x_plus_one(rhs).map(|new_rhs| (lhs, new_rhs)))
|
||||
},
|
||||
LeOrGe::Le => {
|
||||
// case where `x + 1 <= ...` or `1 + x <= ...`
|
||||
(Self::as_x_plus_one(lhs).map(|new_lhs| (new_lhs, rhs)))
|
||||
// case where `... <= y - 1` or `... <= -1 + y`
|
||||
.or_else(|| Self::as_x_minus_one(rhs).map(|new_rhs| (lhs, new_rhs)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_warning(cx: &EarlyContext<'_>, expr: &Expr, new_lhs: &Expr, le_or_ge: LeOrGe, new_rhs: &Expr) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
INT_PLUS_ONE,
|
||||
block.span,
|
||||
expr.span,
|
||||
"unnecessary `>= y + 1` or `x - 1 >=`",
|
||||
"change it to",
|
||||
recommendation,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let ctxt = expr.span.ctxt();
|
||||
let new_lhs = sugg::Sugg::ast(cx, new_lhs, "_", ctxt, &mut app);
|
||||
let new_rhs = sugg::Sugg::ast(cx, new_rhs, "_", ctxt, &mut app);
|
||||
let new_binop = match le_or_ge {
|
||||
LeOrGe::Ge => BinOpKind::Gt,
|
||||
LeOrGe::Le => BinOpKind::Lt,
|
||||
};
|
||||
let rec = sugg::make_binop(new_binop, &new_lhs, &new_rhs);
|
||||
diag.span_suggestion(expr.span, "change it to", rec, app);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for IntPlusOne {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
|
||||
if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind
|
||||
&& let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs)
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::Binary(binop, lhs, rhs) = &expr.kind
|
||||
&& let Ok(le_or_ge) = LeOrGe::try_from(binop.node)
|
||||
&& let Some((new_lhs, new_rhs)) = Self::check_binop(le_or_ge, lhs, rhs)
|
||||
{
|
||||
Self::emit_warning(cx, item, rec);
|
||||
Self::emit_warning(cx, expr, new_lhs, le_or_ge, new_rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#![feature(box_patterns)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(control_flow_into_value)]
|
||||
#![feature(exact_div)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(iter_partition_in_place)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
|
|
@ -110,6 +112,7 @@ mod doc;
|
|||
mod double_parens;
|
||||
mod drop_forget_ref;
|
||||
mod duplicate_mod;
|
||||
mod duration_suboptimal_units;
|
||||
mod else_if_without_else;
|
||||
mod empty_drop;
|
||||
mod empty_enums;
|
||||
|
|
@ -195,6 +198,7 @@ mod manual_abs_diff;
|
|||
mod manual_assert;
|
||||
mod manual_async_fn;
|
||||
mod manual_bits;
|
||||
mod manual_checked_ops;
|
||||
mod manual_clamp;
|
||||
mod manual_float_methods;
|
||||
mod manual_hash_one;
|
||||
|
|
@ -213,6 +217,7 @@ mod manual_rotate;
|
|||
mod manual_slice_size_calculation;
|
||||
mod manual_string_new;
|
||||
mod manual_strip;
|
||||
mod manual_take;
|
||||
mod map_unit_fn;
|
||||
mod match_result_ok;
|
||||
mod matches;
|
||||
|
|
@ -712,7 +717,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
|||
Box::new(|_| Box::<unused_async::UnusedAsync>::default()),
|
||||
Box::new(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))),
|
||||
Box::new(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))),
|
||||
Box::new(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)),
|
||||
Box::new(move |_| Box::new(strlen_on_c_strings::StrlenOnCStrings::new(conf))),
|
||||
Box::new(move |_| Box::new(self_named_constructors::SelfNamedConstructors)),
|
||||
Box::new(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)),
|
||||
Box::new(move |_| Box::new(manual_assert::ManualAssert)),
|
||||
|
|
@ -854,6 +859,9 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
|||
Box::new(|_| Box::<replace_box::ReplaceBox>::default()),
|
||||
Box::new(move |_| Box::new(manual_ilog2::ManualIlog2::new(conf))),
|
||||
Box::new(|_| Box::new(same_length_and_capacity::SameLengthAndCapacity)),
|
||||
Box::new(move |tcx| Box::new(duration_suboptimal_units::DurationSuboptimalUnits::new(tcx, conf))),
|
||||
Box::new(move |_| Box::new(manual_take::ManualTake::new(conf))),
|
||||
Box::new(|_| Box::new(manual_checked_ops::ManualCheckedOps)),
|
||||
// add late passes here, used by `cargo dev new_lint`
|
||||
];
|
||||
store.late_passes.extend(late_lints);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
use clippy_utils::{is_from_proc_macro, trait_ref_of_method};
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::visit::{try_visit, walk_list};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
|
|
@ -149,9 +149,12 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
|||
..
|
||||
} = item.kind
|
||||
{
|
||||
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv);
|
||||
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv, || {
|
||||
is_from_proc_macro(cx, item)
|
||||
});
|
||||
} else if let ItemKind::Impl(impl_) = &item.kind
|
||||
&& !item.span.from_expansion()
|
||||
&& !is_from_proc_macro(cx, item)
|
||||
{
|
||||
report_extra_impl_lifetimes(cx, impl_);
|
||||
}
|
||||
|
|
@ -169,6 +172,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
|||
item.span,
|
||||
report_extra_lifetimes,
|
||||
self.msrv,
|
||||
|| is_from_proc_macro(cx, item),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -179,7 +183,17 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
|||
TraitFn::Required(sig) => (None, Some(sig)),
|
||||
TraitFn::Provided(id) => (Some(id), None),
|
||||
};
|
||||
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, self.msrv);
|
||||
check_fn_inner(
|
||||
cx,
|
||||
sig,
|
||||
body,
|
||||
trait_sig,
|
||||
item.generics,
|
||||
item.span,
|
||||
true,
|
||||
self.msrv,
|
||||
|| is_from_proc_macro(cx, item),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -194,6 +208,7 @@ fn check_fn_inner<'tcx>(
|
|||
span: Span,
|
||||
report_extra_lifetimes: bool,
|
||||
msrv: Msrv,
|
||||
is_from_proc_macro: impl FnOnce() -> bool,
|
||||
) {
|
||||
if span.in_external_macro(cx.sess().source_map()) || has_where_lifetimes(cx, generics) {
|
||||
return;
|
||||
|
|
@ -245,10 +260,19 @@ fn check_fn_inner<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv) {
|
||||
if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) {
|
||||
return;
|
||||
}
|
||||
let elidable = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv);
|
||||
let has_elidable_lts = elidable
|
||||
.as_ref()
|
||||
.is_some_and(|(_, usages)| !usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)));
|
||||
|
||||
// Only check is_from_proc_macro if we're about to emit a lint (it's an expensive check)
|
||||
if (has_elidable_lts || report_extra_lifetimes) && is_from_proc_macro() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((elidable_lts, usages)) = elidable
|
||||
&& has_elidable_lts
|
||||
{
|
||||
// async functions have usages whose spans point at the lifetime declaration which messes up
|
||||
// suggestions
|
||||
let include_suggestions = !sig.header.is_async();
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_blo
|
|||
let_info,
|
||||
Some(if_let.if_then),
|
||||
);
|
||||
} else if els.and_then(|x| x.expr).is_some_and(is_simple_break_expr)
|
||||
} else if els.is_some_and(is_simple_break_block)
|
||||
&& let Some((pat, _)) = let_info
|
||||
{
|
||||
could_be_while_let(cx, expr, pat, init, has_trailing_exprs, let_info, None);
|
||||
|
|
@ -61,17 +61,23 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_blo
|
|||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
if let ExprKind::Block(b, _) = e.kind {
|
||||
match (b.stmts, b.expr) {
|
||||
([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 {
|
||||
matches!(e.kind, ExprKind::Break(dest, None) if dest.label.is_none())
|
||||
/// Checks if `block` contains a single unlabeled `break` expression or statement, possibly embedded
|
||||
/// inside other blocks.
|
||||
fn is_simple_break_block(block: &Block<'_>) -> bool {
|
||||
match (block.stmts, block.expr) {
|
||||
([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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `expr` contains a single unlabeled `break` expression or statement, possibly embedded
|
||||
/// inside other blocks.
|
||||
fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Block(b, _) => is_simple_break_block(b),
|
||||
ExprKind::Break(dest, None) => dest.label.is_none(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
170
src/tools/clippy/clippy_lints/src/manual_checked_ops.rs
Normal file
170
src/tools/clippy/clippy_lints/src/manual_checked_ops.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
|
||||
use clippy_utils::{SpanlessEq, is_integer_literal};
|
||||
use rustc_hir::{AssignOpKind, BinOpKind, Block, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects manual zero checks before dividing integers, such as `if x != 0 { y / x }`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `checked_div` already handles the zero case and makes the intent clearer while avoiding a
|
||||
/// panic from a manual division.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let (a, b) = (10u32, 5u32);
|
||||
/// if b != 0 {
|
||||
/// let result = a / b;
|
||||
/// println!("{result}");
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let (a, b) = (10u32, 5u32);
|
||||
/// if let Some(result) = a.checked_div(b) {
|
||||
/// println!("{result}");
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.95.0"]
|
||||
pub MANUAL_CHECKED_OPS,
|
||||
complexity,
|
||||
"manual zero checks before dividing integers"
|
||||
}
|
||||
declare_lint_pass!(ManualCheckedOps => [MANUAL_CHECKED_OPS]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum NonZeroBranch {
|
||||
Then,
|
||||
Else,
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for ManualCheckedOps {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::If(cond, then, r#else) = expr.kind
|
||||
&& !expr.span.from_expansion()
|
||||
&& let Some((divisor, branch)) = divisor_from_condition(cond)
|
||||
// This lint is intended for unsigned integers only.
|
||||
//
|
||||
// For signed integers, the most direct refactor to `checked_div` is often not
|
||||
// semantically equivalent to the original guard. For example, `rhs > 0` deliberately
|
||||
// excludes negative divisors, while `checked_div` would return `Some` for `rhs = -2`.
|
||||
// Also, `checked_div` can return `None` for `MIN / -1`, which requires additional
|
||||
// handling beyond the zero check.
|
||||
&& is_unsigned_integer(cx, divisor)
|
||||
&& let Some(block) = branch_block(then, r#else, branch)
|
||||
{
|
||||
let mut eq = SpanlessEq::new(cx).deny_side_effects().paths_by_resolution();
|
||||
if !eq.eq_expr(divisor, divisor) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut division_spans = Vec::new();
|
||||
let mut first_use = None;
|
||||
|
||||
let found_early_use = for_each_expr_without_closures(block, |e| {
|
||||
if let ExprKind::Binary(binop, lhs, rhs) = e.kind
|
||||
&& binop.node == BinOpKind::Div
|
||||
&& eq.eq_expr(rhs, divisor)
|
||||
&& is_unsigned_integer(cx, lhs)
|
||||
{
|
||||
match first_use {
|
||||
None => first_use = Some(UseKind::Division),
|
||||
Some(UseKind::Other) => return ControlFlow::Break(()),
|
||||
Some(UseKind::Division) => {},
|
||||
}
|
||||
|
||||
division_spans.push(e.span);
|
||||
|
||||
ControlFlow::<(), _>::Continue(Descend::No)
|
||||
} else if let ExprKind::AssignOp(op, lhs, rhs) = e.kind
|
||||
&& op.node == AssignOpKind::DivAssign
|
||||
&& eq.eq_expr(rhs, divisor)
|
||||
&& is_unsigned_integer(cx, lhs)
|
||||
{
|
||||
match first_use {
|
||||
None => first_use = Some(UseKind::Division),
|
||||
Some(UseKind::Other) => return ControlFlow::Break(()),
|
||||
Some(UseKind::Division) => {},
|
||||
}
|
||||
|
||||
division_spans.push(e.span);
|
||||
|
||||
ControlFlow::<(), _>::Continue(Descend::No)
|
||||
} else if eq.eq_expr(e, divisor) {
|
||||
if first_use.is_none() {
|
||||
first_use = Some(UseKind::Other);
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
ControlFlow::<(), _>::Continue(Descend::Yes)
|
||||
} else {
|
||||
ControlFlow::<(), _>::Continue(Descend::Yes)
|
||||
}
|
||||
});
|
||||
|
||||
if found_early_use.is_some() || first_use != Some(UseKind::Division) || division_spans.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(cx, MANUAL_CHECKED_OPS, cond.span, "manual checked division", |diag| {
|
||||
diag.span_label(cond.span, "check performed here");
|
||||
if let Some((first, rest)) = division_spans.split_first() {
|
||||
diag.span_label(*first, "division performed here");
|
||||
if !rest.is_empty() {
|
||||
diag.span_labels(rest.to_vec(), "... and here");
|
||||
}
|
||||
}
|
||||
diag.help("consider using `checked_div`");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
enum UseKind {
|
||||
Division,
|
||||
Other,
|
||||
}
|
||||
|
||||
fn divisor_from_condition<'tcx>(cond: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, NonZeroBranch)> {
|
||||
let ExprKind::Binary(binop, lhs, rhs) = cond.kind else {
|
||||
return None;
|
||||
};
|
||||
|
||||
match binop.node {
|
||||
BinOpKind::Ne | BinOpKind::Lt if is_zero(lhs) => Some((rhs, NonZeroBranch::Then)),
|
||||
BinOpKind::Ne | BinOpKind::Gt if is_zero(rhs) => Some((lhs, NonZeroBranch::Then)),
|
||||
BinOpKind::Eq if is_zero(lhs) => Some((rhs, NonZeroBranch::Else)),
|
||||
BinOpKind::Eq if is_zero(rhs) => Some((lhs, NonZeroBranch::Else)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn branch_block<'tcx>(
|
||||
then: &'tcx Expr<'tcx>,
|
||||
r#else: Option<&'tcx Expr<'tcx>>,
|
||||
branch: NonZeroBranch,
|
||||
) -> Option<&'tcx Block<'tcx>> {
|
||||
match branch {
|
||||
NonZeroBranch::Then => match then.kind {
|
||||
ExprKind::Block(block, _) => Some(block),
|
||||
_ => None,
|
||||
},
|
||||
NonZeroBranch::Else => match r#else?.kind {
|
||||
ExprKind::Block(block, _) => Some(block),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(expr: &Expr<'_>) -> bool {
|
||||
is_integer_literal(expr, 0)
|
||||
}
|
||||
|
||||
fn is_unsigned_integer(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
matches!(cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Uint(_))
|
||||
}
|
||||
114
src/tools/clippy/clippy_lints/src/manual_take.rs
Normal file
114
src/tools/clippy/clippy_lints/src/manual_take.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{MEM_TAKE, Msrv};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, ExprKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects manual re-implementations of `std::mem::take`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Because the function call is shorter and easier to read.
|
||||
///
|
||||
/// ### Known issues
|
||||
/// Currently the lint only detects cases involving `bool`s.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut x = true;
|
||||
/// let _ = if x {
|
||||
/// x = false;
|
||||
/// true
|
||||
/// } else {
|
||||
/// false
|
||||
/// };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let mut x = true;
|
||||
/// let _ = std::mem::take(&mut x);
|
||||
/// ```
|
||||
#[clippy::version = "1.94.0"]
|
||||
pub MANUAL_TAKE,
|
||||
complexity,
|
||||
"manual `mem::take` implementation"
|
||||
}
|
||||
pub struct ManualTake {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualTake {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self { msrv: conf.msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ManualTake => [MANUAL_TAKE]);
|
||||
|
||||
impl LateLintPass<'_> for ManualTake {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::If(cond, then, Some(otherwise)) = expr.kind
|
||||
&& let ExprKind::Path(_) = cond.kind
|
||||
&& let ExprKind::Block(
|
||||
Block {
|
||||
stmts: [stmt],
|
||||
expr: Some(then_expr),
|
||||
..
|
||||
},
|
||||
..,
|
||||
) = then.kind
|
||||
&& let ExprKind::Block(
|
||||
Block {
|
||||
stmts: [],
|
||||
expr: Some(else_expr),
|
||||
..
|
||||
},
|
||||
..,
|
||||
) = otherwise.kind
|
||||
&& let StmtKind::Semi(assignment) = stmt.kind
|
||||
&& let ExprKind::Assign(mut_c, possible_false, _) = assignment.kind
|
||||
&& let ExprKind::Path(_) = mut_c.kind
|
||||
&& !expr.span.in_external_macro(cx.sess().source_map())
|
||||
&& let Some(std_or_core) = clippy_utils::std_or_core(cx)
|
||||
&& self.msrv.meets(cx, MEM_TAKE)
|
||||
&& clippy_utils::SpanlessEq::new(cx).eq_expr(cond, mut_c)
|
||||
&& Some(false) == as_const_bool(possible_false)
|
||||
&& let Some(then_bool) = as_const_bool(then_expr)
|
||||
&& let Some(else_bool) = as_const_bool(else_expr)
|
||||
&& then_bool != else_bool
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_TAKE,
|
||||
expr.span,
|
||||
"manual implementation of `mem::take`",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let negate = if then_bool { "" } else { "!" };
|
||||
let taken = snippet_with_context(cx, cond.span, expr.span.ctxt(), "_", &mut app).0;
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span,
|
||||
"use",
|
||||
format!("{negate}{std_or_core}::mem::take(&mut {taken})"),
|
||||
app,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_const_bool(e: &Expr<'_>) -> Option<bool> {
|
||||
if let ExprKind::Lit(lit) = e.kind
|
||||
&& let LitKind::Bool(b) = lit.node
|
||||
{
|
||||
Some(b)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, sym};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
|
@ -14,7 +15,37 @@ use rustc_span::{Span, Symbol};
|
|||
|
||||
use super::MANUAL_IS_VARIANT_AND;
|
||||
|
||||
pub(super) fn check(
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Flavor {
|
||||
Option,
|
||||
Result,
|
||||
}
|
||||
|
||||
impl Flavor {
|
||||
fn new(cx: &LateContext<'_>, def_id: DefId) -> Option<Self> {
|
||||
match cx.tcx.get_diagnostic_name(def_id)? {
|
||||
sym::Option => Some(Self::Option),
|
||||
sym::Result => Some(Self::Result),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
const fn diag_sym(self) -> Symbol {
|
||||
match self {
|
||||
Self::Option => sym::Option,
|
||||
Self::Result => sym::Result,
|
||||
}
|
||||
}
|
||||
|
||||
const fn positive_variant_name(self) -> Symbol {
|
||||
match self {
|
||||
Self::Option => sym::Some,
|
||||
Self::Result => sym::Ok,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_map_unwrap_or_default(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
map_recv: &Expr<'_>,
|
||||
|
|
@ -30,11 +61,13 @@ pub(super) fn check(
|
|||
}
|
||||
|
||||
// 2. the caller of `map()` is neither `Option` nor `Result`
|
||||
let is_option = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option);
|
||||
let is_result = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Result);
|
||||
if !is_option && !is_result {
|
||||
let Some(flavor) = (cx.typeck_results())
|
||||
.expr_ty(map_recv)
|
||||
.opt_def_id()
|
||||
.and_then(|did| Flavor::new(cx, did))
|
||||
else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// 3. the caller of `unwrap_or_default` is neither `Option<bool>` nor `Result<bool, _>`
|
||||
if !cx.typeck_results().expr_ty(expr).is_bool() {
|
||||
|
|
@ -46,44 +79,23 @@ pub(super) fn check(
|
|||
return;
|
||||
}
|
||||
|
||||
let lint_msg = if is_option {
|
||||
"called `map(<f>).unwrap_or_default()` on an `Option` value"
|
||||
} else {
|
||||
"called `map(<f>).unwrap_or_default()` on a `Result` value"
|
||||
let lint_span = expr.span.with_lo(map_span.lo());
|
||||
let lint_msg = match flavor {
|
||||
Flavor::Option => "called `map(<f>).unwrap_or_default()` on an `Option` value",
|
||||
Flavor::Result => "called `map(<f>).unwrap_or_default()` on a `Result` value",
|
||||
};
|
||||
let suggestion = if is_option { "is_some_and" } else { "is_ok_and" };
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_IS_VARIANT_AND,
|
||||
expr.span.with_lo(map_span.lo()),
|
||||
lint_msg,
|
||||
"use",
|
||||
format!("{}({})", suggestion, snippet(cx, map_arg.span, "..")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
span_lint_and_then(cx, MANUAL_IS_VARIANT_AND, lint_span, lint_msg, |diag| {
|
||||
let method = match flavor {
|
||||
Flavor::Option => "is_some_and",
|
||||
Flavor::Result => "is_ok_and",
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Flavor {
|
||||
Option,
|
||||
Result,
|
||||
}
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let map_arg_snippet = snippet_with_applicability(cx, map_arg.span, "_", &mut app);
|
||||
|
||||
impl Flavor {
|
||||
const fn symbol(self) -> Symbol {
|
||||
match self {
|
||||
Self::Option => sym::Option,
|
||||
Self::Result => sym::Result,
|
||||
}
|
||||
}
|
||||
|
||||
const fn positive(self) -> Symbol {
|
||||
match self {
|
||||
Self::Option => sym::Some,
|
||||
Self::Result => sym::Ok,
|
||||
}
|
||||
}
|
||||
diag.span_suggestion(lint_span, "use", format!("{method}({map_arg_snippet})"), app);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
|
|
@ -178,7 +190,7 @@ fn emit_lint<'tcx>(
|
|||
cx,
|
||||
MANUAL_IS_VARIANT_AND,
|
||||
span,
|
||||
format!("called `.map() {op} {pos}()`", pos = flavor.positive(),),
|
||||
format!("called `.map() {op} {pos}()`", pos = flavor.positive_variant_name()),
|
||||
"use",
|
||||
format!(
|
||||
"{inversion}{recv}.{method}({body})",
|
||||
|
|
@ -195,24 +207,23 @@ pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
&& op.span.eq_ctxt(expr.span)
|
||||
&& let Ok(op) = Op::try_from(op.node)
|
||||
{
|
||||
// Check `left` and `right` expression in any order, and for `Option` and `Result`
|
||||
// Check `left` and `right` expression in any order
|
||||
for (expr1, expr2) in [(left, right), (right, left)] {
|
||||
for flavor in [Flavor::Option, Flavor::Result] {
|
||||
if let ExprKind::Call(call, [arg]) = expr1.kind
|
||||
&& let ExprKind::Lit(lit) = arg.kind
|
||||
&& let LitKind::Bool(bool_cst) = lit.node
|
||||
&& 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(flavor.symbol(), adt.did())
|
||||
&& args.type_at(0).is_bool()
|
||||
&& let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind
|
||||
&& cx.typeck_results().expr_ty(recv).is_diag_item(cx, flavor.symbol())
|
||||
&& let Ok(map_func) = MapFunc::try_from(map_expr)
|
||||
{
|
||||
return emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv);
|
||||
}
|
||||
if let ExprKind::Call(call, [arg]) = expr1.kind
|
||||
&& let ExprKind::Lit(lit) = arg.kind
|
||||
&& let LitKind::Bool(bool_cst) = lit.node
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = call.kind
|
||||
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = path.res
|
||||
&& let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind
|
||||
&& let ty = cx.typeck_results().expr_ty(expr1)
|
||||
&& let ty::Adt(adt, args) = ty.kind()
|
||||
&& let Some(flavor) = Flavor::new(cx, adt.did())
|
||||
&& args.type_at(0).is_bool()
|
||||
&& cx.typeck_results().expr_ty(recv).is_diag_item(cx, flavor.diag_sym())
|
||||
&& let Ok(map_func) = MapFunc::try_from(map_expr)
|
||||
{
|
||||
emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,71 +1,210 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use clippy_utils::res::{MaybeDef as _, MaybeResPath as _};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr, walk_path};
|
||||
use rustc_hir::{ExprKind, HirId, LangItem, Node, PatKind, Path, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_span::{Span, sym};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use super::MAP_UNWRAP_OR;
|
||||
|
||||
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
|
||||
///
|
||||
/// Returns true if the lint was emitted
|
||||
/// lint use of `map().unwrap_or()` for `Option`s and `Result`s
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
map_arg: &'tcx hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx hir::Expr<'_>,
|
||||
expr: &rustc_hir::Expr<'_>,
|
||||
recv: &rustc_hir::Expr<'_>,
|
||||
map_arg: &'tcx rustc_hir::Expr<'_>,
|
||||
unwrap_recv: &rustc_hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx rustc_hir::Expr<'_>,
|
||||
map_span: Span,
|
||||
msrv: Msrv,
|
||||
) -> bool {
|
||||
// lint if the caller of `map()` is an `Option` or a `Result`.
|
||||
let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option);
|
||||
let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result);
|
||||
) {
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
let recv_ty_kind = match recv_ty.opt_diag_name(cx) {
|
||||
Some(sym::Option) => sym::Option,
|
||||
Some(sym::Result) if msrv.meets(cx, msrvs::RESULT_MAP_OR) => sym::Result,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if is_result && !msrv.meets(cx, msrvs::RESULT_MAP_OR_ELSE) {
|
||||
return false;
|
||||
}
|
||||
let unwrap_arg_ty = cx.typeck_results().expr_ty(unwrap_arg);
|
||||
if !is_copy(cx, unwrap_arg_ty) {
|
||||
// Replacing `.map(<f>).unwrap_or(<a>)` with `.map_or(<a>, <f>)` can sometimes lead to
|
||||
// borrowck errors, see #10579 for one such instance.
|
||||
// In particular, if `a` causes a move and `f` references that moved binding, then we cannot lint:
|
||||
// ```
|
||||
// let x = vec![1, 2];
|
||||
// x.get(0..1).map(|s| s.to_vec()).unwrap_or(x);
|
||||
// ```
|
||||
// This compiles, but changing it to `map_or` will produce a compile error:
|
||||
// ```
|
||||
// let x = vec![1, 2];
|
||||
// x.get(0..1).map_or(x, |s| s.to_vec())
|
||||
// ^ moving `x` here
|
||||
// ^^^^^^^^^^^ while it is borrowed here (and later used in the closure)
|
||||
// ```
|
||||
// So, we have to check that `a` is not referenced anywhere (even outside of the `.map` closure!)
|
||||
// before the call to `unwrap_or`.
|
||||
|
||||
if is_option || is_result {
|
||||
// Don't make a suggestion that may fail to compile due to mutably borrowing
|
||||
// the same variable twice.
|
||||
let map_mutated_vars = mutated_variables(recv, cx);
|
||||
let unwrap_mutated_vars = mutated_variables(unwrap_arg, cx);
|
||||
if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
|
||||
if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// lint message
|
||||
let msg = if is_option {
|
||||
"called `map(<f>).unwrap_or_else(<g>)` on an `Option` value"
|
||||
} else {
|
||||
"called `map(<f>).unwrap_or_else(<g>)` on a `Result` value"
|
||||
let mut unwrap_visitor = UnwrapVisitor {
|
||||
cx,
|
||||
identifiers: FxHashSet::default(),
|
||||
};
|
||||
// get snippets for args to map() and unwrap_or_else()
|
||||
let map_snippet = snippet(cx, map_arg.span, "..");
|
||||
let unwrap_snippet = snippet(cx, unwrap_arg.span, "..");
|
||||
// lint, with note if both map() and unwrap_or_else() have the same span
|
||||
if map_arg.span.eq_ctxt(unwrap_arg.span) {
|
||||
let var_snippet = snippet(cx, recv.span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_UNWRAP_OR,
|
||||
expr.span,
|
||||
msg,
|
||||
"try",
|
||||
format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
unwrap_visitor.visit_expr(unwrap_arg);
|
||||
|
||||
let mut reference_visitor = ReferenceVisitor {
|
||||
cx,
|
||||
identifiers: unwrap_visitor.identifiers,
|
||||
unwrap_or_span: unwrap_arg.span,
|
||||
};
|
||||
|
||||
let body = cx.tcx.hir_body_owned_by(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
|
||||
|
||||
// Visit the body, and return if we've found a reference
|
||||
if reference_visitor.visit_body(body).is_break() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
if !unwrap_arg.span.eq_ctxt(map_span) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
// get snippet for unwrap_or()
|
||||
let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability);
|
||||
// lint message
|
||||
|
||||
let suggest_kind = if recv_ty_kind == sym::Option
|
||||
&& unwrap_arg
|
||||
.basic_res()
|
||||
.ctor_parent(cx)
|
||||
.is_lang_item(cx, LangItem::OptionNone)
|
||||
{
|
||||
SuggestedKind::AndThen
|
||||
}
|
||||
// is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
|
||||
else if matches!(&unwrap_arg.kind, ExprKind::Lit(lit)
|
||||
if matches!(lit.node, rustc_ast::LitKind::Bool(false)))
|
||||
&& msrv.meets(cx, msrvs::OPTION_RESULT_IS_VARIANT_AND)
|
||||
{
|
||||
SuggestedKind::IsVariantAnd
|
||||
} else {
|
||||
SuggestedKind::Other
|
||||
};
|
||||
|
||||
let arg = match suggest_kind {
|
||||
SuggestedKind::AndThen => "None",
|
||||
SuggestedKind::IsVariantAnd => "false",
|
||||
SuggestedKind::Other => "<a>",
|
||||
};
|
||||
|
||||
let suggest = match (suggest_kind, recv_ty_kind) {
|
||||
(SuggestedKind::AndThen, _) => "and_then(<f>)",
|
||||
(SuggestedKind::IsVariantAnd, sym::Result) => "is_ok_and(<f>)",
|
||||
(SuggestedKind::IsVariantAnd, sym::Option) => "is_some_and(<f>)",
|
||||
_ => "map_or(<a>, <f>)",
|
||||
};
|
||||
|
||||
let msg = format!(
|
||||
"called `map(<f>).unwrap_or({arg})` on {} `{recv_ty_kind}` value",
|
||||
if recv_ty_kind == sym::Option { "an" } else { "a" }
|
||||
);
|
||||
|
||||
span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| {
|
||||
let map_arg_span = map_arg.span;
|
||||
|
||||
let mut suggestion = vec![
|
||||
(
|
||||
map_span,
|
||||
String::from(match (suggest_kind, recv_ty_kind) {
|
||||
(SuggestedKind::AndThen, _) => "and_then",
|
||||
(SuggestedKind::IsVariantAnd, sym::Result) => "is_ok_and",
|
||||
(SuggestedKind::IsVariantAnd, sym::Option) => "is_some_and",
|
||||
(SuggestedKind::Other, _)
|
||||
if unwrap_arg_ty.peel_refs().is_array()
|
||||
&& cx.typeck_results().expr_ty_adjusted(unwrap_arg).peel_refs().is_slice() =>
|
||||
{
|
||||
return;
|
||||
},
|
||||
_ => "map_or",
|
||||
}),
|
||||
),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
|
||||
];
|
||||
|
||||
if matches!(suggest_kind, SuggestedKind::Other) {
|
||||
suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, ")));
|
||||
}
|
||||
|
||||
diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum SuggestedKind {
|
||||
AndThen,
|
||||
IsVariantAnd,
|
||||
Other,
|
||||
}
|
||||
|
||||
struct UnwrapVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
identifiers: FxHashSet<HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for UnwrapVisitor<'_, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
|
||||
fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) {
|
||||
if let Res::Local(local_id) = path.res
|
||||
&& let Node::Pat(pat) = self.cx.tcx.hir_node(local_id)
|
||||
&& let PatKind::Binding(_, local_id, ..) = pat.kind
|
||||
{
|
||||
self.identifiers.insert(local_id);
|
||||
}
|
||||
walk_path(self, path);
|
||||
}
|
||||
|
||||
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
||||
self.cx.tcx
|
||||
}
|
||||
}
|
||||
|
||||
struct ReferenceVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
identifiers: FxHashSet<HirId>,
|
||||
unwrap_or_span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
type Result = ControlFlow<()>;
|
||||
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) -> ControlFlow<()> {
|
||||
// If we haven't found a reference yet, check if this references
|
||||
// one of the locals that was moved in the `unwrap_or` argument.
|
||||
// We are only interested in exprs that appear before the `unwrap_or` call.
|
||||
if expr.span < self.unwrap_or_span
|
||||
&& let ExprKind::Path(ref path) = expr.kind
|
||||
&& let QPath::Resolved(_, path) = path
|
||||
&& let Res::Local(local_id) = path.res
|
||||
&& let Node::Pat(pat) = self.cx.tcx.hir_node(local_id)
|
||||
&& let PatKind::Binding(_, local_id, ..) = pat.kind
|
||||
&& self.identifiers.contains(&local_id)
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
walk_expr(self, expr)
|
||||
}
|
||||
|
||||
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
||||
self.cx.tcx
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::MaybeDef as _;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::sym;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::MAP_UNWRAP_OR;
|
||||
|
||||
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
|
||||
///
|
||||
/// Is part of the `map_unwrap_or` lint, split into separate files for readability.
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
map_arg: &'tcx hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx hir::Expr<'_>,
|
||||
msrv: Msrv,
|
||||
) -> bool {
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
let recv_ty_kind = match recv_ty.opt_diag_name(cx) {
|
||||
Some(sym::Option) => sym::Option,
|
||||
Some(sym::Result) if msrv.meets(cx, msrvs::RESULT_MAP_OR_ELSE) => sym::Result,
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
// Don't make a suggestion that may fail to compile due to mutably borrowing
|
||||
// the same variable twice.
|
||||
let Some(map_mutated_vars) = mutated_variables(recv, cx) else {
|
||||
return false;
|
||||
};
|
||||
let Some(unwrap_mutated_vars) = mutated_variables(unwrap_arg, cx) else {
|
||||
return false;
|
||||
};
|
||||
if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// lint message
|
||||
let msg = if recv_ty_kind == sym::Option {
|
||||
"called `map(<f>).unwrap_or_else(<g>)` on an `Option` value"
|
||||
} else {
|
||||
"called `map(<f>).unwrap_or_else(<g>)` on a `Result` value"
|
||||
};
|
||||
// get snippets for args to map() and unwrap_or_else()
|
||||
let map_snippet = snippet(cx, map_arg.span, "..");
|
||||
let unwrap_snippet = snippet(cx, unwrap_arg.span, "..");
|
||||
// lint, with note if both map() and unwrap_or_else() have the same span
|
||||
if map_arg.span.eq_ctxt(unwrap_arg.span) {
|
||||
let var_snippet = snippet(cx, recv.span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_UNWRAP_OR,
|
||||
expr.span,
|
||||
msg,
|
||||
"try",
|
||||
format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
|
@ -74,6 +74,7 @@ mod map_err_ignore;
|
|||
mod map_flatten;
|
||||
mod map_identity;
|
||||
mod map_unwrap_or;
|
||||
mod map_unwrap_or_else;
|
||||
mod map_with_unused_argument_over_ranges;
|
||||
mod mut_mutex_lock;
|
||||
mod needless_as_bytes;
|
||||
|
|
@ -89,7 +90,6 @@ mod open_options;
|
|||
mod option_as_ref_cloned;
|
||||
mod option_as_ref_deref;
|
||||
mod option_map_or_none;
|
||||
mod option_map_unwrap_or;
|
||||
mod or_fun_call;
|
||||
mod or_then_unwrap;
|
||||
mod path_buf_push_overwrite;
|
||||
|
|
@ -3933,7 +3933,8 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `option.map(f).unwrap_or_default()` and `result.map(f).unwrap_or_default()` where f is a function or closure that returns the `bool` type.
|
||||
/// Checks for usage of `option.map(f).unwrap_or_default()` and `result.map(f).unwrap_or_default()` where `f` is a function or closure that returns the `bool` type.
|
||||
///
|
||||
/// Also checks for equality comparisons like `option.map(f) == Some(true)` and `result.map(f) == Ok(true)`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
|
|
@ -5531,10 +5532,10 @@ impl Methods {
|
|||
stable_sort_primitive::check(cx, expr, recv);
|
||||
},
|
||||
(sym::sort_by, [arg]) => {
|
||||
unnecessary_sort_by::check(cx, expr, recv, arg, false);
|
||||
unnecessary_sort_by::check(cx, expr, call_span, arg, false);
|
||||
},
|
||||
(sym::sort_unstable_by, [arg]) => {
|
||||
unnecessary_sort_by::check(cx, expr, recv, arg, true);
|
||||
unnecessary_sort_by::check(cx, expr, call_span, arg, true);
|
||||
},
|
||||
(sym::split, [arg]) => {
|
||||
str_split::check(cx, expr, recv, arg);
|
||||
|
|
@ -5576,7 +5577,7 @@ impl Methods {
|
|||
unnecessary_fallible_conversions::check_method(cx, expr);
|
||||
},
|
||||
(sym::to_owned, []) => {
|
||||
if !suspicious_to_owned::check(cx, expr, recv) {
|
||||
if !suspicious_to_owned::check(cx, expr, span) {
|
||||
implicit_clone::check(cx, name, expr, recv);
|
||||
}
|
||||
},
|
||||
|
|
@ -5607,7 +5608,7 @@ impl Methods {
|
|||
manual_saturating_arithmetic::check_unwrap_or(cx, expr, lhs, rhs, u_arg, arith);
|
||||
},
|
||||
Some((sym::map, m_recv, [m_arg], span, _)) => {
|
||||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv);
|
||||
map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv);
|
||||
},
|
||||
Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(
|
||||
|
|
@ -5629,7 +5630,7 @@ impl Methods {
|
|||
manual_saturating_arithmetic::check_sub_unwrap_or_default(cx, expr, lhs, rhs);
|
||||
},
|
||||
Some((sym::map, m_recv, [arg], span, _)) => {
|
||||
manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv);
|
||||
manual_is_variant_and::check_map_unwrap_or_default(cx, expr, m_recv, arg, span, self.msrv);
|
||||
},
|
||||
Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(
|
||||
|
|
@ -5648,7 +5649,7 @@ impl Methods {
|
|||
(sym::unwrap_or_else, [u_arg]) => {
|
||||
match method_call(recv) {
|
||||
Some((sym::map, recv, [map_arg], _, _))
|
||||
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
|
||||
if map_unwrap_or_else::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
|
||||
Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use super::NEEDLESS_COLLECT;
|
||||
|
|
@ -16,8 +17,8 @@ use rustc_hir::{
|
|||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{self, AssocTag, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
|
||||
|
||||
|
|
@ -104,16 +105,19 @@ pub(super) fn check<'tcx>(
|
|||
Node::LetStmt(l) => {
|
||||
if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind
|
||||
&& let ty = cx.typeck_results().expr_ty(collect_expr)
|
||||
&& matches!(
|
||||
ty.opt_diag_name(cx),
|
||||
Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList)
|
||||
)
|
||||
&& let Some(extra_spec) = ty.opt_diag_name(cx).and_then(ExtraFunctionSpec::new)
|
||||
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
|
||||
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
|
||||
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
|
||||
&& let Some((iter_calls, extra_calls)) =
|
||||
detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty), extra_spec)
|
||||
&& let [iter_call] = &*iter_calls
|
||||
{
|
||||
let mut used_count_visitor = UsedCountVisitor { cx, id, count: 0 };
|
||||
let mut used_count_visitor = UsedCountVisitor {
|
||||
cx,
|
||||
id,
|
||||
extra_spec,
|
||||
count: 0,
|
||||
};
|
||||
walk_block(&mut used_count_visitor, block);
|
||||
if used_count_visitor.count > 1 {
|
||||
return;
|
||||
|
|
@ -135,11 +139,24 @@ pub(super) fn check<'tcx>(
|
|||
span,
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
|diag| {
|
||||
let iter_replacement =
|
||||
format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
|
||||
let iter_snippet = Sugg::hir(cx, iter_expr, "..");
|
||||
let mut iter_replacement = iter_snippet.to_string();
|
||||
for extra in &extra_calls {
|
||||
iter_replacement = extra.apply_iter_method(cx, &iter_replacement);
|
||||
}
|
||||
iter_replacement.push_str(&iter_call.get_iter_method(cx));
|
||||
|
||||
let mut remove_suggestions = vec![(l.span, String::new())];
|
||||
remove_suggestions.extend(
|
||||
extra_calls
|
||||
.iter()
|
||||
.flat_map(|extra| extra.span().map(|s| (s, String::new()))),
|
||||
);
|
||||
remove_suggestions.push((iter_call.span, iter_replacement));
|
||||
|
||||
diag.multipart_suggestion(
|
||||
iter_call.get_suggestion_text(),
|
||||
vec![(l.span, String::new()), (iter_call.span, iter_replacement)],
|
||||
remove_suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
|
|
@ -272,6 +289,7 @@ struct IterFunction {
|
|||
func: IterFunctionKind,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl IterFunction {
|
||||
fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
|
||||
match &self.func {
|
||||
|
|
@ -288,6 +306,7 @@ impl IterFunction {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn get_suggestion_text(&self) -> &'static str {
|
||||
match &self.func {
|
||||
IterFunctionKind::IntoIter(_) => {
|
||||
|
|
@ -305,6 +324,7 @@ impl IterFunction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum IterFunctionKind {
|
||||
IntoIter(HirId),
|
||||
Len,
|
||||
|
|
@ -312,16 +332,119 @@ enum IterFunctionKind {
|
|||
Contains(Span),
|
||||
}
|
||||
|
||||
struct ExtraFunctionSpan {
|
||||
/// Span of the function call
|
||||
func_span: Span,
|
||||
/// Span of the argument
|
||||
arg_span: Span,
|
||||
}
|
||||
|
||||
enum ExtraFunction {
|
||||
Push {
|
||||
back: Vec<ExtraFunctionSpan>,
|
||||
front: Vec<ExtraFunctionSpan>,
|
||||
},
|
||||
Extend(ExtraFunctionSpan),
|
||||
}
|
||||
|
||||
impl ExtraFunction {
|
||||
fn apply_iter_method(&self, cx: &LateContext<'_>, inner: &str) -> String {
|
||||
match &self {
|
||||
ExtraFunction::Push { back, front } => {
|
||||
let back_sugg = back
|
||||
.iter()
|
||||
.map(|span| snippet(cx, span.arg_span, ".."))
|
||||
.intersperse(Cow::Borrowed(", "))
|
||||
.collect::<String>();
|
||||
let front = front
|
||||
.iter()
|
||||
.map(|span| snippet(cx, span.arg_span, ".."))
|
||||
.intersperse(Cow::Borrowed(", "))
|
||||
.collect::<String>();
|
||||
match (front.is_empty(), back_sugg.is_empty()) {
|
||||
(true, true) => inner.to_string(),
|
||||
(true, false) => format!("{inner}.chain([{back_sugg}])"),
|
||||
(false, true) => format!("[{front}].into_iter().chain({inner})"),
|
||||
(false, false) => format!("[{front}].into_iter().chain({inner}).chain([{back_sugg}])"),
|
||||
}
|
||||
},
|
||||
ExtraFunction::Extend(span) => {
|
||||
let s = snippet(cx, span.arg_span, "..");
|
||||
format!("{inner}.chain({s})")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn span(&self) -> Box<dyn Iterator<Item = Span> + '_> {
|
||||
match &self {
|
||||
ExtraFunction::Push { back, front } => Box::new(
|
||||
back.iter()
|
||||
.map(|s| s.func_span)
|
||||
.chain(front.iter().map(|s| s.func_span)),
|
||||
),
|
||||
ExtraFunction::Extend(span) => Box::new(std::iter::once(span.func_span)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct ExtraFunctionPushSpec {
|
||||
back: Option<Symbol>,
|
||||
front: Option<Symbol>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct ExtraFunctionSpec {
|
||||
push_symbol: ExtraFunctionPushSpec,
|
||||
extend_symbol: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl ExtraFunctionSpec {
|
||||
fn new(target: Symbol) -> Option<Self> {
|
||||
match target {
|
||||
sym::Vec => Some(ExtraFunctionSpec {
|
||||
push_symbol: ExtraFunctionPushSpec {
|
||||
back: Some(sym::push),
|
||||
front: None,
|
||||
},
|
||||
extend_symbol: Some(sym::extend),
|
||||
}),
|
||||
sym::VecDeque | sym::LinkedList => Some(ExtraFunctionSpec {
|
||||
push_symbol: ExtraFunctionPushSpec {
|
||||
back: Some(sym::push_back),
|
||||
front: Some(sym::push_front),
|
||||
},
|
||||
extend_symbol: Some(sym::extend),
|
||||
}),
|
||||
sym::BinaryHeap => Some(ExtraFunctionSpec {
|
||||
push_symbol: ExtraFunctionPushSpec {
|
||||
back: None,
|
||||
front: None,
|
||||
},
|
||||
extend_symbol: None,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_extra_function(self, name: Symbol) -> bool {
|
||||
self.push_symbol.back == Some(name) || self.push_symbol.front == Some(name) || self.extend_symbol == Some(name)
|
||||
}
|
||||
}
|
||||
|
||||
struct IterFunctionVisitor<'a, 'tcx> {
|
||||
illegal_mutable_capture_ids: HirIdSet,
|
||||
current_mutably_captured_ids: HirIdSet,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
uses: Vec<Option<IterFunction>>,
|
||||
extras: Vec<ExtraFunction>,
|
||||
extra_spec: ExtraFunctionSpec,
|
||||
hir_id_uses_map: FxHashMap<HirId, usize>,
|
||||
current_statement_hir_id: Option<HirId>,
|
||||
seen_other: bool,
|
||||
target: HirId,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
||||
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
|
||||
for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
|
||||
|
|
@ -341,6 +464,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
// Check function calls on our collection
|
||||
if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind {
|
||||
|
|
@ -384,6 +508,53 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
|||
func: IterFunctionKind::Contains(args[0].span),
|
||||
span: expr.span,
|
||||
})),
|
||||
name if let is_push_back = self.extra_spec.push_symbol.back.is_some_and(|sym| name == sym)
|
||||
&& (is_push_back || self.extra_spec.push_symbol.front.is_some_and(|sym| name == sym))
|
||||
&& self.uses.is_empty() =>
|
||||
{
|
||||
let span = get_span_of_expr_or_parent_stmt(self.cx, expr);
|
||||
match self.extras.last_mut() {
|
||||
Some(ExtraFunction::Push { back, .. }) if is_push_back => {
|
||||
back.push(ExtraFunctionSpan {
|
||||
func_span: span,
|
||||
arg_span: args[0].span,
|
||||
});
|
||||
},
|
||||
Some(ExtraFunction::Push { front, .. }) => {
|
||||
front.push(ExtraFunctionSpan {
|
||||
func_span: span,
|
||||
arg_span: args[0].span,
|
||||
});
|
||||
},
|
||||
_ if is_push_back => {
|
||||
self.extras.push(ExtraFunction::Push {
|
||||
back: vec![ExtraFunctionSpan {
|
||||
func_span: span,
|
||||
arg_span: args[0].span,
|
||||
}],
|
||||
front: Vec::new(),
|
||||
});
|
||||
},
|
||||
_ => {
|
||||
self.extras.push(ExtraFunction::Push {
|
||||
back: Vec::new(),
|
||||
front: vec![ExtraFunctionSpan {
|
||||
func_span: span,
|
||||
arg_span: args[0].span,
|
||||
}],
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
name if self.extra_spec.extend_symbol.is_some_and(|sym| name == sym)
|
||||
&& self.uses.is_empty() =>
|
||||
{
|
||||
let span = get_span_of_expr_or_parent_stmt(self.cx, expr);
|
||||
self.extras.push(ExtraFunction::Extend(ExtraFunctionSpan {
|
||||
func_span: span,
|
||||
arg_span: args[0].span,
|
||||
}));
|
||||
},
|
||||
_ => {
|
||||
self.seen_other = true;
|
||||
if let Some(hir_id) = self.current_statement_hir_id {
|
||||
|
|
@ -421,6 +592,16 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// If parent of the `expr` is a statement, return the span of the statement, otherwise return the
|
||||
/// span of the expression.
|
||||
fn get_span_of_expr_or_parent_stmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Span {
|
||||
if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) {
|
||||
stmt.span
|
||||
} else {
|
||||
expr.span
|
||||
}
|
||||
}
|
||||
|
||||
enum LoopKind<'tcx> {
|
||||
Conditional(&'tcx Expr<'tcx>),
|
||||
Loop,
|
||||
|
|
@ -468,6 +649,7 @@ fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>
|
|||
struct UsedCountVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
id: HirId,
|
||||
extra_spec: ExtraFunctionSpec,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
|
|
@ -475,11 +657,20 @@ impl<'tcx> Visitor<'tcx> for UsedCountVisitor<'_, 'tcx> {
|
|||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if expr.res_local_id() == Some(self.id) {
|
||||
self.count += 1;
|
||||
} else {
|
||||
if expr.res_local_id() != Some(self.id) {
|
||||
walk_expr(self, expr);
|
||||
return;
|
||||
}
|
||||
|
||||
let parent = self.cx.tcx.parent_hir_node(expr.hir_id);
|
||||
if let Node::Expr(expr) = parent
|
||||
&& let ExprKind::MethodCall(method_name, _, _, _) = &expr.kind
|
||||
&& self.extra_spec.is_extra_function(method_name.ident.name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.count += 1;
|
||||
}
|
||||
|
||||
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
||||
|
|
@ -494,12 +685,15 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
|
|||
id: HirId,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
captured_ids: HirIdSet,
|
||||
) -> Option<Vec<IterFunction>> {
|
||||
extra_spec: ExtraFunctionSpec,
|
||||
) -> Option<(Vec<IterFunction>, Vec<ExtraFunction>)> {
|
||||
let mut visitor = IterFunctionVisitor {
|
||||
illegal_mutable_capture_ids: captured_ids,
|
||||
current_mutably_captured_ids: HirIdSet::default(),
|
||||
cx,
|
||||
uses: Vec::new(),
|
||||
extras: Vec::new(),
|
||||
extra_spec,
|
||||
hir_id_uses_map: FxHashMap::default(),
|
||||
current_statement_hir_id: None,
|
||||
seen_other: false,
|
||||
|
|
@ -509,7 +703,7 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
|
|||
if visitor.seen_other {
|
||||
None
|
||||
} else {
|
||||
Some(visitor.uses.into_iter().flatten().collect())
|
||||
Some((visitor.uses.into_iter().flatten().collect(), visitor.extras))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,180 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{Visitor, walk_path};
|
||||
use rustc_hir::{ExprKind, HirId, Node, PatKind, Path, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_span::{Span, sym};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use super::MAP_UNWRAP_OR;
|
||||
|
||||
/// lint use of `map().unwrap_or()` for `Option`s
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &rustc_hir::Expr<'_>,
|
||||
recv: &rustc_hir::Expr<'_>,
|
||||
map_arg: &'tcx rustc_hir::Expr<'_>,
|
||||
unwrap_recv: &rustc_hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx rustc_hir::Expr<'_>,
|
||||
map_span: Span,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) {
|
||||
if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) {
|
||||
// Replacing `.map(<f>).unwrap_or(<a>)` with `.map_or(<a>, <f>)` can sometimes lead to
|
||||
// borrowck errors, see #10579 for one such instance.
|
||||
// In particular, if `a` causes a move and `f` references that moved binding, then we cannot lint:
|
||||
// ```
|
||||
// let x = vec![1, 2];
|
||||
// x.get(0..1).map(|s| s.to_vec()).unwrap_or(x);
|
||||
// ```
|
||||
// This compiles, but changing it to `map_or` will produce a compile error:
|
||||
// ```
|
||||
// let x = vec![1, 2];
|
||||
// x.get(0..1).map_or(x, |s| s.to_vec())
|
||||
// ^ moving `x` here
|
||||
// ^^^^^^^^^^^ while it is borrowed here (and later used in the closure)
|
||||
// ```
|
||||
// So, we have to check that `a` is not referenced anywhere (even outside of the `.map` closure!)
|
||||
// before the call to `unwrap_or`.
|
||||
|
||||
let mut unwrap_visitor = UnwrapVisitor {
|
||||
cx,
|
||||
identifiers: FxHashSet::default(),
|
||||
};
|
||||
unwrap_visitor.visit_expr(unwrap_arg);
|
||||
|
||||
let mut reference_visitor = ReferenceVisitor {
|
||||
cx,
|
||||
identifiers: unwrap_visitor.identifiers,
|
||||
unwrap_or_span: unwrap_arg.span,
|
||||
};
|
||||
|
||||
let body = cx.tcx.hir_body_owned_by(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
|
||||
|
||||
// Visit the body, and return if we've found a reference
|
||||
if reference_visitor.visit_body(body).is_break() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if !unwrap_arg.span.eq_ctxt(map_span) {
|
||||
return;
|
||||
}
|
||||
|
||||
// is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
|
||||
let suggest_is_some_and = matches!(&unwrap_arg.kind, ExprKind::Lit(lit)
|
||||
if matches!(lit.node, rustc_ast::LitKind::Bool(false)))
|
||||
&& msrv.meets(cx, msrvs::OPTION_RESULT_IS_VARIANT_AND);
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
// get snippet for unwrap_or()
|
||||
let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability);
|
||||
// lint message
|
||||
// comparing the snippet from source to raw text ("None") below is safe
|
||||
// because we already have checked the type.
|
||||
let arg = if unwrap_snippet == "None" {
|
||||
"None"
|
||||
} else if suggest_is_some_and {
|
||||
"false"
|
||||
} else {
|
||||
"<a>"
|
||||
};
|
||||
let unwrap_snippet_none = unwrap_snippet == "None";
|
||||
let suggest = if unwrap_snippet_none {
|
||||
"and_then(<f>)"
|
||||
} else if suggest_is_some_and {
|
||||
"is_some_and(<f>)"
|
||||
} else {
|
||||
"map_or(<a>, <f>)"
|
||||
};
|
||||
let msg = format!("called `map(<f>).unwrap_or({arg})` on an `Option` value");
|
||||
|
||||
span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| {
|
||||
let map_arg_span = map_arg.span;
|
||||
|
||||
let mut suggestion = vec![
|
||||
(
|
||||
map_span,
|
||||
String::from(if unwrap_snippet_none {
|
||||
"and_then"
|
||||
} else if suggest_is_some_and {
|
||||
"is_some_and"
|
||||
} else {
|
||||
"map_or"
|
||||
}),
|
||||
),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
|
||||
];
|
||||
|
||||
if !unwrap_snippet_none && !suggest_is_some_and {
|
||||
suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, ")));
|
||||
}
|
||||
|
||||
diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct UnwrapVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
identifiers: FxHashSet<HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for UnwrapVisitor<'_, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
|
||||
fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) {
|
||||
if let Res::Local(local_id) = path.res
|
||||
&& let Node::Pat(pat) = self.cx.tcx.hir_node(local_id)
|
||||
&& let PatKind::Binding(_, local_id, ..) = pat.kind
|
||||
{
|
||||
self.identifiers.insert(local_id);
|
||||
}
|
||||
walk_path(self, path);
|
||||
}
|
||||
|
||||
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
||||
self.cx.tcx
|
||||
}
|
||||
}
|
||||
|
||||
struct ReferenceVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
identifiers: FxHashSet<HirId>,
|
||||
unwrap_or_span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
type Result = ControlFlow<()>;
|
||||
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) -> ControlFlow<()> {
|
||||
// If we haven't found a reference yet, check if this references
|
||||
// one of the locals that was moved in the `unwrap_or` argument.
|
||||
// We are only interested in exprs that appear before the `unwrap_or` call.
|
||||
if expr.span < self.unwrap_or_span
|
||||
&& let ExprKind::Path(ref path) = expr.kind
|
||||
&& let QPath::Resolved(_, path) = path
|
||||
&& let Res::Local(local_id) = path.res
|
||||
&& let Node::Pat(pat) = self.cx.tcx.hir_node(local_id)
|
||||
&& let PatKind::Binding(_, local_id, ..) = pat.kind
|
||||
&& self.identifiers.contains(&local_id)
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
rustc_hir::intravisit::walk_expr(self, expr)
|
||||
}
|
||||
|
||||
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
||||
self.cx.tcx
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,14 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use super::SUSPICIOUS_TO_OWNED;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool {
|
||||
pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span) -> bool {
|
||||
if cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
|
|
@ -18,28 +17,22 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -
|
|||
&& let input_type = cx.typeck_results().expr_ty(expr)
|
||||
&& input_type.is_diag_item(cx, sym::Cow)
|
||||
{
|
||||
let mut app = Applicability::MaybeIncorrect;
|
||||
let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
|
||||
let app = Applicability::MaybeIncorrect;
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUSPICIOUS_TO_OWNED,
|
||||
expr.span,
|
||||
with_forced_trimmed_paths!(format!(
|
||||
"this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"
|
||||
"this `to_owned` call clones the `{input_type}` itself and does not cause its contents to become owned"
|
||||
)),
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"depending on intent, either make the Cow an Owned variant",
|
||||
format!("{recv_snip}.into_owned()"),
|
||||
app,
|
||||
);
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"or clone the Cow itself",
|
||||
format!("{recv_snip}.clone()"),
|
||||
method_span,
|
||||
"depending on intent, either make the `Cow` an `Owned` variant",
|
||||
"into_owned".to_string(),
|
||||
app,
|
||||
);
|
||||
diag.span_suggestion(method_span, "or clone the `Cow` itself", "clone".to_string(), app);
|
||||
},
|
||||
);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use clippy_utils::sugg::{Sugg, make_binop};
|
|||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{get_parent_expr, is_from_proc_macro};
|
||||
use rustc_ast::LitKind::Bool;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -48,13 +48,14 @@ pub(super) fn check<'a>(
|
|||
let ExprKind::Lit(def_kind) = def.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
|
||||
|
||||
let Bool(def_bool) = def_kind.node else {
|
||||
let LitKind::Bool(def_bool) = def_kind.node else {
|
||||
return;
|
||||
};
|
||||
|
||||
let typeck = cx.typeck_results();
|
||||
|
||||
let recv_ty = typeck.expr_ty_adjusted(recv);
|
||||
|
||||
let variant = match recv_ty.opt_diag_name(cx) {
|
||||
Some(sym::Option) => Variant::Some,
|
||||
Some(sym::Result) => Variant::Ok,
|
||||
|
|
@ -63,12 +64,12 @@ pub(super) fn check<'a>(
|
|||
|
||||
let ext_def_span = def.span.until(map.span);
|
||||
|
||||
let (sugg, method, applicability) = if cx.typeck_results().expr_adjustments(recv).is_empty()
|
||||
let (sugg, method, applicability): (_, Cow<'_, _>, _) = if typeck.expr_adjustments(recv).is_empty()
|
||||
&& let ExprKind::Closure(map_closure) = map.kind
|
||||
&& let closure_body = cx.tcx.hir_body(map_closure.body)
|
||||
&& let closure_body_value = closure_body.value.peel_blocks()
|
||||
&& let ExprKind::Binary(op, l, r) = closure_body_value.kind
|
||||
&& let Some(param) = closure_body.params.first()
|
||||
&& let [param] = closure_body.params
|
||||
&& let PatKind::Binding(_, hir_id, _, _) = param.pat.kind
|
||||
// checking that map_or is one of the following:
|
||||
// .map_or(false, |x| x == y)
|
||||
|
|
@ -78,14 +79,13 @@ pub(super) fn check<'a>(
|
|||
&& ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool))
|
||||
&& let non_binding_location = if l.res_local_id() == Some(hir_id) { r } else { l }
|
||||
&& switch_to_eager_eval(cx, non_binding_location)
|
||||
// if its both then that's a strange edge case and
|
||||
// if it's both then that's a strange edge case and
|
||||
// we can just ignore it, since by default clippy will error on this
|
||||
&& (l.res_local_id() == Some(hir_id)) != (r.res_local_id() == Some(hir_id))
|
||||
&& !is_local_used(cx, non_binding_location, hir_id)
|
||||
&& let typeck_results = cx.typeck_results()
|
||||
&& let l_ty = typeck_results.expr_ty(l)
|
||||
&& l_ty == typeck_results.expr_ty(r)
|
||||
&& let Some(partial_eq) = cx.tcx.get_diagnostic_item(sym::PartialEq)
|
||||
&& let l_ty = typeck.expr_ty(l)
|
||||
&& l_ty == typeck.expr_ty(r)
|
||||
&& let Some(partial_eq) = cx.tcx.lang_items().eq_trait()
|
||||
&& implements_trait(cx, recv_ty, partial_eq, &[recv_ty.into()])
|
||||
&& is_copy(cx, l_ty)
|
||||
{
|
||||
|
|
@ -97,12 +97,12 @@ pub(super) fn check<'a>(
|
|||
// being converted to `Some(5) == Some(5).then(|| 1)` isn't
|
||||
// the same thing
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let inner_non_binding = Sugg::NonParen(Cow::Owned(format!(
|
||||
"{wrap}({})",
|
||||
Sugg::hir(cx, non_binding_location, "")
|
||||
Sugg::hir_with_applicability(cx, non_binding_location, "", &mut app)
|
||||
)));
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let binop = make_binop(
|
||||
op.node,
|
||||
&Sugg::hir_with_applicability(cx, recv, "..", &mut app),
|
||||
|
|
@ -126,18 +126,18 @@ pub(super) fn check<'a>(
|
|||
}
|
||||
.into_string();
|
||||
|
||||
(vec![(expr.span, sugg)], "a standard comparison", app)
|
||||
(vec![(expr.span, sugg)], "a standard comparison".into(), app)
|
||||
} else if !def_bool && msrv.meets(cx, msrvs::OPTION_RESULT_IS_VARIANT_AND) {
|
||||
let suggested_name = variant.method_name();
|
||||
(
|
||||
vec![(method_span, suggested_name.into()), (ext_def_span, String::default())],
|
||||
suggested_name,
|
||||
vec![(method_span, suggested_name.into()), (ext_def_span, String::new())],
|
||||
format!("`{suggested_name}`").into(),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
} else if def_bool && matches!(variant, Variant::Some) && msrv.meets(cx, msrvs::IS_NONE_OR) {
|
||||
(
|
||||
vec![(method_span, "is_none_or".into()), (ext_def_span, String::default())],
|
||||
"is_none_or",
|
||||
vec![(method_span, "is_none_or".into()), (ext_def_span, String::new())],
|
||||
"`is_none_or`".into(),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,47 +1,51 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::std_or_core;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::ty::{implements_trait, is_copy, peel_n_ty_refs};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
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_span::sym;
|
||||
use rustc_middle::ty::{self, GenericArgKind, Ty};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{Span, sym};
|
||||
use std::iter;
|
||||
use std::ops::Not;
|
||||
|
||||
use super::UNNECESSARY_SORT_BY;
|
||||
|
||||
enum LintTrigger {
|
||||
Sort(SortDetection),
|
||||
Sort,
|
||||
SortByKey(SortByKeyDetection),
|
||||
}
|
||||
|
||||
struct SortDetection {
|
||||
vec_name: String,
|
||||
}
|
||||
|
||||
struct SortByKeyDetection {
|
||||
vec_name: String,
|
||||
closure_arg: String,
|
||||
closure_arg: Span,
|
||||
closure_body: String,
|
||||
reverse: bool,
|
||||
applicability: Applicability,
|
||||
}
|
||||
|
||||
/// Detect if the two expressions are mirrored (identical, except one
|
||||
/// contains a and the other replaces it with b)
|
||||
fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
|
||||
match (&a_expr.kind, &b_expr.kind) {
|
||||
fn mirrored_exprs(
|
||||
a_expr: &Expr<'_>,
|
||||
b_expr: &Expr<'_>,
|
||||
binding_map: &BindingMap,
|
||||
binding_source: BindingSource,
|
||||
) -> bool {
|
||||
match (a_expr.kind, b_expr.kind) {
|
||||
// Two arrays with mirrored contents
|
||||
(ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
|
||||
iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||
},
|
||||
(ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => iter::zip(left_exprs, right_exprs)
|
||||
.all(|(left, right)| mirrored_exprs(left, right, binding_map, binding_source)),
|
||||
// The two exprs are function calls.
|
||||
// Check to see that the function itself and its arguments are mirrored
|
||||
(ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
|
||||
mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
|
||||
&& iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||
mirrored_exprs(left_expr, right_expr, binding_map, binding_source)
|
||||
&& iter::zip(left_args, right_args)
|
||||
.all(|(left, right)| mirrored_exprs(left, right, binding_map, binding_source))
|
||||
},
|
||||
// The two exprs are method calls.
|
||||
// Check to see that the function is the same and the arguments and receivers are mirrored
|
||||
|
|
@ -50,116 +54,219 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident
|
|||
ExprKind::MethodCall(right_segment, right_receiver, right_args, _),
|
||||
) => {
|
||||
left_segment.ident == right_segment.ident
|
||||
&& iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||
&& mirrored_exprs(left_receiver, a_ident, right_receiver, b_ident)
|
||||
&& iter::zip(left_args, right_args)
|
||||
.all(|(left, right)| mirrored_exprs(left, right, binding_map, binding_source))
|
||||
&& mirrored_exprs(left_receiver, right_receiver, binding_map, binding_source)
|
||||
},
|
||||
// Two tuples with mirrored contents
|
||||
(ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
|
||||
iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||
},
|
||||
(ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => iter::zip(left_exprs, right_exprs)
|
||||
.all(|(left, right)| mirrored_exprs(left, right, binding_map, binding_source)),
|
||||
// Two binary ops, which are the same operation and which have mirrored arguments
|
||||
(ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
|
||||
left_op.node == right_op.node
|
||||
&& mirrored_exprs(left_left, a_ident, right_left, b_ident)
|
||||
&& mirrored_exprs(left_right, a_ident, right_right, b_ident)
|
||||
&& mirrored_exprs(left_left, right_left, binding_map, binding_source)
|
||||
&& mirrored_exprs(left_right, right_right, binding_map, binding_source)
|
||||
},
|
||||
// Two unary ops, which are the same operation and which have the same argument
|
||||
(ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
|
||||
left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
|
||||
left_op == right_op && mirrored_exprs(left_expr, right_expr, binding_map, binding_source)
|
||||
},
|
||||
// The two exprs are literals of some kind
|
||||
(ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
|
||||
(ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
|
||||
(ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, right, binding_map, binding_source),
|
||||
(ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
|
||||
mirrored_exprs(left_block, a_ident, right_block, b_ident)
|
||||
mirrored_exprs(left_block, right_block, binding_map, binding_source)
|
||||
},
|
||||
(ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
|
||||
left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
|
||||
left_ident.name == right_ident.name && mirrored_exprs(left_expr, right_expr, binding_map, binding_source)
|
||||
},
|
||||
// Two paths: either one is a and the other is b, or they're identical to each other
|
||||
(
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
Path {
|
||||
&Path {
|
||||
segments: left_segments,
|
||||
..
|
||||
},
|
||||
)),
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
Path {
|
||||
&Path {
|
||||
segments: right_segments,
|
||||
..
|
||||
},
|
||||
)),
|
||||
) => {
|
||||
(iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
|
||||
&& left_segments
|
||||
.iter()
|
||||
.all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
|
||||
(iter::zip(left_segments, right_segments).all(|(left, right)| left.ident == right.ident)
|
||||
&& left_segments.iter().all(|seg| {
|
||||
!binding_map.contains_key(&BindingKey {
|
||||
ident: seg.ident,
|
||||
source: BindingSource::Left,
|
||||
}) && !binding_map.contains_key(&BindingKey {
|
||||
ident: seg.ident,
|
||||
source: BindingSource::Right,
|
||||
})
|
||||
}))
|
||||
|| (left_segments.len() == 1
|
||||
&& &left_segments[0].ident == a_ident
|
||||
&& right_segments.len() == 1
|
||||
&& &right_segments[0].ident == b_ident)
|
||||
&& binding_map
|
||||
.get(&BindingKey {
|
||||
ident: left_segments[0].ident,
|
||||
source: binding_source,
|
||||
})
|
||||
.is_some_and(|value| value.mirrored.ident == right_segments[0].ident))
|
||||
},
|
||||
// Matching expressions, but one or both is borrowed
|
||||
(
|
||||
ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
|
||||
ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
|
||||
) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
|
||||
(_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
|
||||
(ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
|
||||
) => left_kind == right_kind && mirrored_exprs(left_expr, right_expr, binding_map, binding_source),
|
||||
(_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => {
|
||||
mirrored_exprs(a_expr, right_expr, binding_map, binding_source)
|
||||
},
|
||||
(ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => {
|
||||
mirrored_exprs(left_expr, b_expr, binding_map, binding_source)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
enum BindingSource {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Not for BindingSource {
|
||||
type Output = BindingSource;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
BindingSource::Left => BindingSource::Right,
|
||||
BindingSource::Right => BindingSource::Left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct BindingKey {
|
||||
/// The identifier of the binding.
|
||||
ident: Ident,
|
||||
/// The source of the binding.
|
||||
source: BindingSource,
|
||||
}
|
||||
|
||||
struct BindingValue {
|
||||
/// The mirrored binding.
|
||||
mirrored: BindingKey,
|
||||
/// The number of refs the binding is wrapped in.
|
||||
n_refs: usize,
|
||||
}
|
||||
|
||||
/// A map from binding info to the number of refs the binding is wrapped in.
|
||||
type BindingMap = FxHashMap<BindingKey, BindingValue>;
|
||||
/// Extract the binding pairs, if the two patterns are mirrored. The pats are assumed to be used in
|
||||
/// closure inputs and thus irrefutable.
|
||||
fn mapping_of_mirrored_pats(a_pat: &Pat<'_>, b_pat: &Pat<'_>) -> Option<BindingMap> {
|
||||
fn mapping_of_mirrored_pats_inner(
|
||||
a_pat: &Pat<'_>,
|
||||
b_pat: &Pat<'_>,
|
||||
mapping: &mut BindingMap,
|
||||
n_refs: usize,
|
||||
) -> bool {
|
||||
match (&a_pat.kind, &b_pat.kind) {
|
||||
(PatKind::Tuple(a_pats, a_dots), PatKind::Tuple(b_pats, b_dots)) => {
|
||||
a_dots == b_dots
|
||||
&& a_pats.len() == b_pats.len()
|
||||
&& iter::zip(a_pats.iter(), b_pats.iter())
|
||||
.all(|(a, b)| mapping_of_mirrored_pats_inner(a, b, mapping, n_refs))
|
||||
},
|
||||
(PatKind::Binding(_, _, a_ident, _), PatKind::Binding(_, _, b_ident, _)) => {
|
||||
let a_key = BindingKey {
|
||||
ident: *a_ident,
|
||||
source: BindingSource::Left,
|
||||
};
|
||||
let b_key = BindingKey {
|
||||
ident: *b_ident,
|
||||
source: BindingSource::Right,
|
||||
};
|
||||
let a_value = BindingValue {
|
||||
mirrored: b_key,
|
||||
n_refs,
|
||||
};
|
||||
let b_value = BindingValue {
|
||||
mirrored: a_key,
|
||||
n_refs,
|
||||
};
|
||||
mapping.insert(a_key, a_value);
|
||||
mapping.insert(b_key, b_value);
|
||||
true
|
||||
},
|
||||
(PatKind::Wild, PatKind::Wild) => true,
|
||||
(PatKind::TupleStruct(_, a_pats, a_dots), PatKind::TupleStruct(_, b_pats, b_dots)) => {
|
||||
a_dots == b_dots
|
||||
&& a_pats.len() == b_pats.len()
|
||||
&& iter::zip(a_pats.iter(), b_pats.iter())
|
||||
.all(|(a, b)| mapping_of_mirrored_pats_inner(a, b, mapping, n_refs))
|
||||
},
|
||||
(PatKind::Struct(_, a_fields, a_rest), PatKind::Struct(_, b_fields, b_rest)) => {
|
||||
a_rest == b_rest
|
||||
&& a_fields.len() == b_fields.len()
|
||||
&& iter::zip(a_fields.iter(), b_fields.iter()).all(|(a_field, b_field)| {
|
||||
a_field.ident == b_field.ident
|
||||
&& mapping_of_mirrored_pats_inner(a_field.pat, b_field.pat, mapping, n_refs)
|
||||
})
|
||||
},
|
||||
(PatKind::Ref(a_inner, _, _), PatKind::Ref(b_inner, _, _)) => {
|
||||
mapping_of_mirrored_pats_inner(a_inner, b_inner, mapping, n_refs + 1)
|
||||
},
|
||||
(PatKind::Slice(a_elems, None, a_rest), PatKind::Slice(b_elems, None, b_rest)) => {
|
||||
a_elems.len() == b_elems.len()
|
||||
&& iter::zip(a_elems.iter(), b_elems.iter())
|
||||
.all(|(a, b)| mapping_of_mirrored_pats_inner(a, b, mapping, n_refs))
|
||||
&& a_rest.len() == b_rest.len()
|
||||
&& iter::zip(a_rest.iter(), b_rest.iter())
|
||||
.all(|(a, b)| mapping_of_mirrored_pats_inner(a, b, mapping, n_refs))
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
let mut mapping = FxHashMap::default();
|
||||
if mapping_of_mirrored_pats_inner(a_pat, b_pat, &mut mapping, 0) {
|
||||
return Some(mapping);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_assoc(method_id)
|
||||
&& cx.tcx.type_of(impl_id).instantiate_identity().is_slice()
|
||||
&& let ExprKind::Closure(&Closure { body, .. }) = arg.kind
|
||||
&& let closure_body = cx.tcx.hir_body(body)
|
||||
&& let &[
|
||||
Param {
|
||||
pat:
|
||||
Pat {
|
||||
kind: PatKind::Binding(_, _, left_ident, _),
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
Param {
|
||||
pat:
|
||||
Pat {
|
||||
kind: PatKind::Binding(_, _, right_ident, _),
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
] = &closure_body.params
|
||||
&& let &[Param { pat: l_pat, .. }, Param { pat: r_pat, .. }] = closure_body.params
|
||||
&& let Some(binding_map) = mapping_of_mirrored_pats(l_pat, r_pat)
|
||||
&& let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind
|
||||
&& method_path.ident.name == sym::cmp
|
||||
&& cx
|
||||
.ty_based_def(closure_body.value)
|
||||
.opt_parent(cx)
|
||||
.is_diag_item(cx, sym::Ord)
|
||||
&& let Some(ord_trait) = cx.tcx.get_diagnostic_item(sym::Ord)
|
||||
&& cx.ty_based_def(closure_body.value).opt_parent(cx).opt_def_id() == Some(ord_trait)
|
||||
{
|
||||
let (closure_body, closure_arg, reverse) = if mirrored_exprs(left_expr, left_ident, right_expr, right_ident) {
|
||||
(
|
||||
Sugg::hir(cx, left_expr, "..").to_string(),
|
||||
left_ident.name.to_string(),
|
||||
false,
|
||||
)
|
||||
} else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
|
||||
(
|
||||
Sugg::hir(cx, left_expr, "..").to_string(),
|
||||
right_ident.name.to_string(),
|
||||
true,
|
||||
)
|
||||
let (closure_body, closure_arg, reverse) =
|
||||
if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Left) {
|
||||
(left_expr, l_pat.span, false)
|
||||
} else if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Right) {
|
||||
(left_expr, r_pat.span, true)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut applicability = if reverse {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
return None;
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
let vec_name = Sugg::hir(cx, recv, "..").to_string();
|
||||
|
||||
if let ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
|
|
@ -167,22 +274,53 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
|
|||
segments: [PathSegment { ident: left_name, .. }],
|
||||
..
|
||||
},
|
||||
)) = &left_expr.kind
|
||||
&& left_name == left_ident
|
||||
&& cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Ord)
|
||||
.is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]))
|
||||
)) = left_expr.kind
|
||||
{
|
||||
return Some(LintTrigger::Sort(SortDetection { vec_name }));
|
||||
if let PatKind::Binding(_, _, left_ident, _) = l_pat.kind
|
||||
&& *left_name == left_ident
|
||||
&& implements_trait(cx, cx.typeck_results().expr_ty(left_expr), ord_trait, &[])
|
||||
{
|
||||
return Some(LintTrigger::Sort);
|
||||
}
|
||||
|
||||
let mut left_expr_ty = cx.typeck_results().expr_ty(left_expr);
|
||||
let left_ident_n_refs = binding_map
|
||||
.get(&BindingKey {
|
||||
ident: *left_name,
|
||||
source: BindingSource::Left,
|
||||
})
|
||||
.map_or(0, |value| value.n_refs);
|
||||
// Peel off the outer-most ref which is introduced by the closure, if it is not already peeled
|
||||
// by the pattern
|
||||
if left_ident_n_refs == 0 {
|
||||
(left_expr_ty, _) = peel_n_ty_refs(left_expr_ty, 1);
|
||||
}
|
||||
if !reverse && is_copy(cx, left_expr_ty) {
|
||||
let mut closure_body =
|
||||
snippet_with_applicability(cx, closure_body.span, "_", &mut applicability).to_string();
|
||||
if left_ident_n_refs == 0 {
|
||||
closure_body = format!("*{closure_body}");
|
||||
}
|
||||
return Some(LintTrigger::SortByKey(SortByKeyDetection {
|
||||
closure_arg,
|
||||
closure_body,
|
||||
reverse,
|
||||
applicability,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if !expr_borrows(cx, left_expr) {
|
||||
let left_expr_ty = cx.typeck_results().expr_ty(left_expr);
|
||||
if !expr_borrows(left_expr_ty)
|
||||
// Don't lint if the closure is accessing non-Copy fields
|
||||
&& (!expr_is_field_access(left_expr) || is_copy(cx, left_expr_ty))
|
||||
{
|
||||
let closure_body = Sugg::hir_with_applicability(cx, closure_body, "_", &mut applicability).to_string();
|
||||
return Some(LintTrigger::SortByKey(SortByKeyDetection {
|
||||
vec_name,
|
||||
closure_arg,
|
||||
closure_body,
|
||||
reverse,
|
||||
applicability,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -190,61 +328,75 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
|
|||
None
|
||||
}
|
||||
|
||||
fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
fn expr_borrows(ty: Ty<'_>) -> bool {
|
||||
matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(_)))
|
||||
}
|
||||
|
||||
fn expr_is_field_access(expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Field(_, _) => true,
|
||||
ExprKind::AddrOf(_, Mutability::Not, inner) => expr_is_field_access(inner),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
call_span: Span,
|
||||
arg: &'tcx Expr<'_>,
|
||||
is_unstable: bool,
|
||||
) {
|
||||
match detect_lint(cx, expr, recv, arg) {
|
||||
match detect_lint(cx, expr, arg) {
|
||||
Some(LintTrigger::SortByKey(trigger)) => {
|
||||
let method = if is_unstable {
|
||||
"sort_unstable_by_key"
|
||||
} else {
|
||||
"sort_by_key"
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
let Some(std_or_core) = std_or_core(cx) else {
|
||||
// To make it this far the crate has to reference diagnostic items defined in core. Either this is
|
||||
// the `core` crate, there's an `extern crate core` somewhere, or another crate is defining the
|
||||
// diagnostic items. It's fine to not lint in all those cases even if we might be able to.
|
||||
return;
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
format!("consider using `{method}`"),
|
||||
"try",
|
||||
format!(
|
||||
"{}.{}(|{}| {})",
|
||||
trigger.vec_name,
|
||||
method,
|
||||
trigger.closure_arg,
|
||||
if let Some(std_or_core) = std_or_core(cx)
|
||||
&& trigger.reverse
|
||||
{
|
||||
format!("{}::cmp::Reverse({})", std_or_core, trigger.closure_body)
|
||||
|diag| {
|
||||
let mut app = trigger.applicability;
|
||||
let closure_body = if trigger.reverse {
|
||||
format!("{std_or_core}::cmp::Reverse({})", trigger.closure_body)
|
||||
} else {
|
||||
trigger.closure_body
|
||||
},
|
||||
),
|
||||
if trigger.reverse {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
let closure_arg = snippet_with_applicability(cx, trigger.closure_arg, "_", &mut app);
|
||||
diag.span_suggestion_verbose(
|
||||
call_span,
|
||||
"try",
|
||||
format!("{method}(|{closure_arg}| {closure_body})"),
|
||||
app,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
Some(LintTrigger::Sort(trigger)) => {
|
||||
Some(LintTrigger::Sort) => {
|
||||
let method = if is_unstable { "sort_unstable" } else { "sort" };
|
||||
span_lint_and_sugg(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
format!("consider using `{method}`"),
|
||||
"try",
|
||||
format!("{}.{}()", trigger.vec_name, method),
|
||||
Applicability::MachineApplicable,
|
||||
|diag| {
|
||||
diag.span_suggestion_verbose(
|
||||
call_span,
|
||||
"try",
|
||||
format!("{method}()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
None => {},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_lint_allowed;
|
||||
use clippy_utils::macros::span_is_local;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_hir::def_id::DefIdSet;
|
||||
use rustc_hir::{Impl, Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -84,7 +85,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods {
|
|||
cx.tcx.def_span(item.owner_id),
|
||||
format!("missing trait method provided by default: `{}`", assoc.name()),
|
||||
|diag| {
|
||||
diag.span_help(cx.tcx.def_span(assoc.def_id), "implement the method");
|
||||
if assoc.def_id.is_local() {
|
||||
diag.span_help(cx.tcx.def_span(assoc.def_id), "implement the method");
|
||||
} else if let Some(snippet) = snippet_opt(cx, of_trait.trait_ref.path.span) {
|
||||
diag.help(format!(
|
||||
"implement the missing `{}` method of the `{snippet}` trait",
|
||||
assoc.name(),
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::{indent_of, snippet_block, snippet_with_context};
|
||||
use rustc_ast::Label;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, ExprKind, LoopSource, StmtKind};
|
||||
use rustc_hir::{Block, Expr, ExprKind, HirId, LoopSource, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{ExpnKind, Span};
|
||||
|
|
@ -336,14 +336,9 @@ fn emit_warning(cx: &LateContext<'_>, data: &LintData<'_>, header: &str, typ: Li
|
|||
data.if_expr,
|
||||
),
|
||||
};
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
NEEDLESS_CONTINUE,
|
||||
expr.span,
|
||||
message,
|
||||
None,
|
||||
format!("{header}\n{snip}"),
|
||||
);
|
||||
span_lint_hir_and_then(cx, NEEDLESS_CONTINUE, expr.hir_id, expr.span, message, |diag| {
|
||||
diag.help(format!("{header}\n{snip}"));
|
||||
});
|
||||
}
|
||||
|
||||
fn suggestion_snippet_for_continue_inside_if(cx: &LateContext<'_>, data: &LintData<'_>) -> String {
|
||||
|
|
@ -424,11 +419,11 @@ fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &Lint
|
|||
|
||||
fn check_last_stmt_in_expr<F>(cx: &LateContext<'_>, inner_expr: &Expr<'_>, func: &F)
|
||||
where
|
||||
F: Fn(Option<&Label>, Span),
|
||||
F: Fn(HirId, Option<&Label>, Span),
|
||||
{
|
||||
match inner_expr.kind {
|
||||
ExprKind::Continue(continue_label) => {
|
||||
func(continue_label.label.as_ref(), inner_expr.span);
|
||||
func(inner_expr.hir_id, continue_label.label.as_ref(), inner_expr.span);
|
||||
},
|
||||
ExprKind::If(_, then_block, else_block) if let ExprKind::Block(then_block, _) = then_block.kind => {
|
||||
check_last_stmt_in_block(cx, then_block, func);
|
||||
|
|
@ -454,7 +449,7 @@ where
|
|||
|
||||
fn check_last_stmt_in_block<F>(cx: &LateContext<'_>, b: &Block<'_>, func: &F)
|
||||
where
|
||||
F: Fn(Option<&Label>, Span),
|
||||
F: Fn(HirId, Option<&Label>, Span),
|
||||
{
|
||||
if let Some(expr) = b.expr {
|
||||
check_last_stmt_in_expr(cx, expr, func);
|
||||
|
|
@ -470,15 +465,17 @@ where
|
|||
|
||||
fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
with_loop_block(expr, |loop_block, label| {
|
||||
let p = |continue_label: Option<&Label>, span: Span| {
|
||||
let p = |continue_hir_id, continue_label: Option<&Label>, span: Span| {
|
||||
if compare_labels(label, continue_label) {
|
||||
span_lint_and_help(
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
NEEDLESS_CONTINUE,
|
||||
continue_hir_id,
|
||||
span,
|
||||
MSG_REDUNDANT_CONTINUE_EXPRESSION,
|
||||
None,
|
||||
DROP_CONTINUE_EXPRESSION_MSG,
|
||||
|diag| {
|
||||
diag.help(DROP_CONTINUE_EXPRESSION_MSG);
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,7 +33,10 @@ impl ArithmeticSideEffects {
|
|||
allowed_binary.extend([
|
||||
("f32", FxHashSet::from_iter(["f32"])),
|
||||
("f64", FxHashSet::from_iter(["f64"])),
|
||||
("std::string::String", FxHashSet::from_iter(["str"])),
|
||||
(
|
||||
"std::string::String",
|
||||
FxHashSet::from_iter(["str", "std::string::String"]),
|
||||
),
|
||||
]);
|
||||
for (lhs, rhs) in &conf.arithmetic_side_effects_allowed_binary {
|
||||
allowed_binary.entry(lhs).or_default().insert(rhs);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,18 @@ pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &E
|
|||
| (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
|
||||
"=="
|
||||
},
|
||||
// x != y && x >= y => x > y
|
||||
(BinOpKind::And, BinOpKind::Ne, BinOpKind::Ge)
|
||||
// x >= y && x != y => x > y
|
||||
| (BinOpKind::And, BinOpKind::Ge, BinOpKind::Ne) => {
|
||||
">"
|
||||
},
|
||||
// x != y && x <= y => x < y
|
||||
(BinOpKind::And, BinOpKind::Ne, BinOpKind::Le)
|
||||
// x <= y && x != y => x < y
|
||||
| (BinOpKind::And, BinOpKind::Le, BinOpKind::Ne) => {
|
||||
"<"
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Note
|
||||
/// Clippy can only lint compiled code. For this lint to trigger, you must configure `cargo clippy`
|
||||
/// to include test compilation, for instance, by using flags such as `--tests` or `--all-targets`.
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub REDUNDANT_TEST_PREFIX,
|
||||
restriction,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::res::MaybeResPath;
|
||||
use clippy_utils::source::{indent_of, snippet};
|
||||
use clippy_utils::{expr_or_init, get_builtin_attr, peel_hir_expr_unary, sym};
|
||||
use rustc_ast::BindingMode;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
|
@ -89,13 +90,14 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> {
|
|||
|diag| {
|
||||
match apa.counter {
|
||||
0 | 1 => {},
|
||||
2 => {
|
||||
2 if let Some(last_method_span) = apa.last_method_span => {
|
||||
let indent = " ".repeat(indent_of(cx, apa.last_stmt_span).unwrap_or(0));
|
||||
let init_method = snippet(cx, apa.first_method_span, "..");
|
||||
let usage_method = snippet(cx, apa.last_method_span, "..");
|
||||
let stmt = if let Some(last_bind_ident) = apa.last_bind_ident {
|
||||
let usage_method = snippet(cx, last_method_span, "..");
|
||||
let stmt = if let Some((binding_mode, last_bind_ident)) = apa.last_bind_ident {
|
||||
format!(
|
||||
"\n{indent}let {} = {init_method}.{usage_method};",
|
||||
"\n{indent}let {}{} = {init_method}.{usage_method};",
|
||||
binding_mode.prefix_str(),
|
||||
snippet(cx, last_bind_ident.span, ".."),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -310,13 +312,13 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> {
|
|||
};
|
||||
match self.ap.curr_stmt.kind {
|
||||
hir::StmtKind::Let(local) => {
|
||||
if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
|
||||
apa.last_bind_ident = Some(ident);
|
||||
if let hir::PatKind::Binding(binding_mode, _, ident, _) = local.pat.kind {
|
||||
apa.last_bind_ident = Some((binding_mode, ident));
|
||||
}
|
||||
if let Some(local_init) = local.init
|
||||
&& let hir::ExprKind::MethodCall(_, _, _, span) = local_init.kind
|
||||
{
|
||||
apa.last_method_span = span;
|
||||
apa.last_method_span = Some(span);
|
||||
}
|
||||
},
|
||||
hir::StmtKind::Semi(semi_expr) => {
|
||||
|
|
@ -326,7 +328,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> {
|
|||
return;
|
||||
}
|
||||
if let hir::ExprKind::MethodCall(_, _, _, span) = semi_expr.kind {
|
||||
apa.last_method_span = span;
|
||||
apa.last_method_span = Some(span);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
|
@ -385,9 +387,9 @@ struct AuxParamsAttr {
|
|||
|
||||
/// The last visited binding or variable span within a block that had any referenced inner type
|
||||
/// marked with `#[has_significant_drop]`.
|
||||
last_bind_ident: Option<Ident>,
|
||||
last_bind_ident: Option<(BindingMode, Ident)>,
|
||||
/// Similar to `last_bind_span` but encompasses the right-hand method call.
|
||||
last_method_span: Span,
|
||||
last_method_span: Option<Span>,
|
||||
/// Similar to `last_bind_span` but encompasses the whole contained statement.
|
||||
last_stmt_span: Span,
|
||||
}
|
||||
|
|
@ -403,7 +405,7 @@ impl Default for AuxParamsAttr {
|
|||
first_method_span: DUMMY_SP,
|
||||
first_stmt_span: DUMMY_SP,
|
||||
last_bind_ident: None,
|
||||
last_method_span: DUMMY_SP,
|
||||
last_method_span: None,
|
||||
last_stmt_span: DUMMY_SP,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,9 @@ impl SlowVectorInit {
|
|||
&& func.ty_rel_def(cx).is_diag_item(cx, sym::vec_with_capacity)
|
||||
{
|
||||
Some(InitializedSize::Initialized(len_expr))
|
||||
} else if matches!(expr.kind, ExprKind::Call(func, []) if func.ty_rel_def(cx).is_diag_item(cx, sym::vec_new)) {
|
||||
} else if let ExprKind::Call(func, []) = expr.kind
|
||||
&& func.ty_rel_def(cx).is_diag_item(cx, sym::vec_new)
|
||||
{
|
||||
Some(InitializedSize::Uninitialized)
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::visitors::is_expr_unsafe;
|
||||
|
|
@ -6,12 +8,12 @@ use clippy_utils::{match_libc_symbol, sym};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, UnsafeSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `libc::strlen` on a `CString` or `CStr` value,
|
||||
/// and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead.
|
||||
/// and suggest calling `count_bytes()` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// libc::strlen is an unsafe function, which we don't need to call
|
||||
|
|
@ -27,15 +29,25 @@ declare_clippy_lint! {
|
|||
/// ```rust, no_run
|
||||
/// use std::ffi::CString;
|
||||
/// let cstring = CString::new("foo").expect("CString::new failed");
|
||||
/// let len = cstring.as_bytes().len();
|
||||
/// let len = cstring.count_bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.55.0"]
|
||||
pub STRLEN_ON_C_STRINGS,
|
||||
complexity,
|
||||
"using `libc::strlen` on a `CString` or `CStr` value, while `as_bytes().len()` or `to_bytes().len()` respectively can be used instead"
|
||||
"using `libc::strlen` on a `CString` or `CStr` value, while `count_bytes()` can be used instead"
|
||||
}
|
||||
|
||||
declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
|
||||
pub struct StrlenOnCStrings {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl StrlenOnCStrings {
|
||||
pub fn new(conf: &Conf) -> Self {
|
||||
Self { msrv: conf.msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
|
|
@ -47,7 +59,21 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
|
|||
&& let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind
|
||||
&& !recv.span.from_expansion()
|
||||
&& path.ident.name == sym::as_ptr
|
||||
&& let typeck = cx.typeck_results()
|
||||
&& typeck
|
||||
.expr_ty_adjusted(self_arg)
|
||||
.peel_refs()
|
||||
.is_lang_item(cx, LangItem::CStr)
|
||||
{
|
||||
let ty = typeck.expr_ty(self_arg).peel_refs();
|
||||
let ty_kind = if ty.is_diag_item(cx, sym::cstring_type) {
|
||||
"`CString` value"
|
||||
} else if ty.is_lang_item(cx, LangItem::CStr) {
|
||||
"`CStr` value"
|
||||
} else {
|
||||
"type that dereferences to `CStr`"
|
||||
};
|
||||
|
||||
let ctxt = expr.span.ctxt();
|
||||
let span = match cx.tcx.parent_hir_node(expr.hir_id) {
|
||||
Node::Block(&Block {
|
||||
|
|
@ -58,25 +84,23 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
|
|||
_ => expr.span,
|
||||
};
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
|
||||
let method_name = if ty.is_diag_item(cx, sym::cstring_type) {
|
||||
"as_bytes"
|
||||
} else if ty.is_lang_item(cx, LangItem::CStr) {
|
||||
"to_bytes"
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
STRLEN_ON_C_STRINGS,
|
||||
span,
|
||||
"using `libc::strlen` on a `CString` or `CStr` value",
|
||||
"try",
|
||||
format!("{val_name}.{method_name}().len()"),
|
||||
app,
|
||||
format!("using `libc::strlen` on a {ty_kind}"),
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let val_name = snippet_with_context(cx, self_arg.span, ctxt, "_", &mut app).0;
|
||||
|
||||
let suggestion = if self.msrv.meets(cx, msrvs::CSTR_COUNT_BYTES) {
|
||||
format!("{val_name}.count_bytes()")
|
||||
} else {
|
||||
format!("{val_name}.to_bytes().len()")
|
||||
};
|
||||
|
||||
diag.span_suggestion(span, "use", suggestion, app);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,18 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
|||
return true;
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(std::ptr::without_provenance::<i32>(0))`
|
||||
// `std::mem::transmute(std::ptr::without_provenance_mut::<i32>(0))`
|
||||
if let ExprKind::Call(func1, [arg1]) = arg.kind
|
||||
&& (func1.basic_res().is_diag_item(cx, sym::ptr_without_provenance)
|
||||
|| func1.basic_res().is_diag_item(cx, sym::ptr_without_provenance_mut))
|
||||
&& is_integer_const(cx, arg1, 0)
|
||||
{
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute({ 0 as *const u64 })` and similar const blocks
|
||||
if let ExprKind::Block(block, _) = arg.kind
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
|
|||
&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
|
||||
&& !is_unsafe_from_proc_macro(cx, block.span)
|
||||
&& !block_has_safety_comment(cx, block.span, self.accept_comment_above_attributes)
|
||||
&& !block_has_inner_safety_comment(cx, block.span)
|
||||
&& !block_parents_have_safety_comment(
|
||||
self.accept_comment_above_statement,
|
||||
self.accept_comment_above_attributes,
|
||||
|
|
@ -839,6 +840,23 @@ fn text_has_safety_comment(
|
|||
}
|
||||
}
|
||||
}
|
||||
// Check for a comment that appears after other code on the same line (e.g., `let x = // SAFETY:`)
|
||||
// This handles cases in macros where the comment is on the same line as preceding code.
|
||||
// We only check the first (immediate preceding) line for this pattern.
|
||||
// Only whitespace is allowed between the comment marker and `SAFETY:`.
|
||||
if let Some(comment_start) = [line.find("//"), line.find("/*")].into_iter().flatten().min()
|
||||
&& let after_marker = &line[comment_start + 2..] // skip marker
|
||||
&& let trimmed = after_marker.trim_start() // skip whitespace
|
||||
&& trimmed.get(..7).is_some_and(|s| s.eq_ignore_ascii_case("SAFETY:"))
|
||||
{
|
||||
let safety_offset = 2 + (after_marker.len() - trimmed.len());
|
||||
return HasSafetyComment::Yes(
|
||||
start_pos
|
||||
+ BytePos(u32::try_from(line_start).unwrap())
|
||||
+ BytePos(u32::try_from(comment_start + safety_offset).unwrap()),
|
||||
false,
|
||||
);
|
||||
}
|
||||
// No line comments; look for the start of a block comment.
|
||||
// This will only find them if they are at the start of a line.
|
||||
let (mut line_start, mut line) = (line_start, line);
|
||||
|
|
@ -894,3 +912,20 @@ fn is_const_or_static(node: &Node<'_>) -> bool {
|
|||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn block_has_inner_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
||||
let source_map = cx.sess().source_map();
|
||||
if let Ok(src) = source_map.span_to_snippet(span)
|
||||
&& let Some(after_brace) = src
|
||||
.strip_prefix("unsafe")
|
||||
.and_then(|s| s.trim_start().strip_prefix('{'))
|
||||
&& let Some(comment) = after_brace
|
||||
.trim_start()
|
||||
.strip_prefix("//")
|
||||
.or_else(|| after_brace.trim_start().strip_prefix("/*"))
|
||||
{
|
||||
comment.trim_start().to_ascii_uppercase().starts_with("SAFETY:")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,12 @@ fn insert_necessary_parens(pat: &mut Pat) {
|
|||
walk_pat(self, pat);
|
||||
let target = match &mut pat.kind {
|
||||
// `i @ a | b`, `box a | b`, and `& mut? a | b`.
|
||||
Ident(.., Some(p)) | Box(p) | Ref(p, _, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
|
||||
Ident(.., Some(p)) | Box(p) | Ref(p, _, _)
|
||||
if let Or(ps) = &p.kind
|
||||
&& ps.len() > 1 =>
|
||||
{
|
||||
p
|
||||
},
|
||||
// `&(mut x)`
|
||||
Ref(p, Pinnedness::Not, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p,
|
||||
_ => return,
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
format!("useless conversion to the same type: `{b}`"),
|
||||
"consider removing `.into_iter()`",
|
||||
sugg,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
ConstArgKind::Infer(..) => chain!(self, "let ConstArgKind::Infer(..) = {const_arg}.kind"),
|
||||
ConstArgKind::Error(..) => chain!(self, "let ConstArgKind::Error(..) = {const_arg}.kind"),
|
||||
ConstArgKind::Tup(..) => chain!(self, "let ConstArgKind::Tup(..) = {const_arg}.kind"),
|
||||
ConstArgKind::Literal(..) => chain!(self, "let ConstArgKind::Literal(..) = {const_arg}.kind")
|
||||
ConstArgKind::Literal(..) => chain!(self, "let ConstArgKind::Literal(..) = {const_arg}.kind"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, SyntaxContext};
|
||||
|
||||
use std::borrow::{Borrow, Cow};
|
||||
|
||||
|
|
@ -88,33 +88,42 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
|
|||
&& let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind
|
||||
&& let ExprKind::Path(..) = recv.kind
|
||||
{
|
||||
let and_then_snippets =
|
||||
get_and_then_snippets(cx, call_cx.span, call_lint.span, call_sp.span, call_msg.span);
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let expr_ctxt = expr.span.ctxt();
|
||||
let and_then_snippets = get_and_then_snippets(
|
||||
cx,
|
||||
expr_ctxt,
|
||||
call_cx.span,
|
||||
call_lint.span,
|
||||
call_sp.span,
|
||||
call_msg.span,
|
||||
&mut app,
|
||||
);
|
||||
let mut sle = SpanlessEq::new(cx).deny_side_effects();
|
||||
match ps.ident.as_str() {
|
||||
"span_suggestion" if sle.eq_expr(call_sp, &span_call_args[0]) => {
|
||||
suggest_suggestion(
|
||||
cx,
|
||||
expr,
|
||||
&and_then_snippets,
|
||||
&span_suggestion_snippets(cx, span_call_args),
|
||||
);
|
||||
match ps.ident.name {
|
||||
sym::span_suggestion if sle.eq_expr(call_sp, &span_call_args[0]) => {
|
||||
let snippets = span_suggestion_snippets(cx, expr_ctxt, span_call_args, &mut app);
|
||||
suggest_suggestion(cx, expr, &and_then_snippets, &snippets, app);
|
||||
},
|
||||
"span_help" if sle.eq_expr(call_sp, &span_call_args[0]) => {
|
||||
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
|
||||
sym::span_help if sle.eq_expr(call_sp, &span_call_args[0]) => {
|
||||
let help_snippet =
|
||||
snippet_with_context(cx, span_call_args[1].span, expr_ctxt, r#""...""#, &mut app).0;
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true, app);
|
||||
},
|
||||
"span_note" if sle.eq_expr(call_sp, &span_call_args[0]) => {
|
||||
let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
|
||||
sym::span_note if sle.eq_expr(call_sp, &span_call_args[0]) => {
|
||||
let note_snippet =
|
||||
snippet_with_context(cx, span_call_args[1].span, expr_ctxt, r#""...""#, &mut app).0;
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true, app);
|
||||
},
|
||||
"help" => {
|
||||
let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
|
||||
sym::help => {
|
||||
let help_snippet =
|
||||
snippet_with_context(cx, span_call_args[0].span, expr_ctxt, r#""...""#, &mut app).0;
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false, app);
|
||||
},
|
||||
"note" => {
|
||||
let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
|
||||
sym::note => {
|
||||
let note_snippet =
|
||||
snippet_with_context(cx, span_call_args[0].span, expr_ctxt, r#""...""#, &mut app).0;
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false, app);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -122,24 +131,26 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
|
|||
}
|
||||
}
|
||||
|
||||
struct AndThenSnippets<'a> {
|
||||
cx: Cow<'a, str>,
|
||||
lint: Cow<'a, str>,
|
||||
span: Cow<'a, str>,
|
||||
msg: Cow<'a, str>,
|
||||
struct AndThenSnippets {
|
||||
cx: Cow<'static, str>,
|
||||
lint: Cow<'static, str>,
|
||||
span: Cow<'static, str>,
|
||||
msg: Cow<'static, str>,
|
||||
}
|
||||
|
||||
fn get_and_then_snippets(
|
||||
cx: &LateContext<'_>,
|
||||
expr_ctxt: SyntaxContext,
|
||||
cx_span: Span,
|
||||
lint_span: Span,
|
||||
span_span: Span,
|
||||
msg_span: Span,
|
||||
) -> AndThenSnippets<'static> {
|
||||
let cx_snippet = snippet(cx, cx_span, "cx");
|
||||
let lint_snippet = snippet(cx, lint_span, "..");
|
||||
let span_snippet = snippet(cx, span_span, "span");
|
||||
let msg_snippet = snippet(cx, msg_span, r#""...""#);
|
||||
app: &mut Applicability,
|
||||
) -> AndThenSnippets {
|
||||
let cx_snippet = snippet_with_applicability(cx, cx_span, "cx", app);
|
||||
let lint_snippet = snippet_with_applicability(cx, lint_span, "..", app);
|
||||
let span_snippet = snippet_with_applicability(cx, span_span, "span", app);
|
||||
let msg_snippet = snippet_with_context(cx, msg_span, expr_ctxt, r#""...""#, app).0;
|
||||
|
||||
AndThenSnippets {
|
||||
cx: cx_snippet,
|
||||
|
|
@ -149,19 +160,22 @@ fn get_and_then_snippets(
|
|||
}
|
||||
}
|
||||
|
||||
struct SpanSuggestionSnippets<'a> {
|
||||
help: Cow<'a, str>,
|
||||
sugg: Cow<'a, str>,
|
||||
applicability: Cow<'a, str>,
|
||||
struct SpanSuggestionSnippets {
|
||||
help: Cow<'static, str>,
|
||||
sugg: Cow<'static, str>,
|
||||
applicability: Cow<'static, str>,
|
||||
}
|
||||
|
||||
fn span_suggestion_snippets<'a, 'hir>(
|
||||
fn span_suggestion_snippets<'hir>(
|
||||
cx: &LateContext<'_>,
|
||||
expr_ctxt: SyntaxContext,
|
||||
span_call_args: &'hir [Expr<'hir>],
|
||||
) -> SpanSuggestionSnippets<'a> {
|
||||
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
let sugg_snippet = snippet(cx, span_call_args[2].span, "..");
|
||||
let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable");
|
||||
app: &mut Applicability,
|
||||
) -> SpanSuggestionSnippets {
|
||||
let help_snippet = snippet_with_context(cx, span_call_args[1].span, expr_ctxt, r#""...""#, app).0;
|
||||
let sugg_snippet = snippet_with_context(cx, span_call_args[2].span, expr_ctxt, "..", app).0;
|
||||
let applicability_snippet =
|
||||
snippet_with_applicability(cx, span_call_args[3].span, "Applicability::MachineApplicable", app);
|
||||
|
||||
SpanSuggestionSnippets {
|
||||
help: help_snippet,
|
||||
|
|
@ -173,8 +187,9 @@ fn span_suggestion_snippets<'a, 'hir>(
|
|||
fn suggest_suggestion(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
and_then_snippets: &AndThenSnippets<'_>,
|
||||
span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
|
||||
and_then_snippets: &AndThenSnippets,
|
||||
span_suggestion_snippets: &SpanSuggestionSnippets,
|
||||
app: Applicability,
|
||||
) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
@ -192,16 +207,17 @@ fn suggest_suggestion(
|
|||
span_suggestion_snippets.sugg,
|
||||
span_suggestion_snippets.applicability
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
fn suggest_help(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
and_then_snippets: &AndThenSnippets<'_>,
|
||||
and_then_snippets: &AndThenSnippets,
|
||||
help: &str,
|
||||
with_span: bool,
|
||||
app: Applicability,
|
||||
) {
|
||||
let option_span = if with_span {
|
||||
format!("Some({})", and_then_snippets.span)
|
||||
|
|
@ -219,16 +235,17 @@ fn suggest_help(
|
|||
"span_lint_and_help({}, {}, {}, {}, {}, {help})",
|
||||
and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
fn suggest_note(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
and_then_snippets: &AndThenSnippets<'_>,
|
||||
and_then_snippets: &AndThenSnippets,
|
||||
note: &str,
|
||||
with_span: bool,
|
||||
app: Applicability,
|
||||
) {
|
||||
let note_span = if with_span {
|
||||
format!("Some({})", and_then_snippets.span)
|
||||
|
|
@ -246,6 +263,6 @@ fn suggest_note(
|
|||
"span_lint_and_note({}, {}, {}, {}, {note_span}, {note})",
|
||||
and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
#![allow(
|
||||
clippy::missing_docs_in_private_items,
|
||||
clippy::must_use_candidate,
|
||||
clippy::symbol_as_str,
|
||||
clippy::symbol_as_str
|
||||
)]
|
||||
#![warn(
|
||||
trivial_casts,
|
||||
|
|
@ -29,7 +29,7 @@ extern crate rustc_session;
|
|||
extern crate rustc_span;
|
||||
|
||||
mod almost_standard_lint_formulation;
|
||||
mod collapsible_calls;
|
||||
mod collapsible_span_lint_calls;
|
||||
mod derive_deserialize_allowing_unknown;
|
||||
mod internal_paths;
|
||||
mod lint_without_lint_pass;
|
||||
|
|
@ -46,7 +46,7 @@ use rustc_lint::{Lint, LintStore};
|
|||
|
||||
static LINTS: &[&Lint] = &[
|
||||
almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION,
|
||||
collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
collapsible_span_lint_calls::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
derive_deserialize_allowing_unknown::DERIVE_DESERIALIZE_ALLOWING_UNKNOWN,
|
||||
lint_without_lint_pass::DEFAULT_LINT,
|
||||
lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE,
|
||||
|
|
@ -67,7 +67,7 @@ pub fn register_lints(store: &mut LintStore) {
|
|||
|
||||
store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths));
|
||||
store.register_early_pass(|| Box::new(produce_ice::ProduceIce));
|
||||
store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls));
|
||||
store.register_late_pass(|_| Box::new(collapsible_span_lint_calls::CollapsibleCalls));
|
||||
store.register_late_pass(|_| Box::new(derive_deserialize_allowing_unknown::DeriveDeserializeAllowingUnknown));
|
||||
store.register_late_pass(|_| Box::<symbols::Symbols>::default());
|
||||
store.register_late_pass(|_| Box::<lint_without_lint_pass::LintWithoutLintPass>::default());
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
edition = "2024"
|
||||
description = "Helpful tools for writing lints, provided as they are used in Clippy"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
|
|||
|
||||
<!-- begin autogenerated nightly -->
|
||||
```
|
||||
nightly-2026-01-08
|
||||
nightly-2026-01-22
|
||||
```
|
||||
<!-- end autogenerated nightly -->
|
||||
|
||||
|
|
|
|||
|
|
@ -159,7 +159,10 @@ impl LimitStack {
|
|||
}
|
||||
pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
|
||||
let stack = &mut self.stack;
|
||||
parse_attrs(sess, attrs, name, |val| debug_assert_eq!(stack.pop(), Some(val)));
|
||||
parse_attrs(sess, attrs, name, |val| {
|
||||
let popped = stack.pop();
|
||||
debug_assert_eq!(popped, Some(val));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
(ExprKind::Block(l, _), ExprKind::Block(r, _)) => self.eq_block(l, r),
|
||||
(ExprKind::Binary(l_op, ll, lr), ExprKind::Binary(r_op, rl, rr)) => {
|
||||
l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
|
||||
|| swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| {
|
||||
|| swap_binop(self.inner.cx, l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| {
|
||||
l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
|
||||
})
|
||||
},
|
||||
|
|
@ -667,7 +667,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
|
||||
match (&left.kind, &right.kind) {
|
||||
(ConstArgKind::Tup(l_t), ConstArgKind::Tup(r_t)) => {
|
||||
l_t.len() == r_t.len() && l_t.iter().zip(*r_t).all(|(l_c, r_c)| self.eq_const_arg(*l_c, *r_c))
|
||||
l_t.len() == r_t.len() && l_t.iter().zip(*r_t).all(|(l_c, r_c)| self.eq_const_arg(l_c, r_c))
|
||||
},
|
||||
(ConstArgKind::Path(l_p), ConstArgKind::Path(r_p)) => self.eq_qpath(l_p, r_p),
|
||||
(ConstArgKind::Anon(l_an), ConstArgKind::Anon(r_an)) => self.eq_body(l_an.body, r_an.body),
|
||||
|
|
@ -689,9 +689,12 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
(ConstArgKind::Literal(kind_l), ConstArgKind::Literal(kind_r)) => kind_l == kind_r,
|
||||
(ConstArgKind::Array(l_arr), ConstArgKind::Array(r_arr)) => {
|
||||
l_arr.elems.len() == r_arr.elems.len()
|
||||
&& l_arr.elems.iter().zip(r_arr.elems.iter())
|
||||
.all(|(l_elem, r_elem)| self.eq_const_arg(l_elem, r_elem))
|
||||
}
|
||||
&& l_arr
|
||||
.elems
|
||||
.iter()
|
||||
.zip(r_arr.elems.iter())
|
||||
.all(|(l_elem, r_elem)| self.eq_const_arg(l_elem, r_elem))
|
||||
},
|
||||
// Use explicit match for now since ConstArg is undergoing flux.
|
||||
(
|
||||
ConstArgKind::Path(..)
|
||||
|
|
@ -955,26 +958,35 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
|
|||
}
|
||||
|
||||
fn swap_binop<'a>(
|
||||
cx: &LateContext<'_>,
|
||||
binop: BinOpKind,
|
||||
lhs: &'a Expr<'a>,
|
||||
rhs: &'a Expr<'a>,
|
||||
) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
|
||||
match binop {
|
||||
BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
|
||||
Some((binop, rhs, lhs))
|
||||
},
|
||||
// `==` and `!=`, are commutative
|
||||
BinOpKind::Eq | BinOpKind::Ne => Some((binop, rhs, lhs)),
|
||||
// Comparisons can be reversed
|
||||
BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
|
||||
BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
|
||||
BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
|
||||
BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
|
||||
BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
|
||||
| BinOpKind::Shl
|
||||
| BinOpKind::Shr
|
||||
| BinOpKind::Rem
|
||||
| BinOpKind::Sub
|
||||
| BinOpKind::Div
|
||||
// Non-commutative operators
|
||||
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Rem | BinOpKind::Sub | BinOpKind::Div => None,
|
||||
// We know that those operators are commutative for primitive types,
|
||||
// and we don't assume anything for other types
|
||||
BinOpKind::Mul
|
||||
| BinOpKind::Add
|
||||
| BinOpKind::And
|
||||
| BinOpKind::Or => None,
|
||||
| BinOpKind::Or
|
||||
| BinOpKind::BitAnd
|
||||
| BinOpKind::BitXor
|
||||
| BinOpKind::BitOr => cx
|
||||
.typeck_results()
|
||||
.expr_ty_adjusted(lhs)
|
||||
.peel_refs()
|
||||
.is_primitive()
|
||||
.then_some((binop, rhs, lhs)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1564,7 +1576,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
match &const_arg.kind {
|
||||
ConstArgKind::Tup(tup) => {
|
||||
for arg in *tup {
|
||||
self.hash_const_arg(*arg);
|
||||
self.hash_const_arg(arg);
|
||||
}
|
||||
},
|
||||
ConstArgKind::Path(path) => self.hash_qpath(path),
|
||||
|
|
|
|||
|
|
@ -6,11 +6,7 @@
|
|||
#![feature(assert_matches)]
|
||||
#![feature(unwrap_infallible)]
|
||||
#![recursion_limit = "512"]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::must_use_candidate,
|
||||
)]
|
||||
#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
|
||||
#![warn(
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
|
|
@ -2028,12 +2024,12 @@ pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
}
|
||||
|
||||
pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
|
||||
if !is_no_std_crate(cx) {
|
||||
Some("std")
|
||||
} else if !is_no_core_crate(cx) {
|
||||
if is_no_core_crate(cx) {
|
||||
None
|
||||
} else if is_no_std_crate(cx) {
|
||||
Some("core")
|
||||
} else {
|
||||
None
|
||||
Some("std")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ macro_rules! msrv_aliases {
|
|||
|
||||
// names may refer to stabilized feature flags or library items
|
||||
msrv_aliases! {
|
||||
1,91,0 { DURATION_FROM_MINUTES_HOURS }
|
||||
1,88,0 { LET_CHAINS }
|
||||
1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST }
|
||||
1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL }
|
||||
|
|
@ -31,7 +32,7 @@ msrv_aliases! {
|
|||
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP, SPECIALIZED_TO_STRING_FOR_REFS }
|
||||
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF }
|
||||
1,80,0 { BOX_INTO_ITER, LAZY_CELL }
|
||||
1,79,0 { CONST_BLOCKS }
|
||||
1,79,0 { CONST_BLOCKS, CSTR_COUNT_BYTES }
|
||||
1,77,0 { C_STR_LITERALS }
|
||||
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
|
||||
1,75,0 { OPTION_AS_SLICE }
|
||||
|
|
@ -61,7 +62,7 @@ msrv_aliases! {
|
|||
1,45,0 { STR_STRIP_PREFIX }
|
||||
1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS }
|
||||
1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
|
||||
1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
|
||||
1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR, RESULT_MAP_OR_ELSE }
|
||||
1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
|
||||
1,38,0 { POINTER_CAST, REM_EUCLID }
|
||||
1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
|
||||
|
|
@ -69,12 +70,12 @@ msrv_aliases! {
|
|||
1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
|
||||
1,34,0 { TRY_FROM }
|
||||
1,33,0 { UNDERSCORE_IMPORTS }
|
||||
1,32,0 { CONST_IS_POWER_OF_TWO }
|
||||
1,32,0 { CONST_IS_POWER_OF_TWO, CONST_DURATION_FROM_NANOS_MICROS_MILLIS_SECS }
|
||||
1,31,0 { OPTION_REPLACE }
|
||||
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
|
||||
1,29,0 { ITER_FLATTEN }
|
||||
1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF }
|
||||
1,27,0 { ITERATOR_TRY_FOLD, DOUBLE_ENDED_ITERATOR_RFIND }
|
||||
1,27,0 { ITERATOR_TRY_FOLD, DOUBLE_ENDED_ITERATOR_RFIND, DURATION_FROM_NANOS_MICROS }
|
||||
1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS }
|
||||
1,24,0 { IS_ASCII_DIGIT, PTR_NULL }
|
||||
1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
|
||||
|
|
@ -82,6 +83,7 @@ msrv_aliases! {
|
|||
1,16,0 { STR_REPEAT, RESULT_UNWRAP_OR_DEFAULT }
|
||||
1,15,0 { MAYBE_BOUND_IN_WHERE }
|
||||
1,13,0 { QUESTION_MARK_OPERATOR }
|
||||
1,3,0 { DURATION_FROM_MILLIS_SECS }
|
||||
}
|
||||
|
||||
/// `#[clippy::msrv]` attributes are rarely used outside of Clippy's test suite, as a basic
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ generate! {
|
|||
disallowed_types,
|
||||
drain,
|
||||
dump,
|
||||
duration_constructors,
|
||||
ends_with,
|
||||
enum_glob_use,
|
||||
enumerate,
|
||||
|
|
@ -163,12 +164,20 @@ generate! {
|
|||
from_be_bytes,
|
||||
from_bytes_with_nul,
|
||||
from_bytes_with_nul_unchecked,
|
||||
from_days,
|
||||
from_hours,
|
||||
from_le_bytes,
|
||||
from_micros,
|
||||
from_millis,
|
||||
from_mins,
|
||||
from_nanos,
|
||||
from_ne_bytes,
|
||||
from_ptr,
|
||||
from_raw,
|
||||
from_raw_parts,
|
||||
from_secs,
|
||||
from_str_radix,
|
||||
from_weeks,
|
||||
fs,
|
||||
fuse,
|
||||
futures_util,
|
||||
|
|
@ -179,6 +188,7 @@ generate! {
|
|||
get_unchecked,
|
||||
get_unchecked_mut,
|
||||
has_significant_drop,
|
||||
help,
|
||||
hidden_glob_reexports,
|
||||
hygiene,
|
||||
ilog,
|
||||
|
|
@ -271,6 +281,8 @@ generate! {
|
|||
powi,
|
||||
product,
|
||||
push,
|
||||
push_back,
|
||||
push_front,
|
||||
push_str,
|
||||
read,
|
||||
read_exact,
|
||||
|
|
@ -325,7 +337,10 @@ generate! {
|
|||
sort,
|
||||
sort_by,
|
||||
sort_unstable_by,
|
||||
span_help,
|
||||
span_lint_and_then,
|
||||
span_note,
|
||||
span_suggestion,
|
||||
split,
|
||||
split_at,
|
||||
split_at_checked,
|
||||
|
|
|
|||
|
|
@ -441,6 +441,21 @@ pub fn peel_and_count_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize, Option<Mutabili
|
|||
(ty, count, mutbl)
|
||||
}
|
||||
|
||||
/// Peels off `n` references on the type. Returns the underlying type and, if any references
|
||||
/// were removed, whether the pointer is ultimately mutable or not.
|
||||
pub fn peel_n_ty_refs(mut ty: Ty<'_>, n: usize) -> (Ty<'_>, Option<Mutability>) {
|
||||
let mut mutbl = None;
|
||||
for _ in 0..n {
|
||||
if let ty::Ref(_, dest_ty, m) = ty.kind() {
|
||||
ty = *dest_ty;
|
||||
mutbl.replace(mutbl.map_or(*m, |mutbl: Mutability| mutbl.min(*m)));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(ty, mutbl)
|
||||
}
|
||||
|
||||
/// Checks whether `a` and `b` are same types having same `Const` generic args, but ignores
|
||||
/// lifetimes.
|
||||
///
|
||||
|
|
@ -818,7 +833,7 @@ impl AdtVariantInfo {
|
|||
.enumerate()
|
||||
.map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst))))
|
||||
.collect::<Vec<_>>();
|
||||
fields_size.sort_by(|(_, a_size), (_, b_size)| a_size.cmp(b_size));
|
||||
fields_size.sort_by_key(|(_, a_size)| *a_size);
|
||||
|
||||
Self {
|
||||
ind: i,
|
||||
|
|
@ -827,7 +842,7 @@ impl AdtVariantInfo {
|
|||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
variants_size.sort_by(|a, b| b.size.cmp(&a.size));
|
||||
variants_size.sort_by_key(|b| std::cmp::Reverse(b.size));
|
||||
variants_size
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "declare_clippy_lint"
|
||||
version = "0.1.94"
|
||||
version = "0.1.95"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
|
|
|||
|
|
@ -281,8 +281,7 @@ impl CrateWithSource {
|
|||
CrateSource::Path { path } => {
|
||||
fn is_cache_dir(entry: &DirEntry) -> bool {
|
||||
fs::read(entry.path().join("CACHEDIR.TAG"))
|
||||
.map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
|
||||
.unwrap_or(false)
|
||||
.is_ok_and(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
|
||||
}
|
||||
|
||||
// copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[toolchain]
|
||||
# begin autogenerated nightly
|
||||
channel = "nightly-2026-01-08"
|
||||
channel = "nightly-2026-01-22"
|
||||
# end autogenerated nightly
|
||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
profile = "minimal"
|
||||
|
|
|
|||
|
|
@ -30,11 +30,10 @@ use rustc_span::symbol::Symbol;
|
|||
|
||||
use std::env;
|
||||
use std::fs::read_to_string;
|
||||
use std::io::Write as _;
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
|
||||
use anstream::println;
|
||||
|
||||
/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
|
||||
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
|
||||
fn arg_value<'a>(args: &'a [String], find_arg: &str, pred: impl Fn(&str) -> bool) -> Option<&'a str> {
|
||||
|
|
@ -184,7 +183,9 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
|
|||
}
|
||||
|
||||
fn display_help() {
|
||||
println!("{}", help_message());
|
||||
if writeln!(&mut anstream::stdout().lock(), "{}", help_message()).is_err() {
|
||||
exit(rustc_driver::EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml";
|
||||
|
|
@ -251,8 +252,10 @@ pub fn main() {
|
|||
if orig_args.iter().any(|a| a == "--version" || a == "-V") {
|
||||
let version_info = rustc_tools_util::get_version_info!();
|
||||
|
||||
println!("{version_info}");
|
||||
exit(0);
|
||||
match writeln!(&mut anstream::stdout().lock(), "{version_info}") {
|
||||
Ok(()) => exit(rustc_driver::EXIT_SUCCESS),
|
||||
Err(_) => exit(rustc_driver::EXIT_FAILURE),
|
||||
}
|
||||
}
|
||||
|
||||
// Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
|
||||
|
|
|
|||
|
|
@ -4,19 +4,24 @@
|
|||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{self, Command};
|
||||
extern crate rustc_driver;
|
||||
|
||||
use anstream::println;
|
||||
use std::env;
|
||||
use std::io::Write as _;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{self, Command, exit};
|
||||
|
||||
fn show_help() {
|
||||
println!("{}", help_message());
|
||||
if writeln!(&mut anstream::stdout().lock(), "{}", help_message()).is_err() {
|
||||
exit(rustc_driver::EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
fn show_version() {
|
||||
let version_info = rustc_tools_util::get_version_info!();
|
||||
println!("{version_info}");
|
||||
if writeln!(&mut anstream::stdout().lock(), "{version_info}").is_err() {
|
||||
exit(rustc_driver::EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,15 @@ impl EarlyLintPass for Pass {
|
|||
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
db.help(help_msg).help(help_msg);
|
||||
});
|
||||
|
||||
// Issue #15880
|
||||
#[expect(clippy::disallowed_names)]
|
||||
let foo = "foo";
|
||||
span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, format!("try using {foo}"), format!("{foo}.use"), Applicability::MachineApplicable);
|
||||
span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), format!("try using {foo}"));
|
||||
span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, format!("try using {foo}"));
|
||||
span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), format!("required because of {foo}"));
|
||||
span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, format!("required because of {foo}"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,35 @@ impl EarlyLintPass for Pass {
|
|||
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
db.help(help_msg).help(help_msg);
|
||||
});
|
||||
|
||||
// Issue #15880
|
||||
#[expect(clippy::disallowed_names)]
|
||||
let foo = "foo";
|
||||
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
//~^ collapsible_span_lint_calls
|
||||
db.span_suggestion(
|
||||
expr.span,
|
||||
format!("try using {foo}"),
|
||||
format!("{foo}.use"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
});
|
||||
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
//~^ collapsible_span_lint_calls
|
||||
db.span_help(expr.span, format!("try using {foo}"));
|
||||
});
|
||||
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
//~^ collapsible_span_lint_calls
|
||||
db.help(format!("try using {foo}"));
|
||||
});
|
||||
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
//~^ collapsible_span_lint_calls
|
||||
db.span_note(expr.span, format!("required because of {foo}"));
|
||||
});
|
||||
span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
//~^ collapsible_span_lint_calls
|
||||
db.note(format!("required because of {foo}"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,5 +49,53 @@ LL | | db.note(note_msg);
|
|||
LL | | });
|
||||
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
error: this call is collapsible
|
||||
--> tests/ui-internal/collapsible_span_lint_calls.rs:72:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | |
|
||||
LL | | db.span_suggestion(
|
||||
LL | | expr.span,
|
||||
... |
|
||||
LL | | );
|
||||
LL | | });
|
||||
| |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, format!("try using {foo}"), format!("{foo}.use"), Applicability::MachineApplicable)`
|
||||
|
||||
error: this call is collapsible
|
||||
--> tests/ui-internal/collapsible_span_lint_calls.rs:81:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | |
|
||||
LL | | db.span_help(expr.span, format!("try using {foo}"));
|
||||
LL | | });
|
||||
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), format!("try using {foo}"))`
|
||||
|
||||
error: this call is collapsible
|
||||
--> tests/ui-internal/collapsible_span_lint_calls.rs:85:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | |
|
||||
LL | | db.help(format!("try using {foo}"));
|
||||
LL | | });
|
||||
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, format!("try using {foo}"))`
|
||||
|
||||
error: this call is collapsible
|
||||
--> tests/ui-internal/collapsible_span_lint_calls.rs:89:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | |
|
||||
LL | | db.span_note(expr.span, format!("required because of {foo}"));
|
||||
LL | | });
|
||||
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), format!("required because of {foo}"))`
|
||||
|
||||
error: this call is collapsible
|
||||
--> tests/ui-internal/collapsible_span_lint_calls.rs:93:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | |
|
||||
LL | | db.note(format!("required because of {foo}"));
|
||||
LL | | });
|
||||
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, format!("required because of {foo}"))`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,39 @@
|
|||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:270:19
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:244:22
|
||||
|
|
||||
LL | let _x = unsafe { 1 };
|
||||
| ^^^^^^^^^^^^
|
||||
...
|
||||
LL | t!();
|
||||
| ---- in this macro invocation
|
||||
|
|
||||
= help: consider adding a safety comment on the preceding line
|
||||
= note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]`
|
||||
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:256:13
|
||||
|
|
||||
LL | unsafe { 1 };
|
||||
| ^^^^^^^^^^^^
|
||||
...
|
||||
LL | t!();
|
||||
| ---- in this macro invocation
|
||||
|
|
||||
= help: consider adding a safety comment on the preceding line
|
||||
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:337:19
|
||||
|
|
||||
LL | /* Safety: */ unsafe {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: consider adding a safety comment on the preceding line
|
||||
= note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]`
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:275:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:342:5
|
||||
|
|
||||
LL | unsafe {}
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -17,7 +41,7 @@ LL | unsafe {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:280:14
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:347:14
|
||||
|
|
||||
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -25,7 +49,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:280:29
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:347:29
|
||||
|
|
||||
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -33,7 +57,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:280:48
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:347:48
|
||||
|
|
||||
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -41,7 +65,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:18
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:354:18
|
||||
|
|
||||
LL | let _ = (42, unsafe {}, "test", unsafe {});
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -49,7 +73,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {});
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:37
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:354:37
|
||||
|
|
||||
LL | let _ = (42, unsafe {}, "test", unsafe {});
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -57,7 +81,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {});
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:293:14
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:360:14
|
||||
|
|
||||
LL | let _ = *unsafe { &42 };
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
@ -65,7 +89,7 @@ LL | let _ = *unsafe { &42 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:299:19
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:366:19
|
||||
|
|
||||
LL | let _ = match unsafe {} {
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -73,7 +97,7 @@ LL | let _ = match unsafe {} {
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:306:14
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:373:14
|
||||
|
|
||||
LL | let _ = &unsafe {};
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -81,7 +105,7 @@ LL | let _ = &unsafe {};
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:311:14
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:378:14
|
||||
|
|
||||
LL | let _ = [unsafe {}; 5];
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -89,7 +113,7 @@ LL | let _ = [unsafe {}; 5];
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:316:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:383:13
|
||||
|
|
||||
LL | let _ = unsafe {};
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -97,7 +121,7 @@ LL | let _ = unsafe {};
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:327:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:394:8
|
||||
|
|
||||
LL | t!(unsafe {});
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -105,7 +129,7 @@ LL | t!(unsafe {});
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:334:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:401:13
|
||||
|
|
||||
LL | unsafe {}
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -117,7 +141,7 @@ LL | t!();
|
|||
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:343:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:410:5
|
||||
|
|
||||
LL | unsafe {} // SAFETY:
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -125,7 +149,7 @@ LL | unsafe {} // SAFETY:
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:349:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:416:5
|
||||
|
|
||||
LL | unsafe {
|
||||
| ^^^^^^^^
|
||||
|
|
@ -133,7 +157,7 @@ LL | unsafe {
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:360:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:427:5
|
||||
|
|
||||
LL | unsafe {};
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -141,7 +165,7 @@ LL | unsafe {};
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:365:20
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:432:20
|
||||
|
|
||||
LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -149,7 +173,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:373:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:440:5
|
||||
|
|
||||
LL | unsafe impl A for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -157,7 +181,7 @@ LL | unsafe impl A for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:381:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:448:9
|
||||
|
|
||||
LL | unsafe impl B for (u32) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -165,7 +189,7 @@ LL | unsafe impl B for (u32) {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:470:13
|
||||
|
|
||||
LL | unsafe impl T for $t {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -177,7 +201,7 @@ LL | no_safety_comment!(());
|
|||
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:429:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:496:13
|
||||
|
|
||||
LL | unsafe impl T for $t {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -189,7 +213,7 @@ LL | no_safety_comment!(());
|
|||
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:439:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:506:5
|
||||
|
|
||||
LL | unsafe impl T for (i32) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -197,7 +221,7 @@ LL | unsafe impl T for (i32) {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:429:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:496:13
|
||||
|
|
||||
LL | unsafe impl T for $t {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -209,7 +233,7 @@ LL | no_safety_comment!(u32);
|
|||
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:446:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:513:5
|
||||
|
|
||||
LL | unsafe impl T for (bool) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -217,7 +241,7 @@ LL | unsafe impl T for (bool) {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:493:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:560:5
|
||||
|
|
||||
LL | unsafe impl NoComment for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -225,7 +249,7 @@ LL | unsafe impl NoComment for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:498:19
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:565:19
|
||||
|
|
||||
LL | /* SAFETY: */ unsafe impl InlineComment for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -233,7 +257,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:503:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:570:5
|
||||
|
|
||||
LL | unsafe impl TrailingComment for () {} // SAFETY:
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -241,13 +265,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY:
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: constant has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:508:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:575:5
|
||||
|
|
||||
LL | const BIG_NUMBER: i32 = 1000000;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:574:8
|
||||
|
|
||||
LL | // SAFETY:
|
||||
| ^^^^^^^
|
||||
|
|
@ -255,7 +279,7 @@ LL | // SAFETY:
|
|||
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]`
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:510:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:577:5
|
||||
|
|
||||
LL | unsafe impl Interference for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -263,7 +287,7 @@ LL | unsafe impl Interference for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:518:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:585:5
|
||||
|
|
||||
LL | unsafe impl ImplInFn for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -271,7 +295,7 @@ LL | unsafe impl ImplInFn for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:528:1
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:595:1
|
||||
|
|
||||
LL | unsafe impl CrateRoot for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -279,7 +303,7 @@ LL | unsafe impl CrateRoot for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: statement has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:543:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:610:5
|
||||
|
|
||||
LL | / let _ = {
|
||||
LL | |
|
||||
|
|
@ -289,13 +313,13 @@ LL | | };
|
|||
| |______^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:609:8
|
||||
|
|
||||
LL | // SAFETY: this is more than one level away, so it should warn
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:545:12
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:612:12
|
||||
|
|
||||
LL | if unsafe { true } {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
@ -303,7 +327,7 @@ LL | if unsafe { true } {
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:549:23
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:616:23
|
||||
|
|
||||
LL | let bar = unsafe {};
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -311,7 +335,7 @@ LL | let bar = unsafe {};
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:705:52
|
||||
|
|
||||
LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 };
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -319,7 +343,7 @@ LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:714:41
|
||||
|
|
||||
LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 };
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -327,7 +351,7 @@ LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:724:42
|
||||
|
|
||||
LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 };
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -335,7 +359,7 @@ LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:729:40
|
||||
|
|
||||
LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 };
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -343,136 +367,136 @@ LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: constant has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:701:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:768:5
|
||||
|
|
||||
LL | const UNIX_EPOCH_JULIAN_DAY: i32 =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:699:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:766:8
|
||||
|
|
||||
LL | // SAFETY: fail ONLY if `accept-comment-above-attribute = false`
|
||||
| ^^^^^^^
|
||||
|
||||
error: statement has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:721:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:788:5
|
||||
|
|
||||
LL | _ = bar();
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:720:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:8
|
||||
|
|
||||
LL | // SAFETY: unnecessary_safety_comment triggers here
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: module has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:741:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:808:5
|
||||
|
|
||||
LL | mod x {}
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:740:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:807:8
|
||||
|
|
||||
LL | // SAFETY: ...
|
||||
| ^^^^^^^
|
||||
|
||||
error: module has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:746:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:813:5
|
||||
|
|
||||
LL | mod y {}
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:744:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:811:8
|
||||
|
|
||||
LL | // SAFETY: ...
|
||||
| ^^^^^^^
|
||||
|
||||
error: module has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:751:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:818:5
|
||||
|
|
||||
LL | mod z {}
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:750:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:817:8
|
||||
|
|
||||
LL | // SAFETY: ...
|
||||
| ^^^^^^^
|
||||
|
||||
error: module has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:759:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:826:5
|
||||
|
|
||||
LL | mod y {}
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:757:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:824:8
|
||||
|
|
||||
LL | // SAFETY: ...
|
||||
| ^^^^^^^
|
||||
|
||||
error: statement has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:774:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:841:9
|
||||
|
|
||||
LL | let x = 34;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:772:12
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:839:12
|
||||
|
|
||||
LL | // SAFETY: ...
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: function has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:781:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:848:5
|
||||
|
|
||||
LL | unsafe fn unsafe_comment() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider changing the `safety` comment for a `# Safety` doc comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:780:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:847:8
|
||||
|
|
||||
LL | // SAFETY: Bla
|
||||
| ^^^^^^^
|
||||
|
||||
error: function has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:854:5
|
||||
|
|
||||
LL | unsafe fn unsafe_block_comment() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider changing the `safety` comment for a `# Safety` doc comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:785:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:852:8
|
||||
|
|
||||
LL | SAFETY: Bla
|
||||
| ^^^^^^^
|
||||
|
||||
error: function has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:791:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:858:5
|
||||
|
|
||||
LL | fn safe_comment() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:790:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:857:8
|
||||
|
|
||||
LL | // SAFETY: Bla
|
||||
| ^^^^^^^
|
||||
|
||||
error: function has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:795:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:862:5
|
||||
|
|
||||
LL | fn safe_doc_comment() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:794:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:861:9
|
||||
|
|
||||
LL | /// SAFETY: Bla
|
||||
| ^^^^^^^
|
||||
|
||||
error: aborting due to 50 previous errors
|
||||
error: aborting due to 52 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,39 @@
|
|||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:270:19
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:244:22
|
||||
|
|
||||
LL | let _x = unsafe { 1 };
|
||||
| ^^^^^^^^^^^^
|
||||
...
|
||||
LL | t!();
|
||||
| ---- in this macro invocation
|
||||
|
|
||||
= help: consider adding a safety comment on the preceding line
|
||||
= note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]`
|
||||
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:256:13
|
||||
|
|
||||
LL | unsafe { 1 };
|
||||
| ^^^^^^^^^^^^
|
||||
...
|
||||
LL | t!();
|
||||
| ---- in this macro invocation
|
||||
|
|
||||
= help: consider adding a safety comment on the preceding line
|
||||
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:337:19
|
||||
|
|
||||
LL | /* Safety: */ unsafe {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: consider adding a safety comment on the preceding line
|
||||
= note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]`
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:275:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:342:5
|
||||
|
|
||||
LL | unsafe {}
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -17,7 +41,7 @@ LL | unsafe {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:280:14
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:347:14
|
||||
|
|
||||
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -25,7 +49,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:280:29
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:347:29
|
||||
|
|
||||
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -33,7 +57,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:280:48
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:347:48
|
||||
|
|
||||
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
@ -41,7 +65,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:18
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:354:18
|
||||
|
|
||||
LL | let _ = (42, unsafe {}, "test", unsafe {});
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -49,7 +73,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {});
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:37
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:354:37
|
||||
|
|
||||
LL | let _ = (42, unsafe {}, "test", unsafe {});
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -57,7 +81,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {});
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:293:14
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:360:14
|
||||
|
|
||||
LL | let _ = *unsafe { &42 };
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
@ -65,7 +89,7 @@ LL | let _ = *unsafe { &42 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:299:19
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:366:19
|
||||
|
|
||||
LL | let _ = match unsafe {} {
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -73,7 +97,7 @@ LL | let _ = match unsafe {} {
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:306:14
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:373:14
|
||||
|
|
||||
LL | let _ = &unsafe {};
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -81,7 +105,7 @@ LL | let _ = &unsafe {};
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:311:14
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:378:14
|
||||
|
|
||||
LL | let _ = [unsafe {}; 5];
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -89,7 +113,7 @@ LL | let _ = [unsafe {}; 5];
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:316:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:383:13
|
||||
|
|
||||
LL | let _ = unsafe {};
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -97,7 +121,7 @@ LL | let _ = unsafe {};
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:327:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:394:8
|
||||
|
|
||||
LL | t!(unsafe {});
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -105,7 +129,7 @@ LL | t!(unsafe {});
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:334:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:401:13
|
||||
|
|
||||
LL | unsafe {}
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -117,7 +141,7 @@ LL | t!();
|
|||
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:343:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:410:5
|
||||
|
|
||||
LL | unsafe {} // SAFETY:
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -125,7 +149,7 @@ LL | unsafe {} // SAFETY:
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:349:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:416:5
|
||||
|
|
||||
LL | unsafe {
|
||||
| ^^^^^^^^
|
||||
|
|
@ -133,7 +157,7 @@ LL | unsafe {
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:360:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:427:5
|
||||
|
|
||||
LL | unsafe {};
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -141,7 +165,7 @@ LL | unsafe {};
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:365:20
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:432:20
|
||||
|
|
||||
LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -149,7 +173,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:373:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:440:5
|
||||
|
|
||||
LL | unsafe impl A for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -157,7 +181,7 @@ LL | unsafe impl A for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:381:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:448:9
|
||||
|
|
||||
LL | unsafe impl B for (u32) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -165,7 +189,7 @@ LL | unsafe impl B for (u32) {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:470:13
|
||||
|
|
||||
LL | unsafe impl T for $t {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -177,7 +201,7 @@ LL | no_safety_comment!(());
|
|||
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:429:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:496:13
|
||||
|
|
||||
LL | unsafe impl T for $t {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -189,7 +213,7 @@ LL | no_safety_comment!(());
|
|||
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:439:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:506:5
|
||||
|
|
||||
LL | unsafe impl T for (i32) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -197,7 +221,7 @@ LL | unsafe impl T for (i32) {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:429:13
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:496:13
|
||||
|
|
||||
LL | unsafe impl T for $t {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -209,7 +233,7 @@ LL | no_safety_comment!(u32);
|
|||
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:446:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:513:5
|
||||
|
|
||||
LL | unsafe impl T for (bool) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -217,7 +241,7 @@ LL | unsafe impl T for (bool) {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:493:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:560:5
|
||||
|
|
||||
LL | unsafe impl NoComment for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -225,7 +249,7 @@ LL | unsafe impl NoComment for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:498:19
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:565:19
|
||||
|
|
||||
LL | /* SAFETY: */ unsafe impl InlineComment for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -233,7 +257,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:503:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:570:5
|
||||
|
|
||||
LL | unsafe impl TrailingComment for () {} // SAFETY:
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -241,13 +265,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY:
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: constant has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:508:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:575:5
|
||||
|
|
||||
LL | const BIG_NUMBER: i32 = 1000000;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:574:8
|
||||
|
|
||||
LL | // SAFETY:
|
||||
| ^^^^^^^
|
||||
|
|
@ -255,7 +279,7 @@ LL | // SAFETY:
|
|||
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]`
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:510:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:577:5
|
||||
|
|
||||
LL | unsafe impl Interference for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -263,7 +287,7 @@ LL | unsafe impl Interference for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:518:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:585:5
|
||||
|
|
||||
LL | unsafe impl ImplInFn for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -271,7 +295,7 @@ LL | unsafe impl ImplInFn for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe impl missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:528:1
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:595:1
|
||||
|
|
||||
LL | unsafe impl CrateRoot for () {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -279,7 +303,7 @@ LL | unsafe impl CrateRoot for () {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:539:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:606:9
|
||||
|
|
||||
LL | unsafe {};
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -287,7 +311,7 @@ LL | unsafe {};
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: statement has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:543:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:610:5
|
||||
|
|
||||
LL | / let _ = {
|
||||
LL | |
|
||||
|
|
@ -297,13 +321,13 @@ LL | | };
|
|||
| |______^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:609:8
|
||||
|
|
||||
LL | // SAFETY: this is more than one level away, so it should warn
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:545:12
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:612:12
|
||||
|
|
||||
LL | if unsafe { true } {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
@ -311,7 +335,7 @@ LL | if unsafe { true } {
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:549:23
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:616:23
|
||||
|
|
||||
LL | let bar = unsafe {};
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -319,7 +343,7 @@ LL | let bar = unsafe {};
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:568:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:635:9
|
||||
|
|
||||
LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -327,7 +351,7 @@ LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:573:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:640:9
|
||||
|
|
||||
LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -335,7 +359,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line()
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:578:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:645:9
|
||||
|
|
||||
LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -343,7 +367,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line()
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:585:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:652:5
|
||||
|
|
||||
LL | unsafe {}
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -351,7 +375,7 @@ LL | unsafe {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:590:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:5
|
||||
|
|
||||
LL | unsafe {
|
||||
| ^^^^^^^^
|
||||
|
|
@ -359,7 +383,7 @@ LL | unsafe {
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:598:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:665:9
|
||||
|
|
||||
LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -367,7 +391,7 @@ LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:604:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:671:9
|
||||
|
|
||||
LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -375,7 +399,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line()
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:611:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:678:9
|
||||
|
|
||||
LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -383,7 +407,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line()
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:617:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:684:5
|
||||
|
|
||||
LL | unsafe {}
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -391,7 +415,7 @@ LL | unsafe {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:705:52
|
||||
|
|
||||
LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 };
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -399,7 +423,7 @@ LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:714:41
|
||||
|
|
||||
LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 };
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -407,7 +431,7 @@ LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:724:42
|
||||
|
|
||||
LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 };
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -415,7 +439,7 @@ LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:729:40
|
||||
|
|
||||
LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 };
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -423,7 +447,7 @@ LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:673:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:740:9
|
||||
|
|
||||
LL | unsafe { here_is_another_variable_with_long_name };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -431,7 +455,7 @@ LL | unsafe { here_is_another_variable_with_long_name };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:702:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:769:9
|
||||
|
|
||||
LL | unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -439,19 +463,19 @@ LL | unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: statement has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:721:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:788:5
|
||||
|
|
||||
LL | _ = bar();
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:720:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:8
|
||||
|
|
||||
LL | // SAFETY: unnecessary_safety_comment triggers here
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:735:12
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:802:12
|
||||
|
|
||||
LL | return unsafe { h() };
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
@ -459,31 +483,31 @@ LL | return unsafe { h() };
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: module has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:741:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:808:5
|
||||
|
|
||||
LL | mod x {}
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:740:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:807:8
|
||||
|
|
||||
LL | // SAFETY: ...
|
||||
| ^^^^^^^
|
||||
|
||||
error: module has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:751:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:818:5
|
||||
|
|
||||
LL | mod z {}
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:750:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:817:8
|
||||
|
|
||||
LL | // SAFETY: ...
|
||||
| ^^^^^^^
|
||||
|
||||
error: unsafe block missing a safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:766:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:833:9
|
||||
|
|
||||
LL | unsafe {}
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -491,52 +515,52 @@ LL | unsafe {}
|
|||
= help: consider adding a safety comment on the preceding line
|
||||
|
||||
error: function has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:781:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:848:5
|
||||
|
|
||||
LL | unsafe fn unsafe_comment() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider changing the `safety` comment for a `# Safety` doc comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:780:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:847:8
|
||||
|
|
||||
LL | // SAFETY: Bla
|
||||
| ^^^^^^^
|
||||
|
||||
error: function has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:854:5
|
||||
|
|
||||
LL | unsafe fn unsafe_block_comment() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider changing the `safety` comment for a `# Safety` doc comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:785:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:852:8
|
||||
|
|
||||
LL | SAFETY: Bla
|
||||
| ^^^^^^^
|
||||
|
||||
error: function has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:791:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:858:5
|
||||
|
|
||||
LL | fn safe_comment() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:790:8
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:857:8
|
||||
|
|
||||
LL | // SAFETY: Bla
|
||||
| ^^^^^^^
|
||||
|
||||
error: function has unnecessary safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:795:5
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:862:5
|
||||
|
|
||||
LL | fn safe_doc_comment() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider removing the safety comment
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:794:9
|
||||
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:861:9
|
||||
|
|
||||
LL | /// SAFETY: Bla
|
||||
| ^^^^^^^
|
||||
|
||||
error: aborting due to 60 previous errors
|
||||
error: aborting due to 62 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -201,6 +201,66 @@ fn comment_macro_def() {
|
|||
t!();
|
||||
}
|
||||
|
||||
fn comment_macro_def_with_let() {
|
||||
macro_rules! t {
|
||||
() => {
|
||||
let _x =
|
||||
// SAFETY: here be exactly one dragon
|
||||
unsafe { 1 };
|
||||
};
|
||||
}
|
||||
|
||||
t!();
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn comment_macro_def_with_let_same_line() {
|
||||
macro_rules! t {
|
||||
() => {
|
||||
let _x =// SAFETY: same line comment
|
||||
unsafe { 1 };
|
||||
};
|
||||
}
|
||||
|
||||
t!();
|
||||
}
|
||||
|
||||
fn inner_comment_macro_def_with_let() {
|
||||
macro_rules! t {
|
||||
() => {
|
||||
let _x = unsafe {
|
||||
// SAFETY: inside the block
|
||||
1
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
t!();
|
||||
}
|
||||
|
||||
fn no_comment_macro_def_with_let() {
|
||||
macro_rules! t {
|
||||
() => {
|
||||
let _x = unsafe { 1 };
|
||||
//~^ undocumented_unsafe_blocks
|
||||
};
|
||||
}
|
||||
|
||||
t!();
|
||||
}
|
||||
|
||||
fn prefixed_safety_comment_macro_def_with_let() {
|
||||
macro_rules! t {
|
||||
() => {
|
||||
let _x =// not a SAFETY: comment, should lint
|
||||
unsafe { 1 };
|
||||
//~^ undocumented_unsafe_blocks
|
||||
};
|
||||
}
|
||||
|
||||
t!();
|
||||
}
|
||||
|
||||
fn non_ascii_comment() {
|
||||
// ॐ᧻໒ SaFeTy: ௵∰
|
||||
unsafe {};
|
||||
|
|
@ -263,6 +323,13 @@ fn in_closure(x: *const u32) {
|
|||
let _ = || unsafe { *x };
|
||||
}
|
||||
|
||||
fn inner_block_comment_block_style(x: *const u32) {
|
||||
let _ = unsafe {
|
||||
/* SAFETY: block comment inside */
|
||||
*x
|
||||
};
|
||||
}
|
||||
|
||||
// Invalid comments
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
|
|||
|
|
@ -174,7 +174,6 @@ pub fn hard_coded_allowed() {
|
|||
let _ = Saturating(0u32) + Saturating(0u32);
|
||||
let _ = String::new() + "";
|
||||
let _ = String::new() + &String::new();
|
||||
//~^ arithmetic_side_effects
|
||||
let _ = Wrapping(0u32) + Wrapping(0u32);
|
||||
|
||||
let saturating: Saturating<u32> = Saturating(0u32);
|
||||
|
|
|
|||
|
|
@ -14,784 +14,778 @@ LL | let _ = 1f128 + 1f128;
|
|||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:176:13
|
||||
|
|
||||
LL | let _ = String::new() + &String::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:312:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:311:5
|
||||
|
|
||||
LL | _n += 1;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:314:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:313:5
|
||||
|
|
||||
LL | _n += &1;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:316:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:315:5
|
||||
|
|
||||
LL | _n -= 1;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:318:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:317:5
|
||||
|
|
||||
LL | _n -= &1;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:320:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:319:5
|
||||
|
|
||||
LL | _n /= 0;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:322:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:321:5
|
||||
|
|
||||
LL | _n /= &0;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:324:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:323:5
|
||||
|
|
||||
LL | _n %= 0;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:326:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:325:5
|
||||
|
|
||||
LL | _n %= &0;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:328:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:327:5
|
||||
|
|
||||
LL | _n *= 2;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:330:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:329:5
|
||||
|
|
||||
LL | _n *= &2;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:332:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:331:5
|
||||
|
|
||||
LL | _n += -1;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:334:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:333:5
|
||||
|
|
||||
LL | _n += &-1;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:336:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:335:5
|
||||
|
|
||||
LL | _n -= -1;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:338:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:337:5
|
||||
|
|
||||
LL | _n -= &-1;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:340:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:339:5
|
||||
|
|
||||
LL | _n /= -0;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:342:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:341:5
|
||||
|
|
||||
LL | _n /= &-0;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:344:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:343:5
|
||||
|
|
||||
LL | _n %= -0;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:346:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:345:5
|
||||
|
|
||||
LL | _n %= &-0;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:348:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:347:5
|
||||
|
|
||||
LL | _n *= -2;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:350:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:349:5
|
||||
|
|
||||
LL | _n *= &-2;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:352:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:351:5
|
||||
|
|
||||
LL | _custom += Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:354:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:353:5
|
||||
|
|
||||
LL | _custom += &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:356:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:355:5
|
||||
|
|
||||
LL | _custom -= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:358:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:357:5
|
||||
|
|
||||
LL | _custom -= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:360:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:359:5
|
||||
|
|
||||
LL | _custom /= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:362:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:361:5
|
||||
|
|
||||
LL | _custom /= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:364:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:363:5
|
||||
|
|
||||
LL | _custom %= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:366:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:365:5
|
||||
|
|
||||
LL | _custom %= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:368:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:367:5
|
||||
|
|
||||
LL | _custom *= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:370:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:369:5
|
||||
|
|
||||
LL | _custom *= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:372:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:371:5
|
||||
|
|
||||
LL | _custom >>= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:374:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:373:5
|
||||
|
|
||||
LL | _custom >>= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:376:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:375:5
|
||||
|
|
||||
LL | _custom <<= Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:378:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:377:5
|
||||
|
|
||||
LL | _custom <<= &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:380:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:379:5
|
||||
|
|
||||
LL | _custom += -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:382:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:381:5
|
||||
|
|
||||
LL | _custom += &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:384:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:383:5
|
||||
|
|
||||
LL | _custom -= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:386:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:385:5
|
||||
|
|
||||
LL | _custom -= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:388:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:387:5
|
||||
|
|
||||
LL | _custom /= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:390:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:389:5
|
||||
|
|
||||
LL | _custom /= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:392:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:391:5
|
||||
|
|
||||
LL | _custom %= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:394:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:393:5
|
||||
|
|
||||
LL | _custom %= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:396:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:395:5
|
||||
|
|
||||
LL | _custom *= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:398:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:397:5
|
||||
|
|
||||
LL | _custom *= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:400:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:399:5
|
||||
|
|
||||
LL | _custom >>= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:402:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:401:5
|
||||
|
|
||||
LL | _custom >>= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:404:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:403:5
|
||||
|
|
||||
LL | _custom <<= -Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:406:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:405:5
|
||||
|
|
||||
LL | _custom <<= &-Custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:410:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:409:10
|
||||
|
|
||||
LL | _n = _n + 1;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:412:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:411:10
|
||||
|
|
||||
LL | _n = _n + &1;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:414:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:413:10
|
||||
|
|
||||
LL | _n = 1 + _n;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:416:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:415:10
|
||||
|
|
||||
LL | _n = &1 + _n;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:418:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:417:10
|
||||
|
|
||||
LL | _n = _n - 1;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:420:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:419:10
|
||||
|
|
||||
LL | _n = _n - &1;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:422:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:421:10
|
||||
|
|
||||
LL | _n = 1 - _n;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:424:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:423:10
|
||||
|
|
||||
LL | _n = &1 - _n;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:426:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:425:10
|
||||
|
|
||||
LL | _n = _n / 0;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:428:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:427:10
|
||||
|
|
||||
LL | _n = _n / &0;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:430:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:429:10
|
||||
|
|
||||
LL | _n = _n % 0;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:432:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:431:10
|
||||
|
|
||||
LL | _n = _n % &0;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:434:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:433:10
|
||||
|
|
||||
LL | _n = _n * 2;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:436:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:435:10
|
||||
|
|
||||
LL | _n = _n * &2;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:438:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:437:10
|
||||
|
|
||||
LL | _n = 2 * _n;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:440:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:439:10
|
||||
|
|
||||
LL | _n = &2 * _n;
|
||||
| ^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:442:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:441:10
|
||||
|
|
||||
LL | _n = 23 + &85;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:444:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:443:10
|
||||
|
|
||||
LL | _n = &23 + 85;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:446:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:445:10
|
||||
|
|
||||
LL | _n = &23 + &85;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:448:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:447:15
|
||||
|
|
||||
LL | _custom = _custom + _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:450:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:449:15
|
||||
|
|
||||
LL | _custom = _custom + &_custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:452:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:451:15
|
||||
|
|
||||
LL | _custom = Custom + _custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:454:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:453:15
|
||||
|
|
||||
LL | _custom = &Custom + _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:456:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:455:15
|
||||
|
|
||||
LL | _custom = _custom - Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:458:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:457:15
|
||||
|
|
||||
LL | _custom = _custom - &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:460:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:459:15
|
||||
|
|
||||
LL | _custom = Custom - _custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:462:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:461:15
|
||||
|
|
||||
LL | _custom = &Custom - _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:464:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:463:15
|
||||
|
|
||||
LL | _custom = _custom / Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:466:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:465:15
|
||||
|
|
||||
LL | _custom = _custom / &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:468:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:467:15
|
||||
|
|
||||
LL | _custom = _custom % Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:470:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:469:15
|
||||
|
|
||||
LL | _custom = _custom % &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:472:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:471:15
|
||||
|
|
||||
LL | _custom = _custom * Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:474:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:473:15
|
||||
|
|
||||
LL | _custom = _custom * &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:476:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:475:15
|
||||
|
|
||||
LL | _custom = Custom * _custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:478:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:477:15
|
||||
|
|
||||
LL | _custom = &Custom * _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:480:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:479:15
|
||||
|
|
||||
LL | _custom = Custom + &Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:482:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:481:15
|
||||
|
|
||||
LL | _custom = &Custom + Custom;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:484:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:483:15
|
||||
|
|
||||
LL | _custom = &Custom + &Custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:486:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:485:15
|
||||
|
|
||||
LL | _custom = _custom >> _custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:488:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:487:15
|
||||
|
|
||||
LL | _custom = _custom >> &_custom;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:490:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:489:15
|
||||
|
|
||||
LL | _custom = Custom << _custom;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:492:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:491:15
|
||||
|
|
||||
LL | _custom = &Custom << _custom;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:496:23
|
||||
--> tests/ui/arithmetic_side_effects.rs:495:23
|
||||
|
|
||||
LL | _n.saturating_div(0);
|
||||
| ^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:498:21
|
||||
--> tests/ui/arithmetic_side_effects.rs:497:21
|
||||
|
|
||||
LL | _n.wrapping_div(0);
|
||||
| ^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:500:21
|
||||
--> tests/ui/arithmetic_side_effects.rs:499:21
|
||||
|
|
||||
LL | _n.wrapping_rem(0);
|
||||
| ^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:502:28
|
||||
--> tests/ui/arithmetic_side_effects.rs:501:28
|
||||
|
|
||||
LL | _n.wrapping_rem_euclid(0);
|
||||
| ^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:505:23
|
||||
--> tests/ui/arithmetic_side_effects.rs:504:23
|
||||
|
|
||||
LL | _n.saturating_div(_n);
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:507:21
|
||||
--> tests/ui/arithmetic_side_effects.rs:506:21
|
||||
|
|
||||
LL | _n.wrapping_div(_n);
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:509:21
|
||||
--> tests/ui/arithmetic_side_effects.rs:508:21
|
||||
|
|
||||
LL | _n.wrapping_rem(_n);
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:511:28
|
||||
--> tests/ui/arithmetic_side_effects.rs:510:28
|
||||
|
|
||||
LL | _n.wrapping_rem_euclid(_n);
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:514:23
|
||||
--> tests/ui/arithmetic_side_effects.rs:513:23
|
||||
|
|
||||
LL | _n.saturating_div(*Box::new(_n));
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:518:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:517:10
|
||||
|
|
||||
LL | _n = -_n;
|
||||
| ^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:520:10
|
||||
--> tests/ui/arithmetic_side_effects.rs:519:10
|
||||
|
|
||||
LL | _n = -&_n;
|
||||
| ^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:522:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:521:15
|
||||
|
|
||||
LL | _custom = -_custom;
|
||||
| ^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:524:15
|
||||
--> tests/ui/arithmetic_side_effects.rs:523:15
|
||||
|
|
||||
LL | _custom = -&_custom;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:526:9
|
||||
--> tests/ui/arithmetic_side_effects.rs:525:9
|
||||
|
|
||||
LL | _ = -*Box::new(_n);
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:536:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:535:5
|
||||
|
|
||||
LL | 1 + i;
|
||||
| ^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:538:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:537:5
|
||||
|
|
||||
LL | i * 2;
|
||||
| ^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:540:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:539:5
|
||||
|
|
||||
LL | 1 % i / 2;
|
||||
| ^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:542:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:541:5
|
||||
|
|
||||
LL | i - 2 + 2 - i;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:544:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:543:5
|
||||
|
|
||||
LL | -i;
|
||||
| ^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:556:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:555:5
|
||||
|
|
||||
LL | i += 1;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:558:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:557:5
|
||||
|
|
||||
LL | i -= 1;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:560:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:559:5
|
||||
|
|
||||
LL | i *= 2;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:563:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:562:5
|
||||
|
|
||||
LL | i /= 0;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:566:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:565:5
|
||||
|
|
||||
LL | i /= var1;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:568:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:567:5
|
||||
|
|
||||
LL | i /= var2;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:571:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:570:5
|
||||
|
|
||||
LL | i %= 0;
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:574:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:573:5
|
||||
|
|
||||
LL | i %= var1;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:576:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:575:5
|
||||
|
|
||||
LL | i %= var2;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:587:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:586:5
|
||||
|
|
||||
LL | 10 / a
|
||||
| ^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:642:9
|
||||
--> tests/ui/arithmetic_side_effects.rs:641:9
|
||||
|
|
||||
LL | x / maybe_zero
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:647:9
|
||||
--> tests/ui/arithmetic_side_effects.rs:646:9
|
||||
|
|
||||
LL | x % maybe_zero
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:659:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:658:5
|
||||
|
|
||||
LL | one.add_assign(1);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:664:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:663:5
|
||||
|
|
||||
LL | one.sub_assign(1);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:685:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:684:5
|
||||
|
|
||||
LL | one.add(&one);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:687:5
|
||||
--> tests/ui/arithmetic_side_effects.rs:686:5
|
||||
|
|
||||
LL | Box::new(one).add(one);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:696:13
|
||||
--> tests/ui/arithmetic_side_effects.rs:695:13
|
||||
|
|
||||
LL | let _ = u128::MAX + u128::from(1u8);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:713:13
|
||||
--> tests/ui/arithmetic_side_effects.rs:712:13
|
||||
|
|
||||
LL | let _ = u128::MAX * u128::from(1u8);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:736:33
|
||||
--> tests/ui/arithmetic_side_effects.rs:735:33
|
||||
|
|
||||
LL | let _ = Duration::from_secs(86400 * Foo::from(1));
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: arithmetic operation that can potentially result in unexpected side-effects
|
||||
--> tests/ui/arithmetic_side_effects.rs:742:33
|
||||
--> tests/ui/arithmetic_side_effects.rs:741:33
|
||||
|
|
||||
LL | let _ = Duration::from_secs(86400 * shift(1));
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 132 previous errors
|
||||
error: aborting due to 131 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -472,3 +472,28 @@ mod issue14422 {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#[clippy::cognitive_complexity = "1"]
|
||||
mod attribute_stacking {
|
||||
fn bad() {
|
||||
//~^ cognitive_complexity
|
||||
if true {
|
||||
println!("a");
|
||||
}
|
||||
}
|
||||
|
||||
#[clippy::cognitive_complexity = "2"]
|
||||
fn ok() {
|
||||
if true {
|
||||
println!("a");
|
||||
}
|
||||
}
|
||||
|
||||
// should revert to cognitive_complexity = "1"
|
||||
fn bad_again() {
|
||||
//~^ cognitive_complexity
|
||||
if true {
|
||||
println!("a");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,5 +176,21 @@ LL | fn bar() {
|
|||
|
|
||||
= help: you could split it up into multiple smaller functions
|
||||
|
||||
error: aborting due to 22 previous errors
|
||||
error: the function has a cognitive complexity of (2/1)
|
||||
--> tests/ui/cognitive_complexity.rs:478:8
|
||||
|
|
||||
LL | fn bad() {
|
||||
| ^^^
|
||||
|
|
||||
= help: you could split it up into multiple smaller functions
|
||||
|
||||
error: the function has a cognitive complexity of (2/1)
|
||||
--> tests/ui/cognitive_complexity.rs:493:8
|
||||
|
|
||||
LL | fn bad_again() {
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: you could split it up into multiple smaller functions
|
||||
|
||||
error: aborting due to 24 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
//@check-pass
|
||||
//@ignore-target: apple
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[warn(clippy::main_recursion)]
|
||||
#[allow(unconditional_recursion)]
|
||||
#[rustc_main]
|
||||
fn a() {
|
||||
println!("Hello, World!");
|
||||
a();
|
||||
//~^ main_recursion
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
error: recursing into entrypoint `a`
|
||||
--> tests/ui/crate_level_checks/entrypoint_recursion.rs:7:5
|
||||
|
|
||||
LL | a();
|
||||
| ^
|
||||
|
|
||||
= help: consider using another function for this recursion
|
||||
= note: `-D clippy::main-recursion` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::main_recursion)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
//@check-pass
|
||||
//@compile-flags: -Cpanic=abort
|
||||
#![no_std]
|
||||
#[warn(clippy::main_recursion)]
|
||||
#[allow(unconditional_recursion)]
|
||||
fn main() {
|
||||
main();
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
|
@ -35,4 +35,20 @@ fn main() {
|
|||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
if x < y {
|
||||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
if x < y {
|
||||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
if x > y {
|
||||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
if x > y {
|
||||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,4 +35,20 @@ fn main() {
|
|||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
if x != y && x <= y {
|
||||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
if x <= y && x != y {
|
||||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
if x != y && x >= y {
|
||||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
if x >= y && x != y {
|
||||
//~^ double_comparisons
|
||||
// do something
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,5 +49,29 @@ error: this binary expression can be simplified
|
|||
LL | if x >= y && x <= y {
|
||||
| ^^^^^^^^^^^^^^^^ help: try: `x == y`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: this binary expression can be simplified
|
||||
--> tests/ui/double_comparison.rs:38:8
|
||||
|
|
||||
LL | if x != y && x <= y {
|
||||
| ^^^^^^^^^^^^^^^^ help: try: `x < y`
|
||||
|
||||
error: this binary expression can be simplified
|
||||
--> tests/ui/double_comparison.rs:42:8
|
||||
|
|
||||
LL | if x <= y && x != y {
|
||||
| ^^^^^^^^^^^^^^^^ help: try: `x < y`
|
||||
|
||||
error: this binary expression can be simplified
|
||||
--> tests/ui/double_comparison.rs:46:8
|
||||
|
|
||||
LL | if x != y && x >= y {
|
||||
| ^^^^^^^^^^^^^^^^ help: try: `x > y`
|
||||
|
||||
error: this binary expression can be simplified
|
||||
--> tests/ui/double_comparison.rs:50:8
|
||||
|
|
||||
LL | if x >= y && x != y {
|
||||
| ^^^^^^^^^^^^^^^^ help: try: `x > y`
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
|
|
|
|||
91
src/tools/clippy/tests/ui/duration_suboptimal_units.fixed
Normal file
91
src/tools/clippy/tests/ui/duration_suboptimal_units.fixed
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//@aux-build:proc_macros.rs
|
||||
#![warn(clippy::duration_suboptimal_units)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
const SIXTY: u64 = 60;
|
||||
|
||||
macro_rules! mac {
|
||||
(slow_rythm) => {
|
||||
3600
|
||||
};
|
||||
(duration) => {
|
||||
Duration::from_mins(5)
|
||||
//~^ duration_suboptimal_units
|
||||
};
|
||||
(arg => $e:expr) => {
|
||||
Duration::from_secs($e)
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let dur = Duration::from_secs(0);
|
||||
let dur = Duration::from_secs(42);
|
||||
let dur = Duration::from_hours(3);
|
||||
|
||||
let dur = Duration::from_mins(1);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_mins(3);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_mins(10);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_hours(24);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_secs(5);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_hours(13);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
// Constants are intentionally not resolved, as we don't want to recommend a literal value over
|
||||
// using constants.
|
||||
let dur = Duration::from_secs(SIXTY);
|
||||
// Technically it would be nice to use Duration::from_mins(SIXTY) here, but that is a follow-up
|
||||
let dur = Duration::from_secs(SIXTY * 60);
|
||||
|
||||
const {
|
||||
let dur = Duration::from_secs(0);
|
||||
let dur = Duration::from_secs(5);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_mins(3);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_hours(24);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
let dur = Duration::from_secs(SIXTY);
|
||||
}
|
||||
|
||||
// Qualified Durations must be kept
|
||||
std::time::Duration::from_mins(1);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
// We lint in normal macros
|
||||
assert_eq!(Duration::from_hours(1), Duration::from_mins(6));
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
// We lint in normal macros (marker is in macro itself)
|
||||
let dur = mac!(duration);
|
||||
|
||||
// We don't lint in macros if duration comes from outside
|
||||
let dur = mac!(arg => 3600);
|
||||
|
||||
// We don't lint in external macros
|
||||
let dur = proc_macros::external! { Duration::from_secs(3_600) };
|
||||
|
||||
// We don't lint values coming from macros
|
||||
let dur = Duration::from_secs(mac!(slow_rythm));
|
||||
}
|
||||
|
||||
mod my_duration {
|
||||
struct Duration {}
|
||||
|
||||
impl Duration {
|
||||
pub const fn from_secs(_secs: u64) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
// Only suggest the change for std::time::Duration, not for other Duration structs
|
||||
let dur = Duration::from_secs(60);
|
||||
}
|
||||
}
|
||||
91
src/tools/clippy/tests/ui/duration_suboptimal_units.rs
Normal file
91
src/tools/clippy/tests/ui/duration_suboptimal_units.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//@aux-build:proc_macros.rs
|
||||
#![warn(clippy::duration_suboptimal_units)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
const SIXTY: u64 = 60;
|
||||
|
||||
macro_rules! mac {
|
||||
(slow_rythm) => {
|
||||
3600
|
||||
};
|
||||
(duration) => {
|
||||
Duration::from_secs(300)
|
||||
//~^ duration_suboptimal_units
|
||||
};
|
||||
(arg => $e:expr) => {
|
||||
Duration::from_secs($e)
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let dur = Duration::from_secs(0);
|
||||
let dur = Duration::from_secs(42);
|
||||
let dur = Duration::from_hours(3);
|
||||
|
||||
let dur = Duration::from_secs(60);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_secs(180);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_secs(10 * 60);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_mins(24 * 60);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_millis(5_000);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_nanos(13 * 60 * 60 * 1_000 * 1_000 * 1_000);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
// Constants are intentionally not resolved, as we don't want to recommend a literal value over
|
||||
// using constants.
|
||||
let dur = Duration::from_secs(SIXTY);
|
||||
// Technically it would be nice to use Duration::from_mins(SIXTY) here, but that is a follow-up
|
||||
let dur = Duration::from_secs(SIXTY * 60);
|
||||
|
||||
const {
|
||||
let dur = Duration::from_secs(0);
|
||||
let dur = Duration::from_millis(5_000);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_secs(180);
|
||||
//~^ duration_suboptimal_units
|
||||
let dur = Duration::from_mins(24 * 60);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
let dur = Duration::from_secs(SIXTY);
|
||||
}
|
||||
|
||||
// Qualified Durations must be kept
|
||||
std::time::Duration::from_secs(60);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
// We lint in normal macros
|
||||
assert_eq!(Duration::from_secs(3_600), Duration::from_mins(6));
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
// We lint in normal macros (marker is in macro itself)
|
||||
let dur = mac!(duration);
|
||||
|
||||
// We don't lint in macros if duration comes from outside
|
||||
let dur = mac!(arg => 3600);
|
||||
|
||||
// We don't lint in external macros
|
||||
let dur = proc_macros::external! { Duration::from_secs(3_600) };
|
||||
|
||||
// We don't lint values coming from macros
|
||||
let dur = Duration::from_secs(mac!(slow_rythm));
|
||||
}
|
||||
|
||||
mod my_duration {
|
||||
struct Duration {}
|
||||
|
||||
impl Duration {
|
||||
pub const fn from_secs(_secs: u64) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
// Only suggest the change for std::time::Duration, not for other Duration structs
|
||||
let dur = Duration::from_secs(60);
|
||||
}
|
||||
}
|
||||
152
src/tools/clippy/tests/ui/duration_suboptimal_units.stderr
Normal file
152
src/tools/clippy/tests/ui/duration_suboptimal_units.stderr
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:26:15
|
||||
|
|
||||
LL | let dur = Duration::from_secs(60);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::duration-suboptimal-units` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::duration_suboptimal_units)]`
|
||||
help: try using from_mins
|
||||
|
|
||||
LL - let dur = Duration::from_secs(60);
|
||||
LL + let dur = Duration::from_mins(1);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:28:15
|
||||
|
|
||||
LL | let dur = Duration::from_secs(180);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_mins
|
||||
|
|
||||
LL - let dur = Duration::from_secs(180);
|
||||
LL + let dur = Duration::from_mins(3);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:30:15
|
||||
|
|
||||
LL | let dur = Duration::from_secs(10 * 60);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_mins
|
||||
|
|
||||
LL - let dur = Duration::from_secs(10 * 60);
|
||||
LL + let dur = Duration::from_mins(10);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:32:15
|
||||
|
|
||||
LL | let dur = Duration::from_mins(24 * 60);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_hours
|
||||
|
|
||||
LL - let dur = Duration::from_mins(24 * 60);
|
||||
LL + let dur = Duration::from_hours(24);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:34:15
|
||||
|
|
||||
LL | let dur = Duration::from_millis(5_000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_secs
|
||||
|
|
||||
LL - let dur = Duration::from_millis(5_000);
|
||||
LL + let dur = Duration::from_secs(5);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:36:15
|
||||
|
|
||||
LL | let dur = Duration::from_nanos(13 * 60 * 60 * 1_000 * 1_000 * 1_000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_hours
|
||||
|
|
||||
LL - let dur = Duration::from_nanos(13 * 60 * 60 * 1_000 * 1_000 * 1_000);
|
||||
LL + let dur = Duration::from_hours(13);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:47:19
|
||||
|
|
||||
LL | let dur = Duration::from_millis(5_000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_secs
|
||||
|
|
||||
LL - let dur = Duration::from_millis(5_000);
|
||||
LL + let dur = Duration::from_secs(5);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:49:19
|
||||
|
|
||||
LL | let dur = Duration::from_secs(180);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_mins
|
||||
|
|
||||
LL - let dur = Duration::from_secs(180);
|
||||
LL + let dur = Duration::from_mins(3);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:51:19
|
||||
|
|
||||
LL | let dur = Duration::from_mins(24 * 60);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_hours
|
||||
|
|
||||
LL - let dur = Duration::from_mins(24 * 60);
|
||||
LL + let dur = Duration::from_hours(24);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:58:5
|
||||
|
|
||||
LL | std::time::Duration::from_secs(60);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_mins
|
||||
|
|
||||
LL - std::time::Duration::from_secs(60);
|
||||
LL + std::time::Duration::from_mins(1);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:62:16
|
||||
|
|
||||
LL | assert_eq!(Duration::from_secs(3_600), Duration::from_mins(6));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_hours
|
||||
|
|
||||
LL - assert_eq!(Duration::from_secs(3_600), Duration::from_mins(6));
|
||||
LL + assert_eq!(Duration::from_hours(1), Duration::from_mins(6));
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units.rs:13:9
|
||||
|
|
||||
LL | Duration::from_secs(300)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | let dur = mac!(duration);
|
||||
| -------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: try using from_mins
|
||||
|
|
||||
LL - Duration::from_secs(300)
|
||||
LL + Duration::from_mins(5)
|
||||
|
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#![warn(clippy::duration_suboptimal_units)]
|
||||
// The duration_constructors feature enables `Duration::from_days` and `Duration::from_weeks`, so we
|
||||
// should suggest them
|
||||
#![feature(duration_constructors)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
let dur = Duration::from_mins(1);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
let dur = Duration::from_days(1);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
let dur = Duration::from_weeks(13);
|
||||
//~^ duration_suboptimal_units
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#![warn(clippy::duration_suboptimal_units)]
|
||||
// The duration_constructors feature enables `Duration::from_days` and `Duration::from_weeks`, so we
|
||||
// should suggest them
|
||||
#![feature(duration_constructors)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
let dur = Duration::from_secs(60);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
let dur = Duration::from_hours(24);
|
||||
//~^ duration_suboptimal_units
|
||||
|
||||
let dur = Duration::from_nanos(13 * 7 * 24 * 60 * 60 * 1_000 * 1_000 * 1_000);
|
||||
//~^ duration_suboptimal_units
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units_days_weeks.rs:9:15
|
||||
|
|
||||
LL | let dur = Duration::from_secs(60);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::duration-suboptimal-units` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::duration_suboptimal_units)]`
|
||||
help: try using from_mins
|
||||
|
|
||||
LL - let dur = Duration::from_secs(60);
|
||||
LL + let dur = Duration::from_mins(1);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units_days_weeks.rs:12:15
|
||||
|
|
||||
LL | let dur = Duration::from_hours(24);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_days
|
||||
|
|
||||
LL - let dur = Duration::from_hours(24);
|
||||
LL + let dur = Duration::from_days(1);
|
||||
|
|
||||
|
||||
error: constructing a `Duration` using a smaller unit when a larger unit would be more readable
|
||||
--> tests/ui/duration_suboptimal_units_days_weeks.rs:15:15
|
||||
|
|
||||
LL | let dur = Duration::from_nanos(13 * 7 * 24 * 60 * 60 * 1_000 * 1_000 * 1_000);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try using from_weeks
|
||||
|
|
||||
LL - let dur = Duration::from_nanos(13 * 7 * 24 * 60 * 60 * 1_000 * 1_000 * 1_000);
|
||||
LL + let dur = Duration::from_weeks(13);
|
||||
|
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue