mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-21 17:52:12 +03:00
Rollup merge of #156552 - flip1995:clippy-subtree-update, r=Manishearth
Clippy subtree update r? Manishearth `Cargo.lock` update due to patch version bump in `ui_test`. One day early, as I won't have access to my laptop from tomorrow till Sunday
This commit is contained in:
+2
-2
@@ -5948,9 +5948,9 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||
|
||||
[[package]]
|
||||
name = "ui_test"
|
||||
version = "0.30.4"
|
||||
version = "0.30.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ada249620d81f010b9a1472b63a5077ac7c722dd0f4bacf6528b313d0b8c15d8"
|
||||
checksum = "980133b75aa9a95dc94feaf629d92d22c1172186f1fa1266b91f5b91414cf9a5"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.11.5",
|
||||
"anyhow",
|
||||
|
||||
@@ -6805,6 +6805,7 @@ Released 2018-09-13
|
||||
[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
|
||||
[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
|
||||
[`inline_modules`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_modules
|
||||
[`inline_trait_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_trait_bounds
|
||||
[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
|
||||
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
|
||||
[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
|
||||
@@ -6885,6 +6886,7 @@ Released 2018-09-13
|
||||
[`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_clear`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clear
|
||||
[`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
|
||||
[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil
|
||||
@@ -7238,6 +7240,7 @@ Released 2018-09-13
|
||||
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
|
||||
[`sliced_string_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#sliced_string_as_bytes
|
||||
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
|
||||
[`some_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#some_filter
|
||||
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
|
||||
[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
|
||||
[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
|
||||
|
||||
@@ -34,7 +34,7 @@ anstream = "0.6.18"
|
||||
|
||||
[dev-dependencies]
|
||||
cargo_metadata = "0.23"
|
||||
ui_test = "0.30.2"
|
||||
ui_test = "0.30.5"
|
||||
regex = "1.5.5"
|
||||
serde = { version = "1.0.145", features = ["derive"] }
|
||||
serde_json = "1.0.122"
|
||||
|
||||
@@ -14,9 +14,9 @@ The different lint groups were defined in the [Clippy 1.0 RFC].
|
||||
|
||||
## Correctness
|
||||
|
||||
The `clippy::correctness` group is the only lint group in Clippy which lints are
|
||||
The `clippy::correctness` group is the only lint group in Clippy whose lints are
|
||||
deny-by-default and abort the compilation when triggered. This is for good
|
||||
reason: If you see a `correctness` lint, it means that your code is outright
|
||||
reason: if you see a `correctness` lint, it means that your code is outright
|
||||
wrong or useless, and you should try to fix it.
|
||||
|
||||
Lints in this category are carefully picked and should be free of false
|
||||
|
||||
@@ -297,8 +297,9 @@ fn negate(bin_op_kind: BinOpKind) -> Option<BinOpKind> {
|
||||
return Err("contains never type".to_owned());
|
||||
}
|
||||
|
||||
let ctxt = e.span.ctxt();
|
||||
for (n, expr) in self.terminals.iter().enumerate() {
|
||||
if eq_expr_value(self.cx, e, expr) {
|
||||
if eq_expr_value(self.cx, ctxt, e, expr) {
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
return Ok(Bool::Term(n as u8));
|
||||
}
|
||||
@@ -307,8 +308,8 @@ fn negate(bin_op_kind: BinOpKind) -> Option<BinOpKind> {
|
||||
&& implements_ord(self.cx, e_lhs)
|
||||
&& let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind
|
||||
&& negate(e_binop.node) == Some(expr_binop.node)
|
||||
&& eq_expr_value(self.cx, e_lhs, expr_lhs)
|
||||
&& eq_expr_value(self.cx, e_rhs, expr_rhs)
|
||||
&& eq_expr_value(self.cx, ctxt, e_lhs, expr_lhs)
|
||||
&& eq_expr_value(self.cx, ctxt, e_rhs, expr_rhs)
|
||||
{
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::{Symbol, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -62,7 +62,8 @@ fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
&& !item.span.in_external_macro(cx.sess().source_map())
|
||||
&& let ctxt = item.span.ctxt()
|
||||
&& !ctxt.in_external_macro(cx.sess().source_map())
|
||||
&& !is_in_const_context(cx)
|
||||
&& let Some(cv) = match op2 {
|
||||
// todo: check for case signed -> larger unsigned == only x >= 0
|
||||
@@ -71,7 +72,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
let upper_lower = |lt1, gt1, lt2, gt2| {
|
||||
check_upper_bound(lt1, gt1)
|
||||
.zip(check_lower_bound(lt2, gt2))
|
||||
.and_then(|(l, r)| l.combine(r, cx))
|
||||
.and_then(|(l, r)| l.combine(r, cx, ctxt))
|
||||
};
|
||||
upper_lower(lt1, gt1, lt2, gt2).or_else(|| upper_lower(lt2, gt2, lt1, gt1))
|
||||
},
|
||||
@@ -126,8 +127,8 @@ fn read_le_ge<'tcx>(
|
||||
|
||||
impl<'a> Conversion<'a> {
|
||||
/// Combine multiple conversions if the are compatible
|
||||
pub fn combine(self, other: Self, cx: &LateContext<'_>) -> Option<Conversion<'a>> {
|
||||
if self.is_compatible(&other, cx) {
|
||||
pub fn combine(self, other: Self, cx: &LateContext<'_>, ctxt: SyntaxContext) -> Option<Conversion<'a>> {
|
||||
if self.is_compatible(&other, cx, ctxt) {
|
||||
// Prefer a Conversion that contains a type-constraint
|
||||
Some(if self.to_type.is_some() { self } else { other })
|
||||
} else {
|
||||
@@ -137,9 +138,9 @@ pub fn combine(self, other: Self, cx: &LateContext<'_>) -> Option<Conversion<'a>
|
||||
|
||||
/// Checks if two conversions are compatible
|
||||
/// same type of conversion, same 'castee' and same 'to type'
|
||||
pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_>) -> bool {
|
||||
pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_>, ctxt: SyntaxContext) -> bool {
|
||||
(self.cvt == other.cvt)
|
||||
&& (SpanlessEq::new(cx).eq_expr(self.expr_to_cast, other.expr_to_cast))
|
||||
&& (SpanlessEq::new(cx).eq_expr(ctxt, self.expr_to_cast, other.expr_to_cast))
|
||||
&& (self.has_compatible_to_type(other))
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{SyntaxContext, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -90,8 +90,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
|
||||
// Check that both sets of operands are equal
|
||||
let mut spanless_eq = SpanlessEq::new(cx);
|
||||
let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2);
|
||||
let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2);
|
||||
let same_fixed_operands = spanless_eq.eq_expr(SyntaxContext::root(), lhs1, lhs2)
|
||||
&& spanless_eq.eq_expr(SyntaxContext::root(), rhs1, rhs2);
|
||||
let same_transposed_operands = spanless_eq.eq_expr(SyntaxContext::root(), lhs1, rhs2)
|
||||
&& spanless_eq.eq_expr(SyntaxContext::root(), rhs1, lhs2);
|
||||
|
||||
if !same_fixed_operands && !same_transposed_operands {
|
||||
return;
|
||||
|
||||
@@ -232,6 +232,7 @@
|
||||
crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO,
|
||||
crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO,
|
||||
crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO,
|
||||
crate::inline_trait_bounds::INLINE_TRAIT_BOUNDS_INFO,
|
||||
crate::int_plus_one::INT_PLUS_ONE_INFO,
|
||||
crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO,
|
||||
crate::item_name_repetitions::MODULE_INCEPTION_INFO,
|
||||
@@ -414,6 +415,7 @@
|
||||
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
|
||||
crate::methods::LINES_FILTER_MAP_OK_INFO,
|
||||
crate::methods::MANUAL_C_STR_LITERALS_INFO,
|
||||
crate::methods::MANUAL_CLEAR_INFO,
|
||||
crate::methods::MANUAL_CONTAINS_INFO,
|
||||
crate::methods::MANUAL_FILTER_MAP_INFO,
|
||||
crate::methods::MANUAL_FIND_MAP_INFO,
|
||||
@@ -474,6 +476,7 @@
|
||||
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
|
||||
crate::methods::SKIP_WHILE_NEXT_INFO,
|
||||
crate::methods::SLICED_STRING_AS_BYTES_INFO,
|
||||
crate::methods::SOME_FILTER_INFO,
|
||||
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
|
||||
crate::methods::STR_SPLIT_AT_NEWLINE_INFO,
|
||||
crate::methods::STRING_EXTEND_CHARS_INFO,
|
||||
|
||||
@@ -498,11 +498,11 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
}
|
||||
|
||||
match try_parse_insert(self.cx, expr) {
|
||||
Some(insert_expr) if self.spanless_eq.eq_expr(self.map, insert_expr.map) => {
|
||||
Some(insert_expr) if self.spanless_eq.eq_expr(self.ctxt, self.map, insert_expr.map) => {
|
||||
self.visit_insert_expr_arguments(&insert_expr);
|
||||
// Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
|
||||
if self.is_map_used
|
||||
|| !self.spanless_eq.eq_expr(self.key, insert_expr.key)
|
||||
|| !self.spanless_eq.eq_expr(self.ctxt, self.key, insert_expr.key)
|
||||
|| expr.span.ctxt() != self.ctxt
|
||||
{
|
||||
self.can_use_entry = false;
|
||||
@@ -521,10 +521,10 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
self.visit_non_tail_expr(insert_expr.value);
|
||||
self.is_single_insert = is_single_insert;
|
||||
},
|
||||
_ if is_any_expr_in_map_used(self.cx, &mut self.spanless_eq, self.map, expr) => {
|
||||
_ if is_any_expr_in_map_used(self.cx, &mut self.spanless_eq, self.ctxt, self.map, expr) => {
|
||||
self.is_map_used = true;
|
||||
},
|
||||
_ if self.spanless_eq.eq_expr(self.key, expr) => {
|
||||
_ if self.spanless_eq.eq_expr(self.ctxt, self.key, expr) => {
|
||||
self.is_key_used = true;
|
||||
},
|
||||
_ => match expr.kind {
|
||||
@@ -600,11 +600,12 @@ fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
|
||||
fn is_any_expr_in_map_used<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
spanless_eq: &mut SpanlessEq<'_, 'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
map: &'tcx Expr<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
) -> bool {
|
||||
for_each_expr(cx, map, |e| {
|
||||
if spanless_eq.eq_expr(e, expr) {
|
||||
if spanless_eq.eq_expr(ctxt, e, expr) {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
|
||||
@@ -14,11 +14,15 @@
|
||||
/// 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 {
|
||||
fn is_testing_positive(cx: &LateContext<'_>, ctxt: SyntaxContext, 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),
|
||||
BinOpKind::Gt | BinOpKind::Ge => {
|
||||
is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, ctxt, left, test)
|
||||
},
|
||||
BinOpKind::Lt | BinOpKind::Le => {
|
||||
is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, ctxt, right, test)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
@@ -27,11 +31,15 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
|
||||
}
|
||||
|
||||
/// See [`is_testing_positive`]
|
||||
fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
|
||||
fn is_testing_negative(cx: &LateContext<'_>, ctxt: SyntaxContext, 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),
|
||||
BinOpKind::Gt | BinOpKind::Ge => {
|
||||
is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, ctxt, right, test)
|
||||
},
|
||||
BinOpKind::Lt | BinOpKind::Le => {
|
||||
is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, ctxt, left, test)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
@@ -55,14 +63,21 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool {
|
||||
/// 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>)> {
|
||||
fn are_negated<'a>(
|
||||
cx: &LateContext<'_>,
|
||||
ctxt: SyntaxContext,
|
||||
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)
|
||||
&& expr1_negated.span.ctxt() == ctxt
|
||||
&& eq_expr_value(cx, ctxt, expr1_negated, expr2)
|
||||
{
|
||||
return Some((false, expr2));
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Neg, expr2_negated) = expr2.kind
|
||||
&& eq_expr_value(cx, expr1, expr2_negated)
|
||||
&& expr2_negated.span.ctxt() == ctxt
|
||||
&& eq_expr_value(cx, ctxt, expr1, expr2_negated)
|
||||
{
|
||||
return Some((true, expr1));
|
||||
}
|
||||
@@ -77,11 +92,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
}) = 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 ctxt = expr.span.ctxt()
|
||||
&& let Some((if_expr_positive, body)) = are_negated(cx, ctxt, if_body_expr, else_body_expr)
|
||||
{
|
||||
let sugg_positive_abs = if is_testing_positive(cx, cond, body) {
|
||||
let sugg_positive_abs = if is_testing_positive(cx, ctxt, cond, body) {
|
||||
if_expr_positive
|
||||
} else if is_testing_negative(cx, cond, body) {
|
||||
} else if is_testing_negative(cx, ctxt, cond, body) {
|
||||
!if_expr_positive
|
||||
} else {
|
||||
return;
|
||||
|
||||
@@ -19,6 +19,7 @@ pub(super) fn detect(cx: &LateContext<'_>, receiver: &Expr<'_>, app: &mut Applic
|
||||
add_rhs,
|
||||
) = receiver.kind
|
||||
{
|
||||
let ctxt = receiver.span.ctxt();
|
||||
// check if expression of the form x * x + y * y
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
@@ -34,8 +35,8 @@ pub(super) fn detect(cx: &LateContext<'_>, receiver: &Expr<'_>, app: &mut Applic
|
||||
rmul_lhs,
|
||||
rmul_rhs,
|
||||
) = add_rhs.kind
|
||||
&& eq_expr_value(cx, lmul_lhs, lmul_rhs)
|
||||
&& eq_expr_value(cx, rmul_lhs, rmul_rhs)
|
||||
&& eq_expr_value(cx, ctxt, lmul_lhs, lmul_rhs)
|
||||
&& eq_expr_value(cx, ctxt, rmul_lhs, rmul_rhs)
|
||||
{
|
||||
return Some(format!(
|
||||
"{}.hypot({})",
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Spanned;
|
||||
use rustc_span::{Spanned, SyntaxContext};
|
||||
|
||||
use super::SUBOPTIMAL_FLOPS;
|
||||
|
||||
fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
|
||||
fn are_same_base_logs(cx: &LateContext<'_>, ctxt: SyntaxContext, 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]));
|
||||
|| method_a.name == sym::log && args_a.len() == 1 && eq_expr_value(cx, ctxt, &args_a[0], &args_b[0]));
|
||||
}
|
||||
|
||||
false
|
||||
@@ -30,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
lhs,
|
||||
rhs,
|
||||
) = expr.kind
|
||||
&& are_same_base_logs(cx, lhs, rhs)
|
||||
&& are_same_base_logs(cx, expr.span.ctxt(), lhs, rhs)
|
||||
&& let ExprKind::MethodCall(_, largs_self, ..) = lhs.kind
|
||||
&& let ExprKind::MethodCall(_, rargs_self, ..) = rhs.kind
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::SyntaxContext;
|
||||
use rustc_span::edition::Edition::Edition2024;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -61,9 +62,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if_else: Some(if_else),
|
||||
..
|
||||
}) = higher::IfLet::hir(cx, expr)
|
||||
&& let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, e, None))
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, ctxt, e, None))
|
||||
&& let Some(arm_mutex) =
|
||||
for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, e, Some(op_mutex)))
|
||||
for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, ctxt, e, Some(op_mutex)))
|
||||
{
|
||||
let diag = |diag: &mut Diag<'_, ()>| {
|
||||
diag.span_label(
|
||||
@@ -89,6 +91,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
|
||||
fn mutex_lock_call<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op_mutex: Option<&'tcx Expr<'_>>,
|
||||
) -> ControlFlow<&'tcx Expr<'tcx>> {
|
||||
@@ -96,7 +99,7 @@ fn mutex_lock_call<'tcx>(
|
||||
&& path.ident.name == sym::lock
|
||||
&& let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
|
||||
&& ty.is_diag_item(cx, sym::Mutex)
|
||||
&& op_mutex.is_none_or(|op| eq_expr_value(cx, self_arg, op))
|
||||
&& op_mutex.is_none_or(|op| eq_expr_value(cx, ctxt, self_arg, op))
|
||||
{
|
||||
ControlFlow::Break(self_arg)
|
||||
} else {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::hygiene::walk_chain;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_span::{Span, Symbol, SyntaxContext};
|
||||
|
||||
use super::BRANCHES_SHARING_CODE;
|
||||
|
||||
@@ -196,7 +196,12 @@ fn eq_stmts(
|
||||
.all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(cx, s, new_bindings)))
|
||||
} else {
|
||||
true
|
||||
}) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt)))
|
||||
}) && blocks.iter().all(|b| {
|
||||
get_stmt(b).is_some_and(|s| {
|
||||
eq.set_eval_ctxt(SyntaxContext::root());
|
||||
eq.eq_stmt(s, stmt)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
@@ -207,7 +212,7 @@ fn scan_block_for_eq<'tcx>(
|
||||
blocks: &[&'tcx Block<'_>],
|
||||
) -> BlockEq {
|
||||
let mut eq = SpanlessEq::new(cx);
|
||||
let mut eq = eq.inter_expr();
|
||||
let mut eq = eq.inter_expr(SyntaxContext::root());
|
||||
let mut moved_locals = Vec::new();
|
||||
|
||||
let mut cond_locals = HirIdSet::default();
|
||||
@@ -334,6 +339,7 @@ fn scan_block_for_eq<'tcx>(
|
||||
});
|
||||
if let Some(e) = block.expr {
|
||||
for block in blocks {
|
||||
eq.set_eval_ctxt(SyntaxContext::root());
|
||||
if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) {
|
||||
moved_locals.truncate(moved_locals_at_start);
|
||||
return BlockEq {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use clippy_utils::higher::has_let_expr;
|
||||
use rustc_hir::{Block, Expr};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::SyntaxContext;
|
||||
|
||||
use super::IF_SAME_THEN_ELSE;
|
||||
|
||||
@@ -12,7 +13,10 @@ pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<
|
||||
.array_windows::<2>()
|
||||
.enumerate()
|
||||
.fold(true, |all_eq, (i, &[lhs, rhs])| {
|
||||
if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) {
|
||||
if eq.eq_block(SyntaxContext::root(), lhs, rhs)
|
||||
&& !has_let_expr(conds[i])
|
||||
&& conds.get(i + 1).is_none_or(|e| !has_let_expr(e))
|
||||
{
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
IF_SAME_THEN_ELSE,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, search_same};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::SyntaxContext;
|
||||
|
||||
use super::IFS_SAME_COND;
|
||||
|
||||
@@ -34,10 +35,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_
|
||||
if method_caller_is_mutable(cx, caller, interior_mut) {
|
||||
false
|
||||
} else {
|
||||
SpanlessEq::new(cx).eq_expr(lhs, rhs)
|
||||
SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), lhs, rhs)
|
||||
}
|
||||
} else {
|
||||
eq_expr_value(cx, lhs, rhs)
|
||||
eq_expr_value(cx, SyntaxContext::root(), lhs, rhs)
|
||||
}
|
||||
},
|
||||
) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
use clippy_utils::{SpanlessEq, eq_expr_value, hash_expr, search_same};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::SyntaxContext;
|
||||
|
||||
use super::SAME_FUNCTIONS_IN_IF_CONDITION;
|
||||
|
||||
@@ -13,10 +14,10 @@ pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
|
||||
return false;
|
||||
}
|
||||
// Do not spawn warning if `IFS_SAME_COND` already produced it.
|
||||
if eq_expr_value(cx, lhs, rhs) {
|
||||
if eq_expr_value(cx, SyntaxContext::root(), lhs, rhs) {
|
||||
return false;
|
||||
}
|
||||
SpanlessEq::new(cx).eq_expr(lhs, rhs)
|
||||
SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), lhs, rhs)
|
||||
};
|
||||
|
||||
for group in search_same(conds, |e| hash_expr(cx, e), eq) {
|
||||
|
||||
@@ -67,7 +67,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& ex.span.ctxt() == ctxt
|
||||
&& cond.span.ctxt() == ctxt
|
||||
&& clippy_utils::SpanlessEq::new(cx).eq_expr(l, target)
|
||||
&& clippy_utils::SpanlessEq::new(cx).eq_expr(ctxt, l, target)
|
||||
&& AssignOpKind::AddAssign == op1.node
|
||||
&& let ExprKind::Lit(lit) = value.kind
|
||||
&& let LitKind::Int(Pu128(1), LitIntType::Unsuffixed) = lit.node
|
||||
|
||||
@@ -184,7 +184,7 @@ fn check_gt(
|
||||
msrv: Msrv,
|
||||
is_composited: bool,
|
||||
) {
|
||||
if is_side_effect_free(cx, big_expr) && is_side_effect_free(cx, little_expr) {
|
||||
if !big_expr.can_have_side_effects() && !little_expr.can_have_side_effects() {
|
||||
check_subtraction(
|
||||
cx,
|
||||
condition_span,
|
||||
@@ -199,10 +199,6 @@ fn check_gt(
|
||||
}
|
||||
}
|
||||
|
||||
fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
eq_expr_value(cx, expr, expr)
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn check_subtraction(
|
||||
cx: &LateContext<'_>,
|
||||
@@ -242,7 +238,8 @@ fn check_subtraction(
|
||||
&& let ExprKind::Binary(op, left, right) = if_block.kind
|
||||
&& let BinOpKind::Sub = op.node
|
||||
{
|
||||
if eq_expr_value(cx, left, big_expr) && eq_expr_value(cx, right, little_expr) {
|
||||
let ctxt = expr_span.ctxt();
|
||||
if eq_expr_value(cx, ctxt, left, big_expr) && eq_expr_value(cx, ctxt, right, little_expr) {
|
||||
// This part of the condition is voluntarily split from the one before to ensure that
|
||||
// if `snippet_opt` fails, it won't try the next conditions.
|
||||
if !is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST) {
|
||||
@@ -277,8 +274,8 @@ fn check_subtraction(
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if eq_expr_value(cx, left, little_expr)
|
||||
&& eq_expr_value(cx, right, big_expr)
|
||||
} else if eq_expr_value(cx, ctxt, left, little_expr)
|
||||
&& eq_expr_value(cx, ctxt, right, big_expr)
|
||||
&& let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr)
|
||||
&& let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr)
|
||||
{
|
||||
@@ -322,14 +319,15 @@ fn check_with_condition<'tcx>(
|
||||
// Extracting out the variable name
|
||||
&& let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind
|
||||
{
|
||||
let ctxt = expr.span.ctxt();
|
||||
// Handle symmetric conditions in the if statement
|
||||
let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
|
||||
let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(ctxt, cond_left, target) {
|
||||
if BinOpKind::Gt == cond_op || BinOpKind::Ne == cond_op {
|
||||
(cond_left, cond_right)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
|
||||
} else if SpanlessEq::new(cx).eq_expr(ctxt, cond_right, target) {
|
||||
if BinOpKind::Lt == cond_op || BinOpKind::Ne == cond_op {
|
||||
(cond_right, cond_left)
|
||||
} else {
|
||||
@@ -399,7 +397,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp
|
||||
ExprKind::Assign(target, value, _) => {
|
||||
if let ExprKind::Binary(ref op1, left1, right1) = value.kind
|
||||
&& BinOpKind::Sub == op1.node
|
||||
&& SpanlessEq::new(cx).eq_expr(left1, target)
|
||||
&& SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), left1, target)
|
||||
&& is_integer_literal(right1, 1)
|
||||
{
|
||||
Some(target)
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{HasSession, snippet};
|
||||
use rustc_ast::NodeId;
|
||||
use rustc_ast::ast::{Fn, FnRetTy, GenericParam, GenericParamKind};
|
||||
use rustc_ast::visit::{FnCtxt, FnKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Enforce that `where` bounds are used for all trait bounds.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Enforce a single style throughout a codebase.
|
||||
/// Avoid uncertainty about whether a bound should be inline
|
||||
/// or out-of-line (i.e. a where bound).
|
||||
/// Avoid complex inline bounds, which could make a function declaration more difficult to read.
|
||||
///
|
||||
/// ### Known limitations
|
||||
/// Only lints functions and method declararions. Bounds on structs, enums,
|
||||
/// and impl blocks are not yet covered.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn foo<T: Clone>() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn foo<T>() where T: Clone {}
|
||||
/// ```
|
||||
#[clippy::version = "1.97.0"]
|
||||
pub INLINE_TRAIT_BOUNDS,
|
||||
restriction,
|
||||
"enforce that `where` bounds are used for all trait bounds"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InlineTraitBounds => [INLINE_TRAIT_BOUNDS]);
|
||||
|
||||
impl EarlyLintPass for InlineTraitBounds {
|
||||
fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) {
|
||||
let FnKind::Fn(ctxt, _vis, f) = kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Skip foreign functions (extern "C" etc.)
|
||||
if !matches!(ctxt, FnCtxt::Free | FnCtxt::Assoc(..)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if f.sig.span.in_external_macro(cx.sess().source_map()) {
|
||||
return;
|
||||
}
|
||||
|
||||
lint_fn(cx, f);
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_fn(cx: &EarlyContext<'_>, f: &Fn) {
|
||||
let generics = &f.generics;
|
||||
let offenders: Vec<&GenericParam> = generics
|
||||
.params
|
||||
.iter()
|
||||
.filter(|param| {
|
||||
!param.bounds.is_empty() && matches!(param.kind, GenericParamKind::Lifetime | GenericParamKind::Type { .. })
|
||||
})
|
||||
.collect();
|
||||
if offenders.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let predicates = offenders
|
||||
.iter()
|
||||
.map(|param| build_predicate_text(cx, param))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut edits = Vec::new();
|
||||
|
||||
for param in offenders {
|
||||
if let Some(colon) = param.colon_span {
|
||||
let remove_span = colon.to(param.bounds.last().unwrap().span());
|
||||
|
||||
edits.push((remove_span, String::new()));
|
||||
}
|
||||
}
|
||||
|
||||
let predicate_text = predicates.join(", ");
|
||||
|
||||
let where_clause = &generics.where_clause;
|
||||
if where_clause.has_where_token {
|
||||
let (insert_at, suffix) = if let Some(last_pred) = where_clause.predicates.last() {
|
||||
// existing `where` with predicates: append after last predicate
|
||||
(last_pred.span.shrink_to_hi(), format!(", {predicate_text}"))
|
||||
} else {
|
||||
// `where` token present but empty predicate list
|
||||
(where_clause.span.shrink_to_hi(), format!(" {predicate_text}"))
|
||||
};
|
||||
|
||||
edits.push((insert_at, suffix));
|
||||
} else {
|
||||
let insert_at = match &f.sig.decl.output {
|
||||
FnRetTy::Default(span) => span.shrink_to_lo(),
|
||||
FnRetTy::Ty(ty) => ty.span.shrink_to_hi(),
|
||||
};
|
||||
edits.push((insert_at, format!(" where {predicate_text}")));
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
INLINE_TRAIT_BOUNDS,
|
||||
generics.span,
|
||||
"inline trait bounds used",
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"move bounds to a `where` clause",
|
||||
edits,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn build_predicate_text(cx: &EarlyContext<'_>, param: &GenericParam) -> String {
|
||||
// bounds is guaranteed non-empty by the filter in `lint_fn`
|
||||
let first = param.bounds.first().unwrap();
|
||||
let last = param.bounds.last().unwrap();
|
||||
|
||||
let bounds_span = first.span().to(last.span());
|
||||
|
||||
let lhs = snippet(cx, param.ident.span, "..");
|
||||
|
||||
let rhs = snippet(cx, bounds_span, "..");
|
||||
|
||||
format!("{lhs}: {rhs}")
|
||||
}
|
||||
@@ -168,6 +168,7 @@
|
||||
mod inherent_to_string;
|
||||
mod init_numbered_fields;
|
||||
mod inline_fn_without_body;
|
||||
mod inline_trait_bounds;
|
||||
mod int_plus_one;
|
||||
mod item_name_repetitions;
|
||||
mod items_after_statements;
|
||||
@@ -518,6 +519,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
||||
Box::new(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)),
|
||||
Box::new(|| Box::new(cfg_not_test::CfgNotTest)),
|
||||
Box::new(|| Box::new(empty_line_after::EmptyLineAfter::new())),
|
||||
Box::new(|| Box::new(inline_trait_bounds::InlineTraitBounds)),
|
||||
// add early passes here, used by `cargo dev new_lint`
|
||||
];
|
||||
store.early_passes.extend(early_lints);
|
||||
|
||||
@@ -89,13 +89,13 @@ fn check_index_usage<'tcx>(
|
||||
// `Index` directly and no deref to `str` would happen in that case).
|
||||
if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_str()
|
||||
&& BYTE_INDEX_METHODS.contains(&segment.ident.name)
|
||||
&& eq_expr_value(cx, chars_recv, recv) =>
|
||||
&& eq_expr_value(cx, expr.span.ctxt(), chars_recv, recv) =>
|
||||
{
|
||||
"passing a character position to a method that expects a byte index"
|
||||
},
|
||||
ExprKind::Index(target, ..)
|
||||
if is_string_like(cx.typeck_results().expr_ty_adjusted(target).peel_refs())
|
||||
&& eq_expr_value(cx, chars_recv, target) =>
|
||||
&& eq_expr_value(cx, expr.span.ctxt(), chars_recv, target) =>
|
||||
{
|
||||
"indexing into a string with a character position where a byte index is expected"
|
||||
},
|
||||
|
||||
@@ -65,7 +65,7 @@ fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr
|
||||
&& let ExprKind::MethodCall(_, pop_recv, ..) = unwrap_recv.kind
|
||||
{
|
||||
// make sure they're the same `Vec`
|
||||
SpanlessEq::new(cx).eq_expr(pop_recv, is_empty_recv)
|
||||
SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), pop_recv, is_empty_recv)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -101,8 +101,9 @@ pub(super) fn check<'tcx>(
|
||||
if let ExprKind::Binary(ref op, left, right) = end.kind
|
||||
&& op.node == BinOpKind::Add
|
||||
{
|
||||
let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
|
||||
let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
|
||||
let ctxt = start.span.ctxt();
|
||||
let start_equal_left = SpanlessEq::new(cx).eq_expr(ctxt, start, left);
|
||||
let start_equal_right = SpanlessEq::new(cx).eq_expr(ctxt, start, right);
|
||||
|
||||
if start_equal_left {
|
||||
take_expr = right;
|
||||
|
||||
@@ -121,12 +121,12 @@ fn is_sub_expr(
|
||||
expected_b: &Expr<'_>,
|
||||
expected_ty: Ty<'_>,
|
||||
) -> bool {
|
||||
let expr = peel_blocks(expr).kind;
|
||||
let expr = peel_blocks(expr);
|
||||
|
||||
if let ty::Int(ty) = expected_ty.kind() {
|
||||
let unsigned = Ty::new_uint(cx.tcx, ty.to_unsigned());
|
||||
|
||||
return if let ExprKind::Cast(expr, cast_ty) = expr
|
||||
return if let ExprKind::Cast(expr, cast_ty) = expr.kind
|
||||
&& cx.typeck_results().node_type(cast_ty.hir_id) == unsigned
|
||||
{
|
||||
is_sub_expr(cx, expr, expected_a, expected_b, unsigned)
|
||||
@@ -135,10 +135,11 @@ fn is_sub_expr(
|
||||
};
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(op, a, b) = expr
|
||||
let ctxt = expr.span.ctxt();
|
||||
if let ExprKind::Binary(op, a, b) = expr.kind
|
||||
&& let BinOpKind::Sub = op.node
|
||||
&& eq_expr_value(cx, a, expected_a)
|
||||
&& eq_expr_value(cx, b, expected_b)
|
||||
&& eq_expr_value(cx, ctxt, a, expected_a)
|
||||
&& eq_expr_value(cx, ctxt, b, expected_b)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::SyntaxContext;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -60,7 +61,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
&& 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) {
|
||||
if !eq.eq_expr(SyntaxContext::root(), divisor, divisor) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
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)
|
||||
&& eq.eq_expr(SyntaxContext::root(), rhs, divisor)
|
||||
&& is_unsigned_integer(cx, lhs)
|
||||
{
|
||||
match first_use {
|
||||
@@ -84,7 +85,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
ControlFlow::<(), _>::Continue(Descend::No)
|
||||
} else if let ExprKind::AssignOp(op, lhs, rhs) = e.kind
|
||||
&& op.node == AssignOpKind::DivAssign
|
||||
&& eq.eq_expr(rhs, divisor)
|
||||
&& eq.eq_expr(SyntaxContext::root(), rhs, divisor)
|
||||
&& is_unsigned_integer(cx, lhs)
|
||||
{
|
||||
match first_use {
|
||||
@@ -96,7 +97,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
division_spans.push(e.span);
|
||||
|
||||
ControlFlow::<(), _>::Continue(Descend::No)
|
||||
} else if eq.eq_expr(e, divisor) {
|
||||
} else if eq.eq_expr(SyntaxContext::root(), e, divisor) {
|
||||
if first_use.is_none() {
|
||||
first_use = Some(UseKind::Other);
|
||||
return ControlFlow::Break(());
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, SyntaxContext};
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::Deref;
|
||||
|
||||
@@ -261,6 +261,7 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx
|
||||
{
|
||||
let params = is_clamp_meta_pattern(
|
||||
cx,
|
||||
expr.span.ctxt(),
|
||||
&BinaryOp::new(peel_blocks(cond))?,
|
||||
&BinaryOp::new(peel_blocks(else_if_cond))?,
|
||||
peel_blocks(then),
|
||||
@@ -268,7 +269,7 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx
|
||||
None,
|
||||
)?;
|
||||
// Contents of the else should be the resolved input.
|
||||
if !eq_expr_value(cx, params.input, peel_blocks(else_body)) {
|
||||
if !eq_expr_value(cx, expr.span.ctxt(), params.input, peel_blocks(else_body)) {
|
||||
return None;
|
||||
}
|
||||
Some(ClampSuggestion {
|
||||
@@ -445,6 +446,7 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt
|
||||
}
|
||||
if let Some(params) = is_clamp_meta_pattern(
|
||||
cx,
|
||||
expr.span.ctxt(),
|
||||
&first,
|
||||
&second,
|
||||
first_expr,
|
||||
@@ -496,11 +498,14 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) ->
|
||||
peel_blocks_with_stmt(first_then).kind
|
||||
&& let ExprKind::Assign(maybe_input_second_path, maybe_min_max_second, _) =
|
||||
peel_blocks_with_stmt(second_then).kind
|
||||
&& eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path)
|
||||
&& let ctxt = first_expr.span.ctxt()
|
||||
&& second_expr.span.ctxt() == ctxt
|
||||
&& eq_expr_value(cx, ctxt, maybe_input_first_path, maybe_input_second_path)
|
||||
&& let Some(first_bin) = BinaryOp::new(first_cond)
|
||||
&& let Some(second_bin) = BinaryOp::new(second_cond)
|
||||
&& let Some(input_min_max) = is_clamp_meta_pattern(
|
||||
cx,
|
||||
ctxt,
|
||||
&first_bin,
|
||||
&second_bin,
|
||||
maybe_min_max_first,
|
||||
@@ -552,15 +557,17 @@ fn is_if_elseif_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) ->
|
||||
&& let ExprKind::Assign(maybe_input_second_path, maybe_min_max_second, _) =
|
||||
peel_blocks_with_stmt(else_if_then).kind
|
||||
{
|
||||
let ctxt = expr.span.ctxt();
|
||||
let params = is_clamp_meta_pattern(
|
||||
cx,
|
||||
ctxt,
|
||||
&BinaryOp::new(peel_blocks(cond))?,
|
||||
&BinaryOp::new(peel_blocks(else_if_cond))?,
|
||||
peel_blocks(maybe_min_max_first),
|
||||
peel_blocks(maybe_min_max_second),
|
||||
None,
|
||||
)?;
|
||||
if !eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) {
|
||||
if !eq_expr_value(cx, ctxt, maybe_input_first_path, maybe_input_second_path) {
|
||||
return None;
|
||||
}
|
||||
Some(ClampSuggestion {
|
||||
@@ -631,6 +638,7 @@ fn flip(&self) -> Self {
|
||||
/// result can not be the shared argument in either case.
|
||||
fn is_clamp_meta_pattern<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
first_bin: &BinaryOp<'tcx>,
|
||||
second_bin: &BinaryOp<'tcx>,
|
||||
first_expr: &'tcx Expr<'tcx>,
|
||||
@@ -642,8 +650,10 @@ fn is_clamp_meta_pattern<'tcx>(
|
||||
// be the input variable, not the min or max.
|
||||
input_hir_ids: Option<(HirId, HirId)>,
|
||||
) -> Option<InputMinMax<'tcx>> {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
first_bin: &BinaryOp<'tcx>,
|
||||
second_bin: &BinaryOp<'tcx>,
|
||||
first_expr: &'tcx Expr<'tcx>,
|
||||
@@ -659,11 +669,11 @@ fn check<'tcx>(
|
||||
peel_blocks(first_bin.left).res_local_id() == Some(first_hir_id)
|
||||
&& peel_blocks(second_bin.left).res_local_id() == Some(second_hir_id)
|
||||
},
|
||||
None => eq_expr_value(cx, first_bin.left, second_bin.left),
|
||||
None => eq_expr_value(cx, ctxt, first_bin.left, second_bin.left),
|
||||
};
|
||||
(refers_to_input
|
||||
&& eq_expr_value(cx, first_bin.right, first_expr)
|
||||
&& eq_expr_value(cx, second_bin.right, second_expr))
|
||||
&& eq_expr_value(cx, ctxt, first_bin.right, first_expr)
|
||||
&& eq_expr_value(cx, ctxt, second_bin.right, second_expr))
|
||||
.then_some(InputMinMax {
|
||||
input: first_bin.left,
|
||||
min,
|
||||
@@ -699,9 +709,20 @@ fn check<'tcx>(
|
||||
];
|
||||
|
||||
cases.into_iter().find_map(|(first, second)| {
|
||||
check(cx, &first, &second, first_expr, second_expr, input_hir_ids, is_float).or_else(|| {
|
||||
check(
|
||||
cx,
|
||||
ctxt,
|
||||
&first,
|
||||
&second,
|
||||
first_expr,
|
||||
second_expr,
|
||||
input_hir_ids,
|
||||
is_float,
|
||||
)
|
||||
.or_else(|| {
|
||||
check(
|
||||
cx,
|
||||
ctxt,
|
||||
&second,
|
||||
&first,
|
||||
second_expr,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::SyntaxContext;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -109,11 +110,12 @@ fn count_ones_receiver<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Optio
|
||||
/// Return `greater` if `smaller == greater - 1`
|
||||
fn is_one_less<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
greater: &'tcx Expr<'tcx>,
|
||||
smaller: &Expr<'tcx>,
|
||||
) -> Option<&'tcx Expr<'tcx>> {
|
||||
if let Some((lhs, rhs)) = unexpanded_binop_operands(smaller, BinOpKind::Sub)
|
||||
&& SpanlessEq::new(cx).eq_expr(greater, lhs)
|
||||
&& SpanlessEq::new(cx).eq_expr(ctxt, greater, lhs)
|
||||
&& is_integer_literal(rhs, 1)
|
||||
&& matches!(cx.typeck_results().expr_ty_adjusted(greater).kind(), ty::Uint(_))
|
||||
{
|
||||
@@ -126,7 +128,7 @@ fn is_one_less<'tcx>(
|
||||
/// Return `v` if `expr` is `v & (v - 1)` or `(v - 1) & v`
|
||||
fn is_and_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
let (lhs, rhs) = unexpanded_binop_operands(expr, BinOpKind::BitAnd)?;
|
||||
is_one_less(cx, lhs, rhs).or_else(|| is_one_less(cx, rhs, lhs))
|
||||
is_one_less(cx, expr.span.ctxt(), lhs, rhs).or_else(|| is_one_less(cx, expr.span.ctxt(), rhs, lhs))
|
||||
}
|
||||
|
||||
/// Return the operands of the `expr` binary operation if the operator is `op` and none of the
|
||||
|
||||
@@ -212,7 +212,7 @@ fn check_is_some_and_pattern<'tcx>(
|
||||
&& let ExprKind::Closure(closure) = closure_arg.kind
|
||||
&& let body = cx.tcx.hir_body(closure.body)
|
||||
&& let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, then_block, pop_method)
|
||||
&& eq_expr_value(cx, collection_expr, pop_collection)
|
||||
&& eq_expr_value(cx, if_expr_span.ctxt(), collection_expr, pop_collection)
|
||||
&& let Some(param) = body.params.first()
|
||||
&& let Some(ident) = param.pat.simple_ident()
|
||||
{
|
||||
@@ -274,7 +274,7 @@ fn check_if_let_pattern<'tcx>(
|
||||
if let ExprKind::If(inner_cond, inner_then, None) = inner_if.kind
|
||||
&& is_local_used(cx, inner_cond, binding_id)
|
||||
&& let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, inner_then, pop_method)
|
||||
&& eq_expr_value(cx, collection_expr, pop_collection)
|
||||
&& eq_expr_value(cx, if_expr_span.ctxt(), collection_expr, pop_collection)
|
||||
{
|
||||
return Some(ManualPopIfPattern {
|
||||
kind,
|
||||
@@ -327,7 +327,7 @@ fn check_let_chain_pattern<'tcx>(
|
||||
&& kind.is_diag_item(cx, collection_expr)
|
||||
&& is_local_used(cx, right, binding_id)
|
||||
&& let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, then_block, pop_method)
|
||||
&& eq_expr_value(cx, collection_expr, pop_collection)
|
||||
&& eq_expr_value(cx, if_expr_span.ctxt(), collection_expr, pop_collection)
|
||||
{
|
||||
return Some(ManualPopIfPattern {
|
||||
kind,
|
||||
@@ -372,7 +372,7 @@ fn check_map_unwrap_or_pattern<'tcx>(
|
||||
&& let body = cx.tcx.hir_body(closure.body)
|
||||
&& cx.typeck_results().expr_ty(body.value).is_bool()
|
||||
&& let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, then_block, pop_method)
|
||||
&& eq_expr_value(cx, collection_expr, pop_collection)
|
||||
&& eq_expr_value(cx, if_expr_span.ctxt(), collection_expr, pop_collection)
|
||||
&& let Some(param) = body.params.first()
|
||||
&& let Some(ident) = param.pat.simple_ident()
|
||||
{
|
||||
|
||||
@@ -85,7 +85,7 @@ fn check_into_iter(
|
||||
&& let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id)
|
||||
&& Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn()
|
||||
&& match_acceptable_type(cx, left_expr, msrv)
|
||||
&& SpanlessEq::new(cx).eq_expr(left_expr, struct_expr)
|
||||
&& SpanlessEq::new(cx).eq_expr(parent_expr_span.ctxt(), left_expr, struct_expr)
|
||||
&& let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = target_expr.kind
|
||||
&& let hir::ExprKind::Closure(closure) = closure_expr.kind
|
||||
&& let filter_body = cx.tcx.hir_body(closure.body)
|
||||
@@ -132,7 +132,7 @@ fn check_iter(
|
||||
&& let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id)
|
||||
&& match_acceptable_sym(cx, iter_expr_def_id)
|
||||
&& match_acceptable_type(cx, left_expr, msrv)
|
||||
&& SpanlessEq::new(cx).eq_expr(left_expr, struct_expr)
|
||||
&& SpanlessEq::new(cx).eq_expr(parent_expr_span.ctxt(), left_expr, struct_expr)
|
||||
&& let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind
|
||||
&& let hir::ExprKind::Closure(closure) = closure_expr.kind
|
||||
&& let filter_body = cx.tcx.hir_body(closure.body)
|
||||
@@ -190,7 +190,7 @@ fn check_to_owned(
|
||||
&& cx.tcx.is_diagnostic_item(sym::str_chars, chars_expr_def_id)
|
||||
&& let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
|
||||
&& ty.is_lang_item(cx, hir::LangItem::String)
|
||||
&& SpanlessEq::new(cx).eq_expr(left_expr, str_expr)
|
||||
&& SpanlessEq::new(cx).eq_expr(parent_expr_span.ctxt(), left_expr, str_expr)
|
||||
&& let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind
|
||||
&& let hir::ExprKind::Closure(closure) = closure_expr.kind
|
||||
&& let filter_body = cx.tcx.hir_body(closure.body)
|
||||
|
||||
@@ -75,7 +75,8 @@ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
&& let Some((l_shift_dir, l_expr, l_amount)) = parse_shift(l)
|
||||
&& let Some((r_shift_dir, r_expr, r_amount)) = parse_shift(r)
|
||||
&& l_shift_dir != r_shift_dir
|
||||
&& clippy_utils::eq_expr_value(cx, l_expr, r_expr)
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& clippy_utils::eq_expr_value(cx, ctxt, l_expr, r_expr)
|
||||
&& let Some(bit_width) = match cx.typeck_results().expr_ty(expr).kind() {
|
||||
ty::Int(itype) => itype.bit_width(),
|
||||
ty::Uint(itype) => itype.bit_width(),
|
||||
@@ -84,7 +85,6 @@ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
{
|
||||
let const_eval = ConstEvalCtxt::new(cx);
|
||||
|
||||
let ctxt = expr.span.ctxt();
|
||||
let (shift_function, amount) = if let Some(Constant::Int(l_amount_val)) =
|
||||
const_eval.eval_local(l_amount, ctxt)
|
||||
&& let Some(Constant::Int(r_amount_val)) = const_eval.eval_local(r_amount, ctxt)
|
||||
@@ -103,7 +103,7 @@ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
};
|
||||
|
||||
if let Some(Constant::Int(minuend)) = const_eval.eval_local(minuend, ctxt)
|
||||
&& clippy_utils::eq_expr_value(cx, amount1, amount2)
|
||||
&& clippy_utils::eq_expr_value(cx, ctxt, amount1, amount2)
|
||||
// (x << s) | (x >> bit_width - s)
|
||||
&& ((binop.node == BinOpKind::Sub && u128::from(bit_width) == minuend)
|
||||
// (x << s) | (x >> (bit_width - 1) ^ s)
|
||||
|
||||
@@ -188,7 +188,7 @@ fn eq_pattern_length<'tcx>(
|
||||
{
|
||||
constant_length(cx, pattern, ctxt).is_some_and(|length| n == length)
|
||||
} else {
|
||||
len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg))
|
||||
len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, SyntaxContext::root(), pattern, arg))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::SpanlessEq;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{MEM_TAKE, Msrv};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
@@ -74,10 +75,11 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
&& 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 ctxt = expr.span.ctxt()
|
||||
&& !ctxt.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)
|
||||
&& SpanlessEq::new(cx).eq_expr(ctxt, 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)
|
||||
|
||||
@@ -14,12 +14,10 @@
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
use crate::collapsible_if::{parens_around, peel_parens};
|
||||
use rustc_span::{BytePos, Ident, Span, SyntaxContext};
|
||||
|
||||
use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or};
|
||||
use crate::collapsible_if::{parens_around, peel_parens};
|
||||
|
||||
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], msrv: Msrv) {
|
||||
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
|
||||
@@ -28,6 +26,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ar
|
||||
let only_wildcards_after = last_non_wildcard.is_none_or(|lnw| idx >= lnw);
|
||||
check_arm(
|
||||
cx,
|
||||
arm.span.ctxt(),
|
||||
true,
|
||||
arm.pat,
|
||||
expr,
|
||||
@@ -43,18 +42,20 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ar
|
||||
|
||||
pub(super) fn check_if_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
pat: &'tcx Pat<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
else_expr: Option<&'tcx Expr<'_>>,
|
||||
let_expr: &'tcx Expr<'_>,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv, false);
|
||||
check_arm(cx, ctxt, false, pat, let_expr, body, None, else_expr, msrv, false);
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, clippy::too_many_lines)]
|
||||
fn check_arm<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
outer_is_match: bool,
|
||||
outer_pat: &'tcx Pat<'tcx>,
|
||||
outer_cond: &'tcx Expr<'tcx>,
|
||||
@@ -94,7 +95,7 @@ fn check_arm<'tcx>(
|
||||
&& match (outer_else_body, inner_else_body) {
|
||||
(None, None) => true,
|
||||
(None, Some(e)) | (Some(e), None) => is_unit_expr(e),
|
||||
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
|
||||
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(ctxt, a, b),
|
||||
}
|
||||
// the binding must not be used in the if guard
|
||||
&& outer_guard.is_none_or(|e| !is_local_used(cx, e, binding_id))
|
||||
@@ -145,7 +146,7 @@ fn check_arm<'tcx>(
|
||||
&& match (outer_else_body, inner.r#else) {
|
||||
(None, None) => true,
|
||||
(None, Some(e)) | (Some(e), None) => is_unit_expr(e),
|
||||
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
|
||||
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(ctxt, a, b),
|
||||
}
|
||||
&& !pat_bindings_moved_or_mutated(cx, outer_pat, inner.cond)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty::{self, TypeckResults};
|
||||
use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol};
|
||||
use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol, SyntaxContext};
|
||||
|
||||
use super::MATCH_SAME_ARMS;
|
||||
|
||||
@@ -87,7 +87,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||
|
||||
SpanlessEq::new(cx)
|
||||
.expr_fallback(eq_fallback)
|
||||
.eq_expr(expr_a, expr_b)
|
||||
.eq_expr(SyntaxContext::root(), expr_a, expr_b)
|
||||
// these checks could be removed to allow unused bindings
|
||||
&& bindings_eq(lhs.pat, local_map.keys().copied().collect())
|
||||
&& bindings_eq(rhs.pat, local_map.values().copied().collect())
|
||||
|
||||
@@ -1138,6 +1138,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
} else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
|
||||
collapsible_match::check_if_let(
|
||||
cx,
|
||||
if_let.let_span.ctxt(),
|
||||
if_let.let_pat,
|
||||
if_let.if_then,
|
||||
if_let.if_else,
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExpr, PatExprKind, PatKind, Path, QPath,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{SyntaxContext, sym};
|
||||
|
||||
pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||
if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) {
|
||||
if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, expr.span.ctxt(), ex, arms) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
@@ -49,7 +49,10 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>],
|
||||
/// }
|
||||
/// ```
|
||||
pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: &higher::IfLet<'tcx>) {
|
||||
if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let_inner(cx, if_let) {
|
||||
if !is_else_clause(cx.tcx, ex)
|
||||
&& expr_ty_matches_p_ty(cx, if_let.let_expr, ex)
|
||||
&& check_if_let_inner(cx, ex.span.ctxt(), if_let)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
@@ -63,7 +66,7 @@ pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let:
|
||||
}
|
||||
}
|
||||
|
||||
fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
|
||||
fn check_all_arms(cx: &LateContext<'_>, ctxt: SyntaxContext, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
|
||||
for arm in arms {
|
||||
let arm_expr = peel_blocks_with_stmt(arm.body);
|
||||
|
||||
@@ -74,7 +77,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>])
|
||||
}
|
||||
|
||||
if let PatKind::Wild = arm.pat.kind {
|
||||
if !eq_expr_value(cx, match_expr, arm_expr) {
|
||||
if !eq_expr_value(cx, ctxt, match_expr, arm_expr) {
|
||||
return false;
|
||||
}
|
||||
} else if !pat_same_as_expr(arm.pat, arm_expr) {
|
||||
@@ -85,7 +88,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>])
|
||||
true
|
||||
}
|
||||
|
||||
fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
|
||||
fn check_if_let_inner(cx: &LateContext<'_>, ctxt: SyntaxContext, if_let: &higher::IfLet<'_>) -> bool {
|
||||
if let Some(if_else) = if_let.if_else {
|
||||
if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) {
|
||||
return false;
|
||||
@@ -93,9 +96,9 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool
|
||||
|
||||
// Recursively check for each `else if let` phrase,
|
||||
if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else)
|
||||
&& SpanlessEq::new(cx).eq_expr(nested_if_let.let_expr, if_let.let_expr)
|
||||
&& SpanlessEq::new(cx).eq_expr(ctxt, nested_if_let.let_expr, if_let.let_expr)
|
||||
{
|
||||
return check_if_let_inner(cx, nested_if_let);
|
||||
return check_if_let_inner(cx, ctxt, nested_if_let);
|
||||
}
|
||||
|
||||
if matches!(if_else.kind, ExprKind::Block(..)) {
|
||||
@@ -106,9 +109,9 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool
|
||||
let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr);
|
||||
if let_expr_ty.is_diag_item(cx, sym::Option) {
|
||||
return else_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
|
||||
|| eq_expr_value(cx, if_let.let_expr, else_expr);
|
||||
|| eq_expr_value(cx, ctxt, if_let.let_expr, else_expr);
|
||||
}
|
||||
return eq_expr_value(cx, if_let.let_expr, else_expr);
|
||||
return eq_expr_value(cx, ctxt, if_let.let_expr, else_expr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ pub(super) fn check<'tcx>(
|
||||
// of the last replace call in the current chain, don't lint as it was already linted
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let Some((sym::replace, _, [current_from, current_to], _, _)) = method_call(parent)
|
||||
&& eq_expr_value(cx, to, current_to)
|
||||
&& eq_expr_value(cx, parent.span.ctxt(), to, current_to)
|
||||
&& from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
|
||||
{
|
||||
return;
|
||||
@@ -46,9 +46,10 @@ fn collect_replace_calls<'tcx>(
|
||||
let mut methods = VecDeque::new();
|
||||
let mut from_args = VecDeque::new();
|
||||
|
||||
let ctxt = expr.span.ctxt();
|
||||
let _: Option<()> = for_each_expr_without_closures(expr, |e| {
|
||||
if let Some((sym::replace, _, [from, to], _, _)) = method_call(e) {
|
||||
if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
|
||||
if eq_expr_value(cx, ctxt, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
|
||||
methods.push_front(e);
|
||||
from_args.push_front(from);
|
||||
ControlFlow::Continue(())
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::TypeckResults;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::{Span, SyntaxContext};
|
||||
|
||||
use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP};
|
||||
|
||||
@@ -109,6 +109,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
|
||||
pub fn check_map_call(
|
||||
&self,
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
map_body: &'tcx Body<'tcx>,
|
||||
map_param_id: HirId,
|
||||
filter_param_id: HirId,
|
||||
@@ -150,7 +151,7 @@ pub fn check_map_call(
|
||||
&& a_typeck_results.expr_ty_adjusted(a) == b_typeck_results.expr_ty_adjusted(b)
|
||||
})
|
||||
&& (simple_equal
|
||||
|| SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(receiver, map_arg_peeled))
|
||||
|| SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(ctxt, receiver, map_arg_peeled))
|
||||
{
|
||||
Some(CheckResult::Method {
|
||||
map_arg,
|
||||
@@ -323,7 +324,9 @@ pub(super) fn check(
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((map_param_ident, check_result)) = is_find_or_filter(cx, map_recv, filter_arg, map_arg) {
|
||||
if let Some((map_param_ident, check_result)) =
|
||||
is_find_or_filter(cx, expr.span.ctxt(), map_recv, filter_arg, map_arg)
|
||||
{
|
||||
let span = filter_span.with_hi(expr.span.hi());
|
||||
let (filter_name, lint) = if is_find {
|
||||
("find", MANUAL_FIND_MAP)
|
||||
@@ -397,6 +400,7 @@ pub(super) fn check(
|
||||
|
||||
fn is_find_or_filter<'a>(
|
||||
cx: &LateContext<'a>,
|
||||
ctxt: SyntaxContext,
|
||||
map_recv: &Expr<'_>,
|
||||
filter_arg: &Expr<'_>,
|
||||
map_arg: &Expr<'_>,
|
||||
@@ -422,7 +426,7 @@ fn is_find_or_filter<'a>(
|
||||
&& let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind
|
||||
|
||||
&& let Some(check_result) =
|
||||
offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref)
|
||||
offending_expr.check_map_call(cx, ctxt, map_body, map_param_id, filter_param_id, is_filter_param_ref)
|
||||
{
|
||||
return Some((map_param_ident, check_result));
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg:
|
||||
&& is_integer_literal(rhs, 1)
|
||||
|
||||
// check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)`
|
||||
&& SpanlessEq::new(cx).eq_expr(recv, lhs_recv)
|
||||
&& SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), recv, lhs_recv)
|
||||
&& !recv.can_have_side_effects()
|
||||
{
|
||||
let method = match cx.typeck_results().expr_ty_adjusted(recv).peel_refs().kind() {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::res::MaybeDef;
|
||||
use clippy_utils::{is_integer_literal, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, LangItem};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
use super::MANUAL_CLEAR;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>, method_span: Span) {
|
||||
let ty = cx.typeck_results().expr_ty_adjusted(recv);
|
||||
let ty = ty.peel_refs();
|
||||
|
||||
let diag_name = ty.ty_adt_def().and_then(|def| cx.tcx.get_diagnostic_name(def.did()));
|
||||
|
||||
if (matches!(diag_name, Some(sym::Vec | sym::VecDeque | sym::OsString)) || ty.is_lang_item(cx, LangItem::String))
|
||||
&& is_integer_literal(arg, 0)
|
||||
{
|
||||
span_lint_and_then(cx, MANUAL_CLEAR, expr.span, "truncating to zero length", |diag| {
|
||||
// Keep the receiver as-is and only rewrite the method.
|
||||
diag.span_suggestion_verbose(
|
||||
method_span.with_hi(expr.span.hi()),
|
||||
"use `clear()` instead",
|
||||
"clear()",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -256,7 +256,7 @@ pub(super) fn check_or<'tcx>(
|
||||
.expr_ty_adjusted(some_recv)
|
||||
.peel_refs()
|
||||
.is_diag_item(cx, sym::Option)
|
||||
&& SpanlessEq::new(cx).eq_expr(none_recv, some_recv)
|
||||
&& SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), none_recv, some_recv)
|
||||
{
|
||||
(some_recv, some_arg)
|
||||
} else {
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
use clippy_utils::peel_blocks;
|
||||
use clippy_utils::res::{MaybeDef, MaybeResPath};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||
use clippy_utils::usage::local_used_in;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{self as hir, Expr, ExprKind, HirId, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use super::MANUAL_OPTION_ZIP;
|
||||
|
||||
@@ -31,13 +30,7 @@ pub(super) fn check<'tcx>(
|
||||
&& method_path.ident.name == sym::map
|
||||
&& cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option)
|
||||
// `b` does not reference the outer closure parameter `a`.
|
||||
&& for_each_expr_without_closures(map_recv, |e| {
|
||||
if e.res_local_id() == Some(outer_param_id) {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}).is_none()
|
||||
&& !local_used_in(cx, outer_param_id, map_recv)
|
||||
// `|b| (a, b)`
|
||||
&& let ExprKind::Closure(&hir::Closure { body: inner_body_id, .. }) = map_arg.kind
|
||||
&& let hir::Body { params: [inner_param], value: inner_value, .. } = cx.tcx.hir_body(inner_body_id)
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
mod lib;
|
||||
mod lines_filter_map_ok;
|
||||
mod manual_c_str_literals;
|
||||
mod manual_clear;
|
||||
mod manual_contains;
|
||||
mod manual_inspect;
|
||||
mod manual_is_variant_and;
|
||||
@@ -111,6 +112,7 @@
|
||||
mod single_char_add_str;
|
||||
mod skip_while_next;
|
||||
mod sliced_string_as_bytes;
|
||||
mod some_filter;
|
||||
mod stable_sort_primitive;
|
||||
mod str_split;
|
||||
mod str_splitn;
|
||||
@@ -1769,6 +1771,31 @@
|
||||
r#"creating a `CStr` through functions when `c""` literals can be used"#
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `.truncate(0)` calls on standard library types where it can be replaced with `.clear()`.
|
||||
///
|
||||
/// Currently this includes `Vec`, `VecDeque`, `String`, and `OsString`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `clear()` expresses the intent better and is likely to be more efficient than `truncate(0)`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut v = vec![1, 2, 3];
|
||||
/// v.truncate(0);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let mut v = vec![1, 2, 3];
|
||||
/// v.clear();
|
||||
/// ```
|
||||
#[clippy::version = "1.97.0"]
|
||||
pub MANUAL_CLEAR,
|
||||
perf,
|
||||
"using `truncate(0)` instead of `clear()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `iter().any()` on slices when it can be replaced with `contains()` and suggests doing so.
|
||||
@@ -3577,6 +3604,29 @@
|
||||
"slicing a string and immediately calling as_bytes is less efficient and can lead to panics"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `Some(x).filter(|_| predicate)`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability, this can be written more concisely as `predicate.then_some(x)`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x = false;
|
||||
/// Some(0).filter(|_| x);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x = false;
|
||||
/// x.then_some(0);
|
||||
/// ```
|
||||
#[clippy::version = "1.97.0"]
|
||||
pub SOME_FILTER,
|
||||
complexity,
|
||||
"using `Some(x).filter(|_| predicate)`, which is more succinctly expressed as `predicate.then(x)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// When sorting primitive values (integers, bools, chars, as well
|
||||
@@ -4839,6 +4889,7 @@
|
||||
ITER_WITH_DRAIN,
|
||||
JOIN_ABSOLUTE_PATHS,
|
||||
LINES_FILTER_MAP_OK,
|
||||
MANUAL_CLEAR,
|
||||
MANUAL_CONTAINS,
|
||||
MANUAL_C_STR_LITERALS,
|
||||
MANUAL_FILTER_MAP,
|
||||
@@ -4900,6 +4951,7 @@
|
||||
SINGLE_CHAR_ADD_STR,
|
||||
SKIP_WHILE_NEXT,
|
||||
SLICED_STRING_AS_BYTES,
|
||||
SOME_FILTER,
|
||||
STABLE_SORT_PRIMITIVE,
|
||||
STRING_EXTEND_CHARS,
|
||||
STRING_LIT_CHARS_ANY,
|
||||
@@ -5307,6 +5359,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
// use the sourcemap to get the span of the closure
|
||||
iter_filter::check(cx, expr, arg, span);
|
||||
}
|
||||
some_filter::check(cx, expr, recv, arg, self.msrv);
|
||||
},
|
||||
(sym::find, [arg]) => {
|
||||
if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) {
|
||||
@@ -5808,6 +5861,9 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
(sym::to_string, []) => {
|
||||
inefficient_to_string::check(cx, expr, recv, self.msrv);
|
||||
},
|
||||
(sym::truncate, [arg]) => {
|
||||
manual_clear::check(cx, expr, recv, arg, method_span);
|
||||
},
|
||||
(sym::unwrap, []) => {
|
||||
unwrap_expect_used::check(
|
||||
cx,
|
||||
|
||||
@@ -28,7 +28,7 @@ pub(super) fn check<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
if SpanlessEq::new(cx).eq_expr(arg1, arg2) {
|
||||
if SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), arg1, arg2) {
|
||||
span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
|
||||
// `.iter()` and `.len()` called on same `Path`
|
||||
&& let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind
|
||||
&& SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments)
|
||||
&& SpanlessEq::new(cx).eq_path_segments(expr.span.ctxt(), iter_path.segments, len_path.segments)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::{as_some_expr, pat_is_wild, peel_blocks, span_contains_comment};
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::SOME_FILTER;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
recv: &'tcx Expr<'tcx>,
|
||||
arg: &'tcx Expr<'tcx>,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
let (condition, value) = if let Some(value) = as_some_expr(cx, recv)
|
||||
&& let ExprKind::Closure(c) = arg.kind
|
||||
&& let Body {
|
||||
params: [p],
|
||||
value: condition,
|
||||
} = cx.tcx.hir_body(c.body)
|
||||
&& pat_is_wild(cx, &p.pat.kind, arg)
|
||||
&& msrv.meets(cx, msrvs::BOOL_THEN_SOME)
|
||||
{
|
||||
(condition, value)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SOME_FILTER,
|
||||
expr.span,
|
||||
"use of `Some(x).filter(|_| predicate)`",
|
||||
|diag| {
|
||||
let condition = if span_contains_comment(cx, condition.span) {
|
||||
condition
|
||||
} else {
|
||||
peel_blocks(condition)
|
||||
};
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let (condition_text, condition_is_macro) =
|
||||
snippet_with_context(cx, condition.span, arg.span.ctxt(), "_", &mut applicability);
|
||||
let parentheses = !condition_is_macro && cx.precedence(condition) < ExprPrecedence::Unambiguous;
|
||||
let value_text = snippet_with_applicability(cx, value.span, "_", &mut applicability);
|
||||
let sugg = format!(
|
||||
"{}{condition_text}{}.then_some({value_text})",
|
||||
if parentheses { "(" } else { "" },
|
||||
if parentheses { ")" } else { "" },
|
||||
);
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span,
|
||||
"consider using `bool::then_some` instead",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
diag.note(
|
||||
"this change will alter the order in which the condition and \
|
||||
the value are evaluated",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -66,7 +66,7 @@ fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Ex
|
||||
for_each_expr_without_closures(block, |e| {
|
||||
match e.kind {
|
||||
ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
|
||||
change |= !can_mut_borrow_both(cx, caller, assignee);
|
||||
change |= !can_mut_borrow_both(cx, body.span.ctxt(), caller, assignee);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
@@ -238,7 +238,9 @@ fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
/// of what it means for an expression to be "used".
|
||||
fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
get_parent_expr(cx, expr).is_none_or(|parent| match parent.kind {
|
||||
ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr),
|
||||
ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => {
|
||||
SpanlessEq::new(cx).eq_expr(parent.span.ctxt(), rhs, expr)
|
||||
},
|
||||
_ => is_used(cx, parent),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -245,7 +245,10 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni
|
||||
let hash = hash_expr(cx, slice);
|
||||
|
||||
let indexes = map.entry(hash).or_default();
|
||||
let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice));
|
||||
let ctxt = expr.span.ctxt();
|
||||
let entry = indexes
|
||||
.iter_mut()
|
||||
.find(|entry| eq_expr_value(cx, ctxt, entry.slice(), slice));
|
||||
|
||||
if let Some(entry) = entry {
|
||||
match entry {
|
||||
@@ -305,7 +308,10 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
|
||||
let hash = hash_expr(cx, slice);
|
||||
let indexes = map.entry(hash).or_default();
|
||||
|
||||
let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice));
|
||||
let ctxt = expr.span.ctxt();
|
||||
let entry = indexes
|
||||
.iter_mut()
|
||||
.find(|entry| eq_expr_value(cx, ctxt, entry.slice(), slice));
|
||||
|
||||
if let Some(entry) = entry {
|
||||
if let IndexEntry::IndexWithoutAssert {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::SyntaxContext;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -169,7 +170,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
}
|
||||
if let Some((lhs_a, a)) = fetch_assign(then)
|
||||
&& let Some((lhs_b, b)) = fetch_assign(else_expr)
|
||||
&& SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b)
|
||||
&& SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), lhs_a, lhs_b)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let cond = Sugg::hir_with_context(cx, cond, e.span.ctxt(), "..", &mut applicability);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use super::implicit_return::IMPLICIT_RETURN;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath};
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{is_from_proc_macro, last_path_segment, std_or_core};
|
||||
use clippy_utils::{is_from_proc_macro, is_lint_allowed, last_path_segment, std_or_core};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, LangItem, UnOp};
|
||||
use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Stmt, StmtKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::{TyCtxt, TypeckResults};
|
||||
use rustc_session::impl_lint_pass;
|
||||
@@ -185,8 +186,8 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if let Some(copy_trait) = self.copy_trait
|
||||
&& implements_trait(cx, trait_impl.self_ty(), copy_trait, &[])
|
||||
{
|
||||
for (assoc, _, block) in assoc_fns {
|
||||
check_clone_on_copy(cx, assoc, block);
|
||||
for (assoc, body, _) in assoc_fns {
|
||||
check_clone_on_copy(cx, assoc, body.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -208,29 +209,34 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &Block<'_>) {
|
||||
if impl_item.ident.name == sym::clone {
|
||||
if block.stmts.is_empty()
|
||||
&& let Some(expr) = block.expr
|
||||
&& let ExprKind::Unary(UnOp::Deref, deref) = expr.kind
|
||||
&& let ExprKind::Path(qpath) = deref.kind
|
||||
&& last_path_segment(&qpath).ident.name == kw::SelfLower
|
||||
{
|
||||
// this is the canonical implementation, `fn clone(&self) -> Self { *self }`
|
||||
return;
|
||||
}
|
||||
fn is_deref_self(expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Unary(UnOp::Deref, deref) = expr.kind
|
||||
&& let ExprKind::Path(qpath) = deref.kind
|
||||
&& last_path_segment(&qpath).ident.name == kw::SelfLower
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, body_expr: &Expr<'_>) {
|
||||
if impl_item.ident.name == sym::clone {
|
||||
if is_from_proc_macro(cx, impl_item) {
|
||||
return;
|
||||
}
|
||||
|
||||
let add_return = match is_canonical_clone_body(body_expr) {
|
||||
IsCanonical::WithReturn if is_lint_allowed(cx, IMPLICIT_RETURN, body_expr.hir_id) => false,
|
||||
IsCanonical::WithReturn | IsCanonical::WithoutReturn => return,
|
||||
IsCanonical::No => !is_lint_allowed(cx, IMPLICIT_RETURN, body_expr.hir_id),
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NON_CANONICAL_CLONE_IMPL,
|
||||
block.span,
|
||||
body_expr.span,
|
||||
"non-canonical implementation of `clone` on a `Copy` type",
|
||||
"change this to",
|
||||
"{ *self }".to_owned(),
|
||||
if add_return { "{ return *self; }" } else { "{ *self }" }.to_owned(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
@@ -248,6 +254,40 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B
|
||||
}
|
||||
}
|
||||
|
||||
enum IsCanonical {
|
||||
WithoutReturn,
|
||||
WithReturn,
|
||||
No,
|
||||
}
|
||||
|
||||
fn is_canonical_clone_body(body_expr: &Expr<'_>) -> IsCanonical {
|
||||
let ExprKind::Block(block, ..) = body_expr.kind else {
|
||||
return IsCanonical::No;
|
||||
};
|
||||
let single_expr = match (block.stmts, block.expr) {
|
||||
([], Some(expr)) => Some(expr),
|
||||
(
|
||||
[
|
||||
Stmt {
|
||||
kind: StmtKind::Expr(expr) | StmtKind::Semi(expr),
|
||||
..
|
||||
},
|
||||
],
|
||||
None,
|
||||
) => Some(*expr),
|
||||
_ => None,
|
||||
};
|
||||
let Some(expr) = single_expr else {
|
||||
return IsCanonical::No;
|
||||
};
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Ret(Some(ret)) if is_deref_self(ret) => IsCanonical::WithReturn,
|
||||
_ if is_deref_self(expr) => IsCanonical::WithoutReturn,
|
||||
_ => IsCanonical::No,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_partial_ord_on_ord<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
impl_item: &ImplItem<'_>,
|
||||
@@ -269,7 +309,7 @@ fn check_partial_ord_on_ord<'tcx>(
|
||||
// Fix #12683, allow [`needless_return`] here
|
||||
else if block.expr.is_none()
|
||||
&& let Some(stmt) = block.stmts.first()
|
||||
&& let rustc_hir::StmtKind::Semi(Expr {
|
||||
&& let StmtKind::Semi(Expr {
|
||||
kind: ExprKind::Ret(Some(ret)),
|
||||
..
|
||||
}) = stmt.kind
|
||||
|
||||
@@ -89,9 +89,10 @@ pub(super) fn check<'tcx>(
|
||||
}
|
||||
};
|
||||
|
||||
let ctxt = expr.span.ctxt();
|
||||
let mut found = false;
|
||||
let found_multiple = for_each_expr_without_closures(e, |e| {
|
||||
if eq_expr_value(cx, assignee, e) {
|
||||
if eq_expr_value(cx, ctxt, assignee, e) {
|
||||
if found {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
@@ -103,12 +104,12 @@ pub(super) fn check<'tcx>(
|
||||
|
||||
if found && !found_multiple {
|
||||
// a = a op b
|
||||
if eq_expr_value(cx, assignee, l) {
|
||||
if eq_expr_value(cx, ctxt, assignee, l) {
|
||||
lint(assignee, r);
|
||||
}
|
||||
// a = b commutative_op a
|
||||
// Limited to primitive type as these ops are know to be commutative
|
||||
if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
|
||||
if eq_expr_value(cx, ctxt, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
|
||||
match op.node {
|
||||
hir::BinOpKind::Add
|
||||
| hir::BinOpKind::Mul
|
||||
|
||||
@@ -63,7 +63,7 @@ pub(super) fn check<'tcx>(
|
||||
&& left_type == right_type
|
||||
|
||||
// Check that the same expression is compared in both comparisons
|
||||
&& SpanlessEq::new(cx).eq_expr(left_expr, right_expr)
|
||||
&& SpanlessEq::new(cx).eq_expr(span.ctxt(), left_expr, right_expr)
|
||||
|
||||
&& !left_expr.can_have_side_effects()
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>, span: Span) {
|
||||
if let ExprKind::Binary(lop, llhs, lrhs) = lhs.kind
|
||||
&& let ExprKind::Binary(rop, rlhs, rrhs) = rhs.kind
|
||||
&& eq_expr_value(cx, llhs, rlhs)
|
||||
&& eq_expr_value(cx, lrhs, rrhs)
|
||||
&& let ctxt = span.ctxt()
|
||||
&& eq_expr_value(cx, ctxt, llhs, rlhs)
|
||||
&& eq_expr_value(cx, ctxt, lrhs, rrhs)
|
||||
{
|
||||
let op = match (op, lop.node, rop.node) {
|
||||
// x == y || x < y => x <= y
|
||||
|
||||
@@ -14,7 +14,7 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
Some(sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro)
|
||||
)
|
||||
}) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
|
||||
&& eq_expr_value(cx, lhs, rhs)
|
||||
&& eq_expr_value(cx, macro_call.span.ctxt(), lhs, rhs)
|
||||
&& macro_call.is_local()
|
||||
&& !is_in_test_function(cx.tcx, e.hir_id)
|
||||
{
|
||||
@@ -37,7 +37,10 @@ pub(crate) fn check<'tcx>(
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
if is_useless_with_eq_exprs(op) && eq_expr_value(cx, left, right) && !is_in_test_function(cx.tcx, e.hir_id) {
|
||||
if is_useless_with_eq_exprs(op)
|
||||
&& eq_expr_value(cx, e.span.ctxt(), left, right)
|
||||
&& !is_in_test_function(cx.tcx, e.hir_id)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
EQ_OP,
|
||||
|
||||
@@ -21,6 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &
|
||||
&& check_int_ty_and_feature(cx, cx.typeck_results().expr_ty(rhs))
|
||||
&& msrv.meets(cx, msrvs::DIV_CEIL)
|
||||
{
|
||||
let ctxt = expr.span.ctxt();
|
||||
match lhs.kind {
|
||||
ExprKind::Binary(inner_op, inner_lhs, inner_rhs) => {
|
||||
// (x + (y - 1)) / y
|
||||
@@ -28,7 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &
|
||||
&& inner_op.node == BinOpKind::Add
|
||||
&& sub_op.node == BinOpKind::Sub
|
||||
&& check_literal(sub_rhs)
|
||||
&& check_eq_expr(cx, sub_lhs, rhs)
|
||||
&& SpanlessEq::new(cx).eq_expr(ctxt, sub_lhs, rhs)
|
||||
{
|
||||
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
|
||||
return;
|
||||
@@ -39,7 +40,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &
|
||||
&& inner_op.node == BinOpKind::Add
|
||||
&& sub_op.node == BinOpKind::Sub
|
||||
&& check_literal(sub_rhs)
|
||||
&& check_eq_expr(cx, sub_lhs, rhs)
|
||||
&& SpanlessEq::new(cx).eq_expr(ctxt, sub_lhs, rhs)
|
||||
{
|
||||
build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability);
|
||||
return;
|
||||
@@ -50,7 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &
|
||||
&& inner_op.node == BinOpKind::Sub
|
||||
&& add_op.node == BinOpKind::Add
|
||||
&& check_literal(inner_rhs)
|
||||
&& check_eq_expr(cx, add_rhs, rhs)
|
||||
&& SpanlessEq::new(cx).eq_expr(ctxt, add_rhs, rhs)
|
||||
{
|
||||
build_suggestion(cx, expr, add_lhs, rhs, &mut applicability);
|
||||
}
|
||||
@@ -76,7 +77,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &
|
||||
ExprKind::MethodCall(method, receiver, [next_multiple_of_arg], _)
|
||||
if method.ident.name == sym::next_multiple_of
|
||||
&& check_int_ty(cx.typeck_results().expr_ty(receiver))
|
||||
&& check_eq_expr(cx, next_multiple_of_arg, rhs) =>
|
||||
&& SpanlessEq::new(cx).eq_expr(ctxt, next_multiple_of_arg, rhs) =>
|
||||
{
|
||||
// x.next_multiple_of(Y) / Y
|
||||
build_suggestion(cx, expr, receiver, rhs, &mut applicability);
|
||||
@@ -88,7 +89,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &
|
||||
.assoc_fn_parent(cx)
|
||||
.opt_impl_ty(cx)
|
||||
&& check_int_ty(impl_ty_binder.skip_binder())
|
||||
&& check_eq_expr(cx, next_multiple_of_arg, rhs)
|
||||
&& SpanlessEq::new(cx).eq_expr(ctxt, next_multiple_of_arg, rhs)
|
||||
{
|
||||
build_suggestion(cx, expr, receiver, rhs, &mut applicability);
|
||||
}
|
||||
@@ -137,10 +138,6 @@ fn check_literal(expr: &Expr<'_>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool {
|
||||
SpanlessEq::new(cx).eq_expr(lhs, rhs)
|
||||
}
|
||||
|
||||
fn build_suggestion(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
|
||||
@@ -19,9 +19,10 @@ pub(super) fn check<'tcx>(
|
||||
return;
|
||||
}
|
||||
// lhs op= l op r
|
||||
if eq_expr_value(cx, lhs, l) {
|
||||
let ctxt = expr.span.ctxt();
|
||||
if eq_expr_value(cx, ctxt, lhs, l) {
|
||||
lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, r);
|
||||
} else if is_commutative(op) && eq_expr_value(cx, lhs, r) {
|
||||
} else if is_commutative(op) && eq_expr_value(cx, ctxt, lhs, r) {
|
||||
// lhs op= l commutative_op r
|
||||
lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, l);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use super::SELF_ASSIGNMENT;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>) {
|
||||
if eq_expr_value(cx, lhs, rhs) {
|
||||
if eq_expr_value(cx, e.span.ctxt(), lhs, rhs) {
|
||||
let lhs = snippet(cx, lhs.span, "<lhs>");
|
||||
let rhs = snippet(cx, rhs.span, "<rhs>");
|
||||
span_lint(
|
||||
|
||||
@@ -72,7 +72,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
&& ty == typeck.expr_ty(op_rhs)
|
||||
&& ty == typeck.expr_ty(other)
|
||||
&& !expr.span.in_external_macro(cx.tcx.sess.source_map())
|
||||
&& (eq_expr_value(cx, op_lhs, other) || (commutative && eq_expr_value(cx, op_rhs, other)))
|
||||
&& (eq_expr_value(cx, ctxt, op_lhs, other) || (commutative && eq_expr_value(cx, ctxt, op_rhs, other)))
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
|
||||
@@ -299,7 +299,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex
|
||||
let by_ref = !cx.type_is_copy_modulo_regions(caller_ty)
|
||||
&& !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
|
||||
let sugg = if let Some(else_inner) = r#else {
|
||||
if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
|
||||
if eq_expr_value(cx, expr.span.ctxt(), caller, peel_blocks(else_inner)) {
|
||||
format!("Some({receiver_str}?)")
|
||||
} else {
|
||||
return;
|
||||
@@ -537,7 +537,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
|
||||
&& let is_option_early_return = is_early_return(sym::Option, cx, &if_block)
|
||||
&& (is_option_early_return || is_early_return(sym::Result, cx, &if_block))
|
||||
&& if_else
|
||||
.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e)))
|
||||
.map(|e| eq_expr_value(cx, expr.span.ctxt(), let_expr, peel_blocks(e)))
|
||||
.is_none_or(|e| !e)
|
||||
{
|
||||
if !is_copy(cx, caller_ty)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use clippy_utils::{is_from_proc_macro, is_inside_let_else};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
|
||||
@@ -23,10 +23,8 @@ pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
&& maybe_constr.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr)
|
||||
|
||||
// Ensure this is not the final stmt, otherwise removing it would cause a compile error
|
||||
&& let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id))
|
||||
&& let ItemKind::Fn { body, .. } = item.kind
|
||||
&& let block = cx.tcx.hir_body(body).value
|
||||
&& let ExprKind::Block(block, _) = block.kind
|
||||
&& let block = cx.tcx.hir_body_owned_by(cx.tcx.hir_enclosing_body_owner(expr.hir_id)).value
|
||||
&& let ExprKind::Block(block, _) = peel_async_body(block).kind
|
||||
&& !is_inside_let_else(cx.tcx, expr)
|
||||
&& let [.., final_stmt] = block.stmts
|
||||
&& final_stmt.hir_id != stmt.hir_id
|
||||
@@ -45,6 +43,21 @@ pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// In async functions, the body is wrapped in a
|
||||
/// `Block { expr: DropTemps(inner) }`. We peel through to `inner` so
|
||||
/// we can check the actual stmts.
|
||||
/// Returns `body_value` unchanged for non-async functions.
|
||||
fn peel_async_body<'a>(body_value: &'a Expr<'a>) -> &'a Expr<'a> {
|
||||
if let ExprKind::Block(block, _) = body_value.kind
|
||||
&& let Some(expr) = block.expr
|
||||
&& let ExprKind::DropTemps(inner) = expr.kind
|
||||
{
|
||||
inner
|
||||
} else {
|
||||
body_value
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed.
|
||||
/// This is the case when the enclosing block expression is coerced to some other type,
|
||||
/// which only works because of the never-ness of `return` expressions
|
||||
|
||||
@@ -79,7 +79,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
&& let ExprKind::Path(QPath::TypeRelative(ty, fn_path)) = path_expr.kind
|
||||
&& fn_path.ident.name == sym::from_raw_parts
|
||||
&& args.len() >= 3
|
||||
&& eq_expr_value(cx, &args[1], &args[2])
|
||||
&& eq_expr_value(cx, expr.span.ctxt(), &args[1], &args[2])
|
||||
{
|
||||
let middle_ty = cx.typeck_results().node_type(ty.hir_id);
|
||||
if middle_ty.is_diag_item(cx, rustc_sym::Vec) {
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{Span, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -119,7 +119,7 @@ fn is_set_mutated<'tcx>(cx: &LateContext<'tcx>, contains_expr: &OpExpr<'tcx>, ex
|
||||
cx.typeck_results().expr_ty(expr).peel_refs().opt_diag_name(cx),
|
||||
Some(sym::HashSet | sym::BTreeSet)
|
||||
)
|
||||
&& SpanlessEq::new(cx).eq_expr(contains_expr.receiver, expr.peel_borrows())
|
||||
&& SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.receiver, expr.peel_borrows())
|
||||
}
|
||||
|
||||
fn find_insert_calls<'tcx>(
|
||||
@@ -129,8 +129,8 @@ fn find_insert_calls<'tcx>(
|
||||
) -> Option<OpExpr<'tcx>> {
|
||||
for_each_expr(cx, expr, |e| {
|
||||
if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym::insert)
|
||||
&& SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver)
|
||||
&& SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value)
|
||||
&& SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.receiver, insert_expr.receiver)
|
||||
&& SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.value, insert_expr.value)
|
||||
{
|
||||
return ControlFlow::Break(Some(insert_expr));
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::SyntaxContext;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -265,7 +266,7 @@ fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
{
|
||||
let is_matching_resize = if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr {
|
||||
// If we have a size expression, check that it is equal to what's passed to `resize`
|
||||
SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
|
||||
SpanlessEq::new(self.cx).eq_expr(SyntaxContext::root(), len_arg, size_expr)
|
||||
|| matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity)
|
||||
} else {
|
||||
self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg);
|
||||
@@ -287,7 +288,7 @@ fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
{
|
||||
if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr {
|
||||
// Check that len expression is equals to `with_capacity` expression
|
||||
return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
|
||||
return SpanlessEq::new(self.cx).eq_expr(SyntaxContext::root(), len_arg, size_expr)
|
||||
|| matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Spanned;
|
||||
use rustc_span::{Spanned, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -220,7 +220,8 @@
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for StringAdd {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if e.span.in_external_macro(cx.sess().source_map()) {
|
||||
let ctxt = e.span.ctxt();
|
||||
if ctxt.in_external_macro(cx.sess().source_map()) {
|
||||
return;
|
||||
}
|
||||
match e.kind {
|
||||
@@ -235,8 +236,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
let parent = get_parent_expr(cx, e);
|
||||
if let Some(p) = parent
|
||||
&& let ExprKind::Assign(target, _, _) = p.kind
|
||||
// avoid duplicate matches
|
||||
&& SpanlessEq::new(cx).eq_expr(target, left)
|
||||
// avoid duplicate matches
|
||||
&& SpanlessEq::new(cx).eq_expr(ctxt, target, left)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -248,7 +249,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
"you added something to a string. Consider using `String::push_str()` instead",
|
||||
);
|
||||
},
|
||||
ExprKind::Assign(target, src, _) if is_string(cx, target) && is_add(cx, src, target) => {
|
||||
ExprKind::Assign(target, src, _) if is_string(cx, target) && is_add(cx, ctxt, src, target) => {
|
||||
span_lint(
|
||||
cx,
|
||||
STRING_ADD_ASSIGN,
|
||||
@@ -280,7 +281,7 @@ fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
.is_lang_item(cx, LangItem::String)
|
||||
}
|
||||
|
||||
fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
|
||||
fn is_add(cx: &LateContext<'_>, ctxt: SyntaxContext, src: &Expr<'_>, target: &Expr<'_>) -> bool {
|
||||
match peel_blocks(src).kind {
|
||||
ExprKind::Binary(
|
||||
Spanned {
|
||||
@@ -288,7 +289,7 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
|
||||
},
|
||||
left,
|
||||
_,
|
||||
) => SpanlessEq::new(cx).eq_expr(target, left),
|
||||
) => SpanlessEq::new(cx).eq_expr(ctxt, target, left),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,12 +98,12 @@ fn generate_swap_warning<'tcx>(
|
||||
let ctxt = span.ctxt();
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
if !can_mut_borrow_both(cx, e1, e2) {
|
||||
if !can_mut_borrow_both(cx, ctxt, e1, e2) {
|
||||
if let ExprKind::Index(lhs1, idx1, _) = e1.kind
|
||||
&& let ExprKind::Index(lhs2, idx2, _) = e2.kind
|
||||
&& eq_expr_value(cx, lhs1, lhs2)
|
||||
&& e1.span.ctxt() == ctxt
|
||||
&& e2.span.ctxt() == ctxt
|
||||
&& eq_expr_value(cx, ctxt, lhs1, lhs2)
|
||||
{
|
||||
let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
|
||||
|
||||
@@ -189,14 +189,15 @@ fn check_manual_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
|
||||
&& rhs2_path.segments.len() == 1
|
||||
|
||||
&& ident.name == rhs2_path.segments[0].ident.name
|
||||
&& eq_expr_value(cx, tmp_init, lhs1)
|
||||
&& eq_expr_value(cx, rhs1, lhs2)
|
||||
|
||||
&& let ctxt = s1.span.ctxt()
|
||||
&& s2.span.ctxt() == ctxt
|
||||
&& s3.span.ctxt() == ctxt
|
||||
&& first.span.ctxt() == ctxt
|
||||
&& second.span.ctxt() == ctxt
|
||||
|
||||
&& eq_expr_value(cx, ctxt, tmp_init, lhs1)
|
||||
&& eq_expr_value(cx, ctxt, rhs1, lhs2)
|
||||
{
|
||||
let span = s1.span.to(s3.span);
|
||||
generate_swap_warning(block, cx, lhs1, lhs2, rhs1, rhs2, span, false);
|
||||
@@ -209,11 +210,12 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
|
||||
for [first, second] in block.stmts.array_windows() {
|
||||
if let Some((lhs0, rhs0)) = parse(first)
|
||||
&& let Some((lhs1, rhs1)) = parse(second)
|
||||
&& first.span.eq_ctxt(second.span)
|
||||
&& !first.span.in_external_macro(cx.sess().source_map())
|
||||
&& is_same(cx, lhs0, rhs1)
|
||||
&& is_same(cx, lhs1, rhs0)
|
||||
&& !is_same(cx, lhs1, rhs1) // Ignore a = b; a = a (#10421)
|
||||
&& let ctxt = first.span.ctxt()
|
||||
&& ctxt == second.span.ctxt()
|
||||
&& !ctxt.in_external_macro(cx.sess().source_map())
|
||||
&& is_same(cx, ctxt, lhs0, rhs1)
|
||||
&& is_same(cx, ctxt, lhs1, rhs0)
|
||||
&& !is_same(cx, ctxt, lhs1, rhs1) // Ignore a = b; a = a (#10421)
|
||||
&& let Some(lhs_sugg) = match &lhs0 {
|
||||
ExprOrIdent::Expr(expr) => Sugg::hir_opt(cx, expr),
|
||||
ExprOrIdent::Ident(ident) => Some(Sugg::NonParen(ident.as_str().into())),
|
||||
@@ -241,9 +243,9 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_same(cx: &LateContext<'_>, lhs: ExprOrIdent<'_>, rhs: &Expr<'_>) -> bool {
|
||||
fn is_same(cx: &LateContext<'_>, ctxt: SyntaxContext, lhs: ExprOrIdent<'_>, rhs: &Expr<'_>) -> bool {
|
||||
match lhs {
|
||||
ExprOrIdent::Expr(expr) => eq_expr_value(cx, expr, rhs),
|
||||
ExprOrIdent::Expr(expr) => eq_expr_value(cx, ctxt, expr, rhs),
|
||||
ExprOrIdent::Ident(ident) => {
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = rhs.kind
|
||||
&& let [segment] = &path.segments
|
||||
@@ -284,10 +286,10 @@ fn check_xor_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
|
||||
if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt)
|
||||
&& let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt)
|
||||
&& let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt)
|
||||
&& eq_expr_value(cx, lhs0, rhs1)
|
||||
&& eq_expr_value(cx, lhs2, rhs1)
|
||||
&& eq_expr_value(cx, lhs1, rhs0)
|
||||
&& eq_expr_value(cx, lhs1, rhs2)
|
||||
&& eq_expr_value(cx, ctxt, lhs0, rhs1)
|
||||
&& eq_expr_value(cx, ctxt, lhs2, rhs1)
|
||||
&& eq_expr_value(cx, ctxt, lhs1, rhs0)
|
||||
&& eq_expr_value(cx, ctxt, lhs1, rhs2)
|
||||
&& s2.span.ctxt() == ctxt
|
||||
&& s3.span.ctxt() == ctxt
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -157,9 +157,11 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tc
|
||||
.filter_map(get_trait_info_from_bound)
|
||||
.for_each(|(trait_item_res, trait_item_segments, span)| {
|
||||
if let Some(self_segments) = self_bounds_map.get(&trait_item_res)
|
||||
&& SpanlessEq::new(cx)
|
||||
.paths_by_resolution()
|
||||
.eq_path_segments(self_segments, trait_item_segments)
|
||||
&& SpanlessEq::new(cx).paths_by_resolution().eq_path_segments(
|
||||
SyntaxContext::root(),
|
||||
self_segments,
|
||||
trait_item_segments,
|
||||
)
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
@@ -252,7 +254,7 @@ struct SpanlessTy<'cx, 'tcx> {
|
||||
impl PartialEq for SpanlessTy<'_, '_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let mut eq = SpanlessEq::new(self.cx);
|
||||
eq.inter_expr().eq_ty(self.ty, other.ty)
|
||||
eq.inter_expr(SyntaxContext::root()).eq_ty(self.ty, other.ty)
|
||||
}
|
||||
}
|
||||
impl Hash for SpanlessTy<'_, '_> {
|
||||
@@ -382,9 +384,11 @@ struct ComparableTraitRef<'a, 'tcx> {
|
||||
impl PartialEq for ComparableTraitRef<'_, '_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
SpanlessEq::eq_modifiers(self.modifiers, other.modifiers)
|
||||
&& SpanlessEq::new(self.cx)
|
||||
.paths_by_resolution()
|
||||
.eq_path(self.trait_ref.path, other.trait_ref.path)
|
||||
&& SpanlessEq::new(self.cx).paths_by_resolution().eq_path(
|
||||
SyntaxContext::root(),
|
||||
self.trait_ref.path,
|
||||
other.trait_ref.path,
|
||||
)
|
||||
}
|
||||
}
|
||||
impl Eq for ComparableTraitRef<'_, '_> {}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::SyntaxContext;
|
||||
|
||||
use super::EAGER_TRANSMUTE;
|
||||
|
||||
@@ -54,9 +55,9 @@ fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_
|
||||
lang_items.range_to_struct()
|
||||
].into_iter().any(|did| did == Some(receiver_adt.did())) =>
|
||||
{
|
||||
eq_expr_value(cx, local_expr, arg.peel_borrows())
|
||||
eq_expr_value(cx, SyntaxContext::root(), local_expr, arg.peel_borrows())
|
||||
},
|
||||
_ => eq_expr_value(cx, local_expr, expr),
|
||||
_ => eq_expr_value(cx, SyntaxContext::root(), local_expr, expr),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, SyntaxContext};
|
||||
|
||||
// TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std
|
||||
declare_clippy_lint! {
|
||||
@@ -64,15 +64,23 @@
|
||||
// Threads: https://github.com/rust-lang/rust-clippy/pull/7682#discussion_r710998368
|
||||
impl<'tcx> LateLintPass<'tcx> for UninitVec {
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
|
||||
if !block.span.in_external_macro(cx.tcx.sess.source_map()) {
|
||||
for w in block.stmts.windows(2) {
|
||||
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind {
|
||||
handle_uninit_vec_pair(cx, &w[0], expr);
|
||||
let ctxt = block.span.ctxt();
|
||||
if !ctxt.in_external_macro(cx.tcx.sess.source_map()) {
|
||||
for [stmt1, stmt2] in block.stmts.array_windows::<2>() {
|
||||
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt2.kind
|
||||
&& stmt1.span.ctxt() == ctxt
|
||||
&& stmt2.span.ctxt() == ctxt
|
||||
&& expr.span.ctxt() == ctxt
|
||||
{
|
||||
handle_uninit_vec_pair(cx, ctxt, stmt1, expr);
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) {
|
||||
handle_uninit_vec_pair(cx, stmt, expr);
|
||||
if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr)
|
||||
&& stmt.span.ctxt() == ctxt
|
||||
&& expr.span.ctxt() == ctxt
|
||||
{
|
||||
handle_uninit_vec_pair(cx, ctxt, stmt, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,12 +88,13 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
|
||||
|
||||
fn handle_uninit_vec_pair<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
maybe_init_or_reserve: &'tcx Stmt<'tcx>,
|
||||
maybe_set_len: &'tcx Expr<'tcx>,
|
||||
) {
|
||||
if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve)
|
||||
&& let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len)
|
||||
&& vec.location.eq_expr(cx, set_len_self)
|
||||
&& vec.location.eq_expr(cx, ctxt, set_len_self)
|
||||
&& let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind()
|
||||
&& let ty::Adt(_, args) = vec_ty.kind()
|
||||
// `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()`
|
||||
@@ -138,10 +147,10 @@ enum VecLocation<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> VecLocation<'tcx> {
|
||||
pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
pub fn eq_expr(self, cx: &LateContext<'tcx>, ctxt: SyntaxContext, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
match self {
|
||||
VecLocation::Local(hir_id) => expr.res_local_id() == Some(hir_id),
|
||||
VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr),
|
||||
VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(ctxt, self_expr, expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +89,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
&& let ExprKind::Path(..) = recv.kind
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let expr_ctxt = expr.span.ctxt();
|
||||
let ctxt = expr.span.ctxt();
|
||||
let and_then_snippets = get_and_then_snippets(
|
||||
cx,
|
||||
expr_ctxt,
|
||||
ctxt,
|
||||
call_cx.span,
|
||||
call_lint.span,
|
||||
call_sp.span,
|
||||
@@ -101,28 +101,24 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
);
|
||||
let mut sle = SpanlessEq::new(cx).deny_side_effects();
|
||||
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);
|
||||
sym::span_suggestion if sle.eq_expr(ctxt, call_sp, &span_call_args[0]) => {
|
||||
let snippets = span_suggestion_snippets(cx, ctxt, span_call_args, &mut app);
|
||||
suggest_suggestion(cx, expr, &and_then_snippets, &snippets, app);
|
||||
},
|
||||
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;
|
||||
sym::span_help if sle.eq_expr(ctxt, call_sp, &span_call_args[0]) => {
|
||||
let help_snippet = snippet_with_context(cx, span_call_args[1].span, ctxt, r#""...""#, &mut app).0;
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true, app);
|
||||
},
|
||||
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;
|
||||
sym::span_note if sle.eq_expr(ctxt, call_sp, &span_call_args[0]) => {
|
||||
let note_snippet = snippet_with_context(cx, span_call_args[1].span, ctxt, r#""...""#, &mut app).0;
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true, app);
|
||||
},
|
||||
sym::help => {
|
||||
let help_snippet =
|
||||
snippet_with_context(cx, span_call_args[0].span, expr_ctxt, r#""...""#, &mut app).0;
|
||||
let help_snippet = snippet_with_context(cx, span_call_args[0].span, ctxt, r#""...""#, &mut app).0;
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false, app);
|
||||
},
|
||||
sym::note => {
|
||||
let note_snippet =
|
||||
snippet_with_context(cx, span_call_args[0].span, expr_ctxt, r#""...""#, &mut app).0;
|
||||
let note_snippet = snippet_with_context(cx, span_call_args[0].span, ctxt, r#""...""#, &mut app).0;
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false, app);
|
||||
},
|
||||
_ => (),
|
||||
@@ -140,7 +136,7 @@ struct AndThenSnippets {
|
||||
|
||||
fn get_and_then_snippets(
|
||||
cx: &LateContext<'_>,
|
||||
expr_ctxt: SyntaxContext,
|
||||
ctxt: SyntaxContext,
|
||||
cx_span: Span,
|
||||
lint_span: Span,
|
||||
span_span: Span,
|
||||
@@ -150,7 +146,7 @@ fn get_and_then_snippets(
|
||||
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;
|
||||
let msg_snippet = snippet_with_context(cx, msg_span, ctxt, r#""...""#, app).0;
|
||||
|
||||
AndThenSnippets {
|
||||
cx: cx_snippet,
|
||||
@@ -168,12 +164,12 @@ struct SpanSuggestionSnippets {
|
||||
|
||||
fn span_suggestion_snippets<'hir>(
|
||||
cx: &LateContext<'_>,
|
||||
expr_ctxt: SyntaxContext,
|
||||
ctxt: SyntaxContext,
|
||||
span_call_args: &'hir [Expr<'hir>],
|
||||
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 help_snippet = snippet_with_context(cx, span_call_args[1].span, ctxt, r#""...""#, app).0;
|
||||
let sugg_snippet = snippet_with_context(cx, span_call_args[2].span, ctxt, "..", app).0;
|
||||
let applicability_snippet =
|
||||
snippet_with_applicability(cx, span_call_args[3].span, "Applicability::MachineApplicable", app);
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
|
||||
)
|
||||
{
|
||||
let lint_span = stmt1_span.to(stmt2_span);
|
||||
let ctxt = lint_span.ctxt();
|
||||
|
||||
// if recv1.is_diag_item(cx, sym1) && .. {
|
||||
// ..
|
||||
@@ -161,8 +162,8 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
|
||||
// }
|
||||
if let Some(first @ (span1, (cx1, recv1, _))) = extract_nested_is_diag_item(cx, cond1)
|
||||
&& let Some(second @ (span2, (cx2, recv2, _))) = extract_nested_is_diag_item(cx, cond2)
|
||||
&& eq_expr_value(cx, cx1, cx2)
|
||||
&& eq_expr_value(cx, recv1, recv2)
|
||||
&& eq_expr_value(cx, ctxt, cx1, cx2)
|
||||
&& eq_expr_value(cx, ctxt, recv1, recv2)
|
||||
{
|
||||
let recv_ty =
|
||||
with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs()));
|
||||
@@ -208,8 +209,8 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
|
||||
// }
|
||||
if let Some(first @ (span1, (tcx1, did1, _))) = extract_nested_is_diagnostic_item(cx, cond1)
|
||||
&& let Some(second @ (span2, (tcx2, did2, _))) = extract_nested_is_diagnostic_item(cx, cond2)
|
||||
&& eq_expr_value(cx, tcx1, tcx2)
|
||||
&& eq_expr_value(cx, did1, did2)
|
||||
&& eq_expr_value(cx, ctxt, tcx1, tcx2)
|
||||
&& eq_expr_value(cx, ctxt, did1, did2)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
@@ -264,11 +265,13 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
}
|
||||
|
||||
fn check_ors(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_>) {
|
||||
let ctxt = span.ctxt();
|
||||
|
||||
// recv1.is_diag_item(cx, sym1) || recv2.is_diag_item(cx, sym2)
|
||||
if let Some((cx1, recv1, sym1)) = extract_is_diag_item(cx, left)
|
||||
&& let Some((cx2, recv2, sym2)) = extract_is_diag_item(cx, right)
|
||||
&& eq_expr_value(cx, cx1, cx2)
|
||||
&& eq_expr_value(cx, recv1, recv2)
|
||||
&& eq_expr_value(cx, ctxt, cx1, cx2)
|
||||
&& eq_expr_value(cx, ctxt, recv1, recv2)
|
||||
{
|
||||
let recv_ty =
|
||||
with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs()));
|
||||
@@ -300,8 +303,8 @@ fn check_ors(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_>
|
||||
// cx.tcx.is_diagnostic_item(sym1, did) || cx.tcx.is_diagnostic_item(sym2, did)
|
||||
if let Some((tcx1, did1, sym1)) = extract_is_diagnostic_item(cx, left)
|
||||
&& let Some((tcx2, did2, sym2)) = extract_is_diagnostic_item(cx, right)
|
||||
&& eq_expr_value(cx, tcx1, tcx2)
|
||||
&& eq_expr_value(cx, did1, did2)
|
||||
&& eq_expr_value(cx, ctxt, tcx1, tcx2)
|
||||
&& eq_expr_value(cx, ctxt, did1, did2)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
@@ -328,11 +331,13 @@ fn check_ors(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_>
|
||||
}
|
||||
|
||||
fn check_ands(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_>) {
|
||||
let ctxt = span.ctxt();
|
||||
|
||||
// !recv1.is_diag_item(cx, sym1) && !recv2.is_diag_item(cx, sym2)
|
||||
if let Some((cx1, recv1, sym1)) = extract_is_diag_item(cx, left)
|
||||
&& let Some((cx2, recv2, sym2)) = extract_is_diag_item(cx, right)
|
||||
&& eq_expr_value(cx, cx1, cx2)
|
||||
&& eq_expr_value(cx, recv1, recv2)
|
||||
&& eq_expr_value(cx, ctxt, cx1, cx2)
|
||||
&& eq_expr_value(cx, ctxt, recv1, recv2)
|
||||
{
|
||||
let recv_ty =
|
||||
with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs()));
|
||||
@@ -364,8 +369,8 @@ fn check_ands(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_
|
||||
// !cx.tcx.is_diagnostic_item(sym1, did) && !cx.tcx.is_diagnostic_item(sym2, did)
|
||||
if let Some((tcx1, did1, sym1)) = extract_is_diagnostic_item(cx, left)
|
||||
&& let Some((tcx2, did2, sym2)) = extract_is_diagnostic_item(cx, right)
|
||||
&& eq_expr_value(cx, tcx1, tcx2)
|
||||
&& eq_expr_value(cx, did1, did2)
|
||||
&& eq_expr_value(cx, ctxt, tcx1, tcx2)
|
||||
&& eq_expr_value(cx, ctxt, did1, did2)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
@@ -400,9 +405,10 @@ fn check_if_chains<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, conds: Vec<&'t
|
||||
// ..
|
||||
// }
|
||||
let mut found = conds.iter().filter_map(|cond| extract_nested_is_diag_item(cx, cond));
|
||||
let ctxt = expr.span.ctxt();
|
||||
if let Some(first @ (_, (cx_1, recv1, _))) = found.next()
|
||||
&& let other =
|
||||
found.filter(|(_, (cx_, recv, _))| eq_expr_value(cx, cx_, cx_1) && eq_expr_value(cx, recv, recv1))
|
||||
&& let other = found
|
||||
.filter(|(_, (cx_, recv, _))| eq_expr_value(cx, ctxt, cx_, cx_1) && eq_expr_value(cx, ctxt, recv, recv1))
|
||||
&& let results = iter::once(first).chain(other).collect::<Vec<_>>()
|
||||
&& results.len() > 1
|
||||
{
|
||||
@@ -457,7 +463,8 @@ fn check_if_chains<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, conds: Vec<&'t
|
||||
.into_iter()
|
||||
.filter_map(|cond| extract_nested_is_diagnostic_item(cx, cond));
|
||||
if let Some(first @ (_, (tcx1, did1, _))) = found.next()
|
||||
&& let other = found.filter(|(_, (tcx, did, _))| eq_expr_value(cx, tcx, tcx1) && eq_expr_value(cx, did, did1))
|
||||
&& let other =
|
||||
found.filter(|(_, (tcx, did, _))| eq_expr_value(cx, ctxt, tcx, tcx1) && eq_expr_value(cx, ctxt, did, did1))
|
||||
&& let results = iter::once(first).chain(other).collect::<Vec<_>>()
|
||||
&& results.len() > 1
|
||||
{
|
||||
|
||||
@@ -4,9 +4,9 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.24.2"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
@@ -34,9 +34,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.75"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
@@ -44,7 +44,7 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -177,9 +177,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
@@ -203,9 +203,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -234,9 +234,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.4"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
@@ -245,9 +245,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -264,15 +264,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -425,75 +425,17 @@ version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
@@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
|
||||
|
||||
<!-- begin autogenerated nightly -->
|
||||
```
|
||||
nightly-2026-04-30
|
||||
nightly-2026-05-13
|
||||
```
|
||||
<!-- end autogenerated nightly -->
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
use crate::macros::macro_backtrace;
|
||||
use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context};
|
||||
use crate::{sym, tokenize_with_text};
|
||||
use core::mem;
|
||||
use rustc_ast::ast;
|
||||
use rustc_ast::ast::InlineAsmTemplatePiece;
|
||||
use rustc_data_structures::fx::{FxHasher, FxIndexMap};
|
||||
@@ -107,46 +108,57 @@ pub fn expr_fallback(
|
||||
|
||||
/// Use this method to wrap comparisons that may involve inter-expression context.
|
||||
/// See `self.locals`.
|
||||
pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
|
||||
pub fn inter_expr(&mut self, ctxt: SyntaxContext) -> HirEqInterExpr<'_, 'a, 'tcx> {
|
||||
HirEqInterExpr {
|
||||
inner: self,
|
||||
left_ctxt: SyntaxContext::root(),
|
||||
right_ctxt: SyntaxContext::root(),
|
||||
eval_ctxt: ctxt,
|
||||
prev_left_ctxt: ctxt,
|
||||
prev_right_ctxt: ctxt,
|
||||
locals: HirIdMap::default(),
|
||||
local_items: FxIndexMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
|
||||
self.inter_expr().eq_block(left, right)
|
||||
pub fn eq_block(&mut self, ctxt: SyntaxContext, left: &Block<'_>, right: &Block<'_>) -> bool {
|
||||
self.inter_expr(ctxt).eq_block(left, right)
|
||||
}
|
||||
|
||||
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
self.inter_expr().eq_expr(left, right)
|
||||
pub fn eq_expr(&mut self, ctxt: SyntaxContext, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
self.inter_expr(ctxt).eq_expr(left, right)
|
||||
}
|
||||
|
||||
pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
|
||||
self.inter_expr().eq_path(left, right)
|
||||
pub fn eq_path(&mut self, ctxt: SyntaxContext, left: &Path<'_>, right: &Path<'_>) -> bool {
|
||||
self.inter_expr(ctxt).eq_path(left, right)
|
||||
}
|
||||
|
||||
pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
|
||||
self.inter_expr().eq_path_segment(left, right)
|
||||
pub fn eq_path_segment(&mut self, ctxt: SyntaxContext, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
|
||||
self.inter_expr(ctxt).eq_path_segment(left, right)
|
||||
}
|
||||
|
||||
pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
|
||||
self.inter_expr().eq_path_segments(left, right)
|
||||
pub fn eq_path_segments(
|
||||
&mut self,
|
||||
ctxt: SyntaxContext,
|
||||
left: &[PathSegment<'_>],
|
||||
right: &[PathSegment<'_>],
|
||||
) -> bool {
|
||||
self.inter_expr(ctxt).eq_path_segments(left, right)
|
||||
}
|
||||
|
||||
pub fn eq_modifiers(left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool {
|
||||
std::mem::discriminant(&left.constness) == std::mem::discriminant(&right.constness)
|
||||
&& std::mem::discriminant(&left.polarity) == std::mem::discriminant(&right.polarity)
|
||||
mem::discriminant(&left.constness) == mem::discriminant(&right.constness)
|
||||
&& mem::discriminant(&left.polarity) == mem::discriminant(&right.polarity)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HirEqInterExpr<'a, 'b, 'tcx> {
|
||||
inner: &'a mut SpanlessEq<'b, 'tcx>,
|
||||
left_ctxt: SyntaxContext,
|
||||
right_ctxt: SyntaxContext,
|
||||
|
||||
/// The root context to view each side from.
|
||||
eval_ctxt: SyntaxContext,
|
||||
|
||||
// Optimization to avoid rechecking the context of desugarings.
|
||||
prev_left_ctxt: SyntaxContext,
|
||||
prev_right_ctxt: SyntaxContext,
|
||||
|
||||
// When binding are declared, the binding ID in the left expression is mapped to the one on the
|
||||
// right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
|
||||
@@ -156,7 +168,17 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> {
|
||||
}
|
||||
|
||||
impl HirEqInterExpr<'_, '_, '_> {
|
||||
pub fn set_eval_ctxt(&mut self, ctxt: SyntaxContext) {
|
||||
self.eval_ctxt = ctxt;
|
||||
self.prev_left_ctxt = ctxt;
|
||||
self.prev_right_ctxt = ctxt;
|
||||
}
|
||||
|
||||
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
|
||||
if self.check_ctxt(left.span.ctxt(), right.span.ctxt()) == Some(false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
match (&left.kind, &right.kind) {
|
||||
(StmtKind::Let(l), StmtKind::Let(r)) => {
|
||||
// This additional check ensures that the type of the locals are equivalent even if the init
|
||||
@@ -372,15 +394,16 @@ fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
|
||||
}
|
||||
let lspan = left.span.data();
|
||||
let rspan = right.span.data();
|
||||
if lspan.ctxt != SyntaxContext::root() && rspan.ctxt != SyntaxContext::root() {
|
||||
// Don't try to check in between statements inside macros.
|
||||
return over(left.stmts, right.stmts, |left, right| self.eq_stmt(left, right))
|
||||
&& both(left.expr.as_ref(), right.expr.as_ref(), |left, right| {
|
||||
self.eq_expr(left, right)
|
||||
});
|
||||
}
|
||||
if lspan.ctxt != rspan.ctxt {
|
||||
return false;
|
||||
match self.check_ctxt(lspan.ctxt, rspan.ctxt) {
|
||||
Some(false) => return false,
|
||||
None if self.eval_ctxt.is_root() => {},
|
||||
_ => {
|
||||
// Don't try to check in between statements inside macros.
|
||||
return over(left.stmts, right.stmts, |left, right| self.eq_stmt(left, right))
|
||||
&& both(left.expr.as_ref(), right.expr.as_ref(), |left, right| {
|
||||
self.eq_expr(left, right)
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
let mut lstart = lspan.lo;
|
||||
@@ -475,26 +498,28 @@ pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results
|
||||
&& typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right)
|
||||
&& let (Some(l), Some(r)) = (
|
||||
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs)
|
||||
.eval_local(left, self.left_ctxt),
|
||||
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs)
|
||||
.eval_local(right, self.right_ctxt),
|
||||
)
|
||||
&& l == r
|
||||
{
|
||||
return true;
|
||||
match self.check_ctxt(left.span.ctxt(), right.span.ctxt()) {
|
||||
None => {
|
||||
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results
|
||||
&& typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right)
|
||||
&& let (Some(l), Some(r)) = (
|
||||
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs)
|
||||
.eval_local(left, self.eval_ctxt),
|
||||
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs)
|
||||
.eval_local(right, self.eval_ctxt),
|
||||
)
|
||||
&& l == r
|
||||
{
|
||||
return true;
|
||||
}
|
||||
},
|
||||
Some(false) => return false,
|
||||
Some(true) => {},
|
||||
}
|
||||
|
||||
let is_eq = match (
|
||||
reduce_exprkind(self.inner.cx, &left.kind),
|
||||
reduce_exprkind(self.inner.cx, &right.kind),
|
||||
reduce_exprkind(self.inner.cx, self.eval_ctxt, &left.kind),
|
||||
reduce_exprkind(self.inner.cx, self.eval_ctxt, &right.kind),
|
||||
) {
|
||||
(ExprKind::AddrOf(lb, l_mut, le), ExprKind::AddrOf(rb, r_mut, re)) => {
|
||||
lb == rb && l_mut == r_mut && self.eq_expr(le, re)
|
||||
@@ -542,7 +567,12 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
&& both(l.ty.as_ref(), r.ty.as_ref(), |l, r| self.eq_ty(l, r))
|
||||
&& self.eq_expr(l.init, r.init)
|
||||
},
|
||||
(ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node,
|
||||
(ExprKind::Lit(l), ExprKind::Lit(r)) => {
|
||||
if self.check_ctxt(l.span.ctxt(), r.span.ctxt()) == Some(false) {
|
||||
return false;
|
||||
}
|
||||
l.node == r.node
|
||||
},
|
||||
(ExprKind::Loop(lb, ll, lls, _), ExprKind::Loop(rb, rl, rls, _)) => {
|
||||
lls == rls && self.eq_block(lb, rb)
|
||||
&& both(ll.as_ref(), rl.as_ref(), |l, r| l.ident.name == r.ident.name)
|
||||
@@ -673,7 +703,7 @@ fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> b
|
||||
}
|
||||
|
||||
fn eq_const_arg(&mut self, left: &ConstArg<'_>, right: &ConstArg<'_>) -> bool {
|
||||
if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) {
|
||||
if self.check_ctxt(left.span.ctxt(), right.span.ctxt()) == Some(false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -892,46 +922,72 @@ fn eq_assoc_eq_constraint(&mut self, left: &AssocItemConstraint<'_>, right: &Ass
|
||||
|| both_some_and(left.ct(), right.ct(), |l, r| self.eq_const_arg(l, r)))
|
||||
}
|
||||
|
||||
fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool {
|
||||
if self.left_ctxt == left && self.right_ctxt == right {
|
||||
return true;
|
||||
} else if self.left_ctxt == left || self.right_ctxt == right {
|
||||
// Only one context has changed. This can only happen if the two nodes are written differently.
|
||||
return false;
|
||||
} else if left != SyntaxContext::root() {
|
||||
/// Checks whether either operand is within a macro context, and if so, whether the macro calls
|
||||
/// are equal.
|
||||
fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> Option<bool> {
|
||||
let prev_left = mem::replace(&mut self.prev_left_ctxt, left);
|
||||
let prev_right = mem::replace(&mut self.prev_right_ctxt, right);
|
||||
|
||||
if left == self.eval_ctxt && right == self.eval_ctxt {
|
||||
None
|
||||
} else if left == prev_left && right == prev_right {
|
||||
// Same as the previous context, no need to recheck anything
|
||||
Some(true)
|
||||
} else if left == prev_left
|
||||
|| right == prev_right
|
||||
|| left == self.eval_ctxt
|
||||
|| right == self.eval_ctxt
|
||||
|| left.is_root()
|
||||
|| right.is_root()
|
||||
{
|
||||
// Either only one context changed, or at least one context is a parent of the
|
||||
// evaluation context.
|
||||
// Unfortunately we can't get a span of a metavariable so we have to treat the
|
||||
// second case as unequal.
|
||||
Some(false)
|
||||
} else {
|
||||
// Walk each context in lockstep up to the evaluation context checking that each
|
||||
// expansion has the same kind.
|
||||
let mut left_data = left.outer_expn_data();
|
||||
let mut right_data = right.outer_expn_data();
|
||||
loop {
|
||||
use TokenKind::{BlockComment, LineComment, Whitespace};
|
||||
if left_data.macro_def_id != right_data.macro_def_id
|
||||
|| (matches!(
|
||||
left_data.kind,
|
||||
ExpnKind::Macro(MacroKind::Bang, name)
|
||||
if name == sym::cfg || name == sym::option_env
|
||||
) && !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| {
|
||||
!matches!(t, Whitespace | LineComment { .. } | BlockComment { .. })
|
||||
}))
|
||||
if left_data.macro_def_id != right_data.macro_def_id || left_data.kind != right_data.kind {
|
||||
return Some(false);
|
||||
}
|
||||
let left = left_data.call_site.ctxt();
|
||||
let right = right_data.call_site.ctxt();
|
||||
if left == self.eval_ctxt && right == self.eval_ctxt {
|
||||
// Finally if the outermost expansion is a macro call, check if the
|
||||
// tokens are the same.
|
||||
if let ExpnKind::Macro(MacroKind::Bang, _) = left_data.kind {
|
||||
return Some(eq_span_tokens(
|
||||
self.inner.cx,
|
||||
left_data.call_site,
|
||||
right_data.call_site,
|
||||
|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. }),
|
||||
));
|
||||
}
|
||||
return Some(true);
|
||||
}
|
||||
if left == prev_left && right == prev_right {
|
||||
return Some(true);
|
||||
}
|
||||
if left == prev_left
|
||||
|| right == prev_right
|
||||
|| left == self.eval_ctxt
|
||||
|| right == self.eval_ctxt
|
||||
|| left.is_root()
|
||||
|| right.is_root()
|
||||
{
|
||||
// Either a different chain of macro calls, or different arguments to the `cfg` macro.
|
||||
return false;
|
||||
// Either there's a different number of expansions, or at least one context is
|
||||
// a parent of the evaluation context.
|
||||
return Some(false);
|
||||
}
|
||||
let left_ctxt = left_data.call_site.ctxt();
|
||||
let right_ctxt = right_data.call_site.ctxt();
|
||||
if left_ctxt == SyntaxContext::root() && right_ctxt == SyntaxContext::root() {
|
||||
break;
|
||||
}
|
||||
if left_ctxt == SyntaxContext::root() || right_ctxt == SyntaxContext::root() {
|
||||
// Different lengths for the expansion stack. This can only happen if nodes are written differently,
|
||||
// or shouldn't be compared to start with.
|
||||
return false;
|
||||
}
|
||||
left_data = left_ctxt.outer_expn_data();
|
||||
right_data = right_ctxt.outer_expn_data();
|
||||
left_data = left.outer_expn_data();
|
||||
right_data = right.outer_expn_data();
|
||||
}
|
||||
}
|
||||
self.left_ctxt = left;
|
||||
self.right_ctxt = right;
|
||||
true
|
||||
}
|
||||
|
||||
fn swap_binop<'a>(
|
||||
@@ -970,7 +1026,11 @@ fn swap_binop<'a>(
|
||||
}
|
||||
|
||||
/// Some simple reductions like `{ return }` => `return`
|
||||
fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
|
||||
fn reduce_exprkind<'hir>(
|
||||
cx: &LateContext<'_>,
|
||||
eval_ctxt: SyntaxContext,
|
||||
kind: &'hir ExprKind<'hir>,
|
||||
) -> &'hir ExprKind<'hir> {
|
||||
if let ExprKind::Block(block, _) = kind {
|
||||
match (block.stmts, block.expr) {
|
||||
// From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
|
||||
@@ -978,17 +1038,20 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
|
||||
([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
|
||||
// `{}` => `()`
|
||||
([], None)
|
||||
if block.span.check_source_text(cx, |src| {
|
||||
tokenize(src, FrontmatterAllowed::No)
|
||||
.map(|t| t.kind)
|
||||
.filter(|t| {
|
||||
!matches!(
|
||||
t,
|
||||
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
|
||||
)
|
||||
})
|
||||
.eq([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied())
|
||||
}) =>
|
||||
if block.span.ctxt() != eval_ctxt
|
||||
|| block.span.check_source_text(cx, |src| {
|
||||
tokenize(src, FrontmatterAllowed::No)
|
||||
.map(|t| t.kind)
|
||||
.filter(|t| {
|
||||
!matches!(
|
||||
t,
|
||||
TokenKind::LineComment { .. }
|
||||
| TokenKind::BlockComment { .. }
|
||||
| TokenKind::Whitespace
|
||||
)
|
||||
})
|
||||
.eq([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied())
|
||||
}) =>
|
||||
{
|
||||
&ExprKind::Tup(&[])
|
||||
},
|
||||
@@ -1039,8 +1102,13 @@ pub fn count_eq<X: Sized>(
|
||||
}
|
||||
|
||||
/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
|
||||
pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
|
||||
///
|
||||
/// The context argument is the context used to view the two expressions. e.g. when comparing the
|
||||
/// two arguments in `f(m!(1), m!(2))` the context of the call expression should be used. This is
|
||||
/// needed to handle the case where two macros expand to the same thing, but the arguments are
|
||||
/// different.
|
||||
pub fn eq_expr_value(cx: &LateContext<'_>, ctxt: SyntaxContext, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
SpanlessEq::new(cx).deny_side_effects().eq_expr(ctxt, left, right)
|
||||
}
|
||||
|
||||
/// Returns the segments of a path that might have generic parameters.
|
||||
@@ -1104,7 +1172,7 @@ pub fn hash_block(&mut self, b: &Block<'_>) {
|
||||
self.hash_expr(e);
|
||||
}
|
||||
|
||||
std::mem::discriminant(&b.rules).hash(&mut self.s);
|
||||
mem::discriminant(&b.rules).hash(&mut self.s);
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
@@ -1120,11 +1188,11 @@ pub fn hash_expr(&mut self, e: &Expr<'_>) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::mem::discriminant(&e.kind).hash(&mut self.s);
|
||||
mem::discriminant(&e.kind).hash(&mut self.s);
|
||||
|
||||
match &e.kind {
|
||||
ExprKind::AddrOf(kind, m, e) => {
|
||||
std::mem::discriminant(kind).hash(&mut self.s);
|
||||
mem::discriminant(kind).hash(&mut self.s);
|
||||
m.hash(&mut self.s);
|
||||
self.hash_expr(e);
|
||||
},
|
||||
@@ -1141,7 +1209,7 @@ pub fn hash_expr(&mut self, e: &Expr<'_>) {
|
||||
self.hash_expr(r);
|
||||
},
|
||||
ExprKind::AssignOp(o, l, r) => {
|
||||
std::mem::discriminant(&o.node).hash(&mut self.s);
|
||||
mem::discriminant(&o.node).hash(&mut self.s);
|
||||
self.hash_expr(l);
|
||||
self.hash_expr(r);
|
||||
},
|
||||
@@ -1152,7 +1220,7 @@ pub fn hash_expr(&mut self, e: &Expr<'_>) {
|
||||
self.hash_block(b);
|
||||
},
|
||||
ExprKind::Binary(op, l, r) => {
|
||||
std::mem::discriminant(&op.node).hash(&mut self.s);
|
||||
mem::discriminant(&op.node).hash(&mut self.s);
|
||||
self.hash_expr(l);
|
||||
self.hash_expr(r);
|
||||
},
|
||||
@@ -1175,7 +1243,7 @@ pub fn hash_expr(&mut self, e: &Expr<'_>) {
|
||||
ExprKind::Closure(Closure {
|
||||
capture_clause, body, ..
|
||||
}) => {
|
||||
std::mem::discriminant(capture_clause).hash(&mut self.s);
|
||||
mem::discriminant(capture_clause).hash(&mut self.s);
|
||||
// closures inherit TypeckResults
|
||||
self.hash_expr(self.cx.tcx.hir_body(*body).value);
|
||||
},
|
||||
@@ -1326,11 +1394,11 @@ pub fn hash_expr(&mut self, e: &Expr<'_>) {
|
||||
self.hash_expr(expr);
|
||||
},
|
||||
ExprKind::Unary(l_op, le) => {
|
||||
std::mem::discriminant(l_op).hash(&mut self.s);
|
||||
mem::discriminant(l_op).hash(&mut self.s);
|
||||
self.hash_expr(le);
|
||||
},
|
||||
ExprKind::UnsafeBinderCast(kind, expr, ty) => {
|
||||
std::mem::discriminant(kind).hash(&mut self.s);
|
||||
mem::discriminant(kind).hash(&mut self.s);
|
||||
self.hash_expr(expr);
|
||||
if let Some(ty) = ty {
|
||||
self.hash_ty(ty);
|
||||
@@ -1363,7 +1431,7 @@ pub fn hash_qpath(&mut self, p: &QPath<'_>) {
|
||||
}
|
||||
|
||||
pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) {
|
||||
std::mem::discriminant(&lit.kind).hash(&mut self.s);
|
||||
mem::discriminant(&lit.kind).hash(&mut self.s);
|
||||
match &lit.kind {
|
||||
PatExprKind::Lit { lit, negated } => {
|
||||
lit.node.hash(&mut self.s);
|
||||
@@ -1374,7 +1442,7 @@ pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) {
|
||||
}
|
||||
|
||||
pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) {
|
||||
std::mem::discriminant(&pat.kind).hash(&mut self.s);
|
||||
mem::discriminant(&pat.kind).hash(&mut self.s);
|
||||
match pat.kind {
|
||||
TyPatKind::Range(s, e) => {
|
||||
self.hash_const_arg(s);
|
||||
@@ -1390,16 +1458,16 @@ pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) {
|
||||
}
|
||||
|
||||
pub fn hash_pat(&mut self, pat: &Pat<'_>) {
|
||||
std::mem::discriminant(&pat.kind).hash(&mut self.s);
|
||||
mem::discriminant(&pat.kind).hash(&mut self.s);
|
||||
match &pat.kind {
|
||||
PatKind::Missing => unreachable!(),
|
||||
PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => {
|
||||
std::mem::discriminant(by_ref).hash(&mut self.s);
|
||||
mem::discriminant(by_ref).hash(&mut self.s);
|
||||
if let ByRef::Yes(pi, mu) = by_ref {
|
||||
std::mem::discriminant(pi).hash(&mut self.s);
|
||||
std::mem::discriminant(mu).hash(&mut self.s);
|
||||
mem::discriminant(pi).hash(&mut self.s);
|
||||
mem::discriminant(mu).hash(&mut self.s);
|
||||
}
|
||||
std::mem::discriminant(mutability).hash(&mut self.s);
|
||||
mem::discriminant(mutability).hash(&mut self.s);
|
||||
if let Some(pat) = pat {
|
||||
self.hash_pat(pat);
|
||||
}
|
||||
@@ -1418,12 +1486,12 @@ pub fn hash_pat(&mut self, pat: &Pat<'_>) {
|
||||
if let Some(e) = e {
|
||||
self.hash_pat_expr(e);
|
||||
}
|
||||
std::mem::discriminant(i).hash(&mut self.s);
|
||||
mem::discriminant(i).hash(&mut self.s);
|
||||
},
|
||||
PatKind::Ref(pat, pi, mu) => {
|
||||
self.hash_pat(pat);
|
||||
std::mem::discriminant(pi).hash(&mut self.s);
|
||||
std::mem::discriminant(mu).hash(&mut self.s);
|
||||
mem::discriminant(pi).hash(&mut self.s);
|
||||
mem::discriminant(mu).hash(&mut self.s);
|
||||
},
|
||||
PatKind::Guard(pat, guard) => {
|
||||
self.hash_pat(pat);
|
||||
@@ -1492,12 +1560,12 @@ pub fn hash_path(&mut self, path: &Path<'_>) {
|
||||
|
||||
pub fn hash_modifiers(&mut self, modifiers: TraitBoundModifiers) {
|
||||
let TraitBoundModifiers { constness, polarity } = modifiers;
|
||||
std::mem::discriminant(&polarity).hash(&mut self.s);
|
||||
std::mem::discriminant(&constness).hash(&mut self.s);
|
||||
mem::discriminant(&polarity).hash(&mut self.s);
|
||||
mem::discriminant(&constness).hash(&mut self.s);
|
||||
}
|
||||
|
||||
pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
|
||||
std::mem::discriminant(&b.kind).hash(&mut self.s);
|
||||
mem::discriminant(&b.kind).hash(&mut self.s);
|
||||
|
||||
match &b.kind {
|
||||
StmtKind::Let(local) => {
|
||||
@@ -1518,14 +1586,14 @@ pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
|
||||
|
||||
pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
|
||||
lifetime.ident.name.hash(&mut self.s);
|
||||
std::mem::discriminant(&lifetime.kind).hash(&mut self.s);
|
||||
mem::discriminant(&lifetime.kind).hash(&mut self.s);
|
||||
if let LifetimeKind::Param(param_id) = lifetime.kind {
|
||||
param_id.hash(&mut self.s);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_ty(&mut self, ty: &Ty<'_>) {
|
||||
std::mem::discriminant(&ty.kind).hash(&mut self.s);
|
||||
mem::discriminant(&ty.kind).hash(&mut self.s);
|
||||
self.hash_tykind(&ty.kind);
|
||||
}
|
||||
|
||||
@@ -1564,7 +1632,7 @@ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
|
||||
for arg in fn_ptr.decl.inputs {
|
||||
self.hash_ty(arg);
|
||||
}
|
||||
std::mem::discriminant(&fn_ptr.decl.output).hash(&mut self.s);
|
||||
mem::discriminant(&fn_ptr.decl.output).hash(&mut self.s);
|
||||
match fn_ptr.decl.output {
|
||||
FnRetTy::DefaultReturn(_) => {},
|
||||
FnRetTy::Return(ty) => {
|
||||
|
||||
@@ -469,19 +469,23 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Opti
|
||||
/// this method will return a tuple, composed of a `Vec`
|
||||
/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
|
||||
/// and an `Expr` for root of them, `v`
|
||||
fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
|
||||
fn projection_stack<'a, 'hir>(
|
||||
mut e: &'a Expr<'hir>,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<(Vec<&'a Expr<'hir>>, &'a Expr<'hir>)> {
|
||||
let mut result = vec![];
|
||||
let root = loop {
|
||||
match e.kind {
|
||||
ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
|
||||
ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) if e.span.ctxt() == ctxt => {
|
||||
result.push(e);
|
||||
e = ep;
|
||||
},
|
||||
ExprKind::Index(..) | ExprKind::Field(..) => return None,
|
||||
_ => break e,
|
||||
}
|
||||
};
|
||||
result.reverse();
|
||||
(result, root)
|
||||
Some((result, root))
|
||||
}
|
||||
|
||||
/// Gets the mutability of the custom deref adjustment, if any.
|
||||
@@ -499,10 +503,14 @@ pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Optio
|
||||
|
||||
/// Checks if two expressions can be mutably borrowed simultaneously
|
||||
/// and they aren't dependent on borrowing same thing twice
|
||||
pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
|
||||
let (s1, r1) = projection_stack(e1);
|
||||
let (s2, r2) = projection_stack(e2);
|
||||
if !eq_expr_value(cx, r1, r2) {
|
||||
pub fn can_mut_borrow_both(cx: &LateContext<'_>, ctxt: SyntaxContext, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
|
||||
let Some((s1, r1)) = projection_stack(e1, ctxt) else {
|
||||
return false;
|
||||
};
|
||||
let Some((s2, r2)) = projection_stack(e2, ctxt) else {
|
||||
return false;
|
||||
};
|
||||
if !eq_expr_value(cx, ctxt, r1, r2) {
|
||||
return true;
|
||||
}
|
||||
if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
|
||||
@@ -520,11 +528,6 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -
|
||||
return true;
|
||||
}
|
||||
},
|
||||
(ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
|
||||
if !eq_expr_value(cx, i1, i2) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
@@ -1744,13 +1747,7 @@ pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool
|
||||
pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
||||
tcx.hir_parent_owner_iter(id)
|
||||
.filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
|
||||
.any(|(id, _)| {
|
||||
find_attr!(
|
||||
tcx,
|
||||
id.def_id,
|
||||
AutomaticallyDerived
|
||||
)
|
||||
})
|
||||
.any(|(id, _)| find_attr!(tcx, id.def_id, AutomaticallyDerived))
|
||||
}
|
||||
|
||||
/// Checks if the given `DefId` matches the `libc` item.
|
||||
|
||||
@@ -134,10 +134,11 @@ fn check_rvalue<'tcx>(
|
||||
) -> McfResult {
|
||||
match rvalue {
|
||||
Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
|
||||
Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::Reborrow(_, _, place) | Rvalue::RawPtr(_, place) => {
|
||||
check_place(cx, *place, span, body, msrv)
|
||||
},
|
||||
Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv),
|
||||
Rvalue::Discriminant(place)
|
||||
| Rvalue::Ref(_, _, place)
|
||||
| Rvalue::Reborrow(_, _, place)
|
||||
| Rvalue::RawPtr(_, place)
|
||||
| Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv),
|
||||
Rvalue::Repeat(operand, _)
|
||||
| Rvalue::Use(operand, _)
|
||||
| Rvalue::WrapUnsafeBinder(operand, _)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[toolchain]
|
||||
# begin autogenerated nightly
|
||||
channel = "nightly-2026-04-30"
|
||||
channel = "nightly-2026-05-13"
|
||||
# end autogenerated nightly
|
||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
profile = "minimal"
|
||||
|
||||
@@ -89,11 +89,33 @@ fn internal_extern_flags() -> Vec<String> {
|
||||
help: Try adding to dev-dependencies in Cargo.toml\n\
|
||||
help: Be sure to also add `extern crate ...;` to tests/compile-test.rs",
|
||||
);
|
||||
crates
|
||||
|
||||
let mut args: Vec<String> = crates
|
||||
.into_iter()
|
||||
.map(|(name, path)| format!("--extern={name}={path}"))
|
||||
.chain([format!("-Ldependency={}", deps_path.display())])
|
||||
.collect()
|
||||
.collect();
|
||||
|
||||
if deps_path.ends_with("deps") {
|
||||
args.push(format!("-Ldependency={}", deps_path.display()));
|
||||
} else {
|
||||
// If the dep_path does not point to `/deps` it very likely means Cargo is using the v2 build-dir
|
||||
// layout
|
||||
assert!(deps_path.ends_with("out"));
|
||||
|
||||
// Get a path to `target/<platform-profile>/build`
|
||||
let build_dir = {
|
||||
let mut d = deps_path.to_path_buf();
|
||||
d.pop(); // remove `out`
|
||||
d.pop(); // remove `<hash>`
|
||||
d.pop(); // remove `<pkgname>`
|
||||
d
|
||||
};
|
||||
|
||||
let out_dirs = discover_out_dirs(&build_dir);
|
||||
args.extend(out_dirs.iter().map(|path| format!("-Ldependency={}", path.display())));
|
||||
}
|
||||
|
||||
args
|
||||
}
|
||||
|
||||
// whether to run internal tests or not
|
||||
@@ -214,8 +236,21 @@ fn base_config(&self, test_dir: &str, mandatory_annotations: bool) -> Config {
|
||||
config.program.envs.push(("RUSTC_ICE".into(), Some("0".into())));
|
||||
|
||||
if let Some(host_libs) = option_env!("HOST_LIBS") {
|
||||
let dep = format!("-Ldependency={}", Path::new(host_libs).join("deps").display());
|
||||
config.program.args.push(dep.into());
|
||||
let deps_dir = Path::new(host_libs).join("deps");
|
||||
|
||||
if deps_dir.exists() {
|
||||
let dep = format!("-Ldependency={}", deps_dir.display());
|
||||
config.program.args.push(dep.into());
|
||||
} else {
|
||||
// If `/deps` does not exist, assume Cargo v2 build-dir layout
|
||||
let build_dir = Path::new(host_libs).join("build");
|
||||
let dependencies = discover_out_dirs(&build_dir);
|
||||
|
||||
for dep in dependencies {
|
||||
let dep = format!("-Ldependency={}", dep.display());
|
||||
config.program.args.push(dep.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(sysroot) = option_env!("TEST_SYSROOT") {
|
||||
config.program.args.push(format!("--sysroot={sysroot}").into());
|
||||
@@ -648,3 +683,20 @@ fn applicability_str(&self) -> &str {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets all of the `out` dirs in a given Cargo `build-dir/<profile>/build` dir.
|
||||
fn discover_out_dirs(dir: &Path) -> Vec<PathBuf> {
|
||||
if !dir.exists() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let read_dir = |path: &Path| path.read_dir().ok().into_iter().flatten().filter_map(Result::ok);
|
||||
dir.read_dir()
|
||||
.unwrap_or_else(|e| panic!("Couldn't read {}: {}", dir.display(), e))
|
||||
.map(|e| e.unwrap())
|
||||
.flat_map(|e| read_dir(&e.path()))
|
||||
.flat_map(|e| read_dir(&e.path()))
|
||||
.map(|e| e.path())
|
||||
.filter(|path| path.ends_with("out"))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
#![warn(clippy::inline_trait_bounds)]
|
||||
|
||||
// Free functions
|
||||
|
||||
fn inline_simple<T>() where T: Clone {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn inline_multiple<T, U>() where T: Clone + Copy, U: core::fmt::Debug {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn inline_lifetime<'a, 'b>(x: &'a str, y: &'b str) -> &'b str where 'a: 'b {
|
||||
//~^ inline_trait_bounds
|
||||
y
|
||||
}
|
||||
|
||||
#[allow(clippy::multiple_bound_locations)]
|
||||
fn inline_with_where<T>()
|
||||
//~^ inline_trait_bounds
|
||||
where
|
||||
T: core::fmt::Debug, T: Clone,
|
||||
{
|
||||
}
|
||||
|
||||
fn inline_with_const<T, const N: usize>() where T: Clone {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn inline_with_return<T>(val: T) -> T where T: Clone {
|
||||
//~^ inline_trait_bounds
|
||||
val
|
||||
}
|
||||
|
||||
// Trait methods
|
||||
|
||||
trait MyTrait {
|
||||
fn trait_method_inline<T>(&self) where T: Clone;
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn trait_method_default<T>(&self) where T: Clone + Copy {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn trait_method_where<T>(&self)
|
||||
where
|
||||
T: Clone;
|
||||
}
|
||||
|
||||
// Impl methods
|
||||
|
||||
struct MyStruct;
|
||||
|
||||
impl MyStruct {
|
||||
fn impl_method_inline<T>(&self) where T: Clone {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn impl_method_multiple<T, U>(&self) where T: Clone, U: core::fmt::Debug {}
|
||||
//~^ inline_trait_bounds
|
||||
}
|
||||
|
||||
impl MyTrait for MyStruct {
|
||||
fn trait_method_inline<T>(&self) where T: Clone {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn trait_method_default<T>(&self) where T: Clone + Copy {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn trait_method_where<T>(&self)
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Should NOT lint
|
||||
|
||||
fn where_only<T>()
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
}
|
||||
|
||||
fn no_bounds<T, U>() {}
|
||||
|
||||
fn no_generics() {}
|
||||
|
||||
struct InlineStruct<T: Clone>(T);
|
||||
|
||||
enum InlineEnum<T: Clone> {
|
||||
A(T),
|
||||
}
|
||||
|
||||
#[allow(invalid_type_param_default)]
|
||||
//~v inline_trait_bounds
|
||||
fn with_default_value<T = u32>(x: T) -> T where T: Clone {
|
||||
x
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#![warn(clippy::inline_trait_bounds)]
|
||||
|
||||
// Free functions
|
||||
|
||||
fn inline_simple<T: Clone>() {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn inline_multiple<T: Clone + Copy, U: core::fmt::Debug>() {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn inline_lifetime<'a: 'b, 'b>(x: &'a str, y: &'b str) -> &'b str {
|
||||
//~^ inline_trait_bounds
|
||||
y
|
||||
}
|
||||
|
||||
#[allow(clippy::multiple_bound_locations)]
|
||||
fn inline_with_where<T: Clone>()
|
||||
//~^ inline_trait_bounds
|
||||
where
|
||||
T: core::fmt::Debug,
|
||||
{
|
||||
}
|
||||
|
||||
fn inline_with_const<T: Clone, const N: usize>() {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn inline_with_return<T: Clone>(val: T) -> T {
|
||||
//~^ inline_trait_bounds
|
||||
val
|
||||
}
|
||||
|
||||
// Trait methods
|
||||
|
||||
trait MyTrait {
|
||||
fn trait_method_inline<T: Clone>(&self);
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn trait_method_default<T: Clone + Copy>(&self) {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn trait_method_where<T>(&self)
|
||||
where
|
||||
T: Clone;
|
||||
}
|
||||
|
||||
// Impl methods
|
||||
|
||||
struct MyStruct;
|
||||
|
||||
impl MyStruct {
|
||||
fn impl_method_inline<T: Clone>(&self) {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn impl_method_multiple<T: Clone, U: core::fmt::Debug>(&self) {}
|
||||
//~^ inline_trait_bounds
|
||||
}
|
||||
|
||||
impl MyTrait for MyStruct {
|
||||
fn trait_method_inline<T: Clone>(&self) {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn trait_method_default<T: Clone + Copy>(&self) {}
|
||||
//~^ inline_trait_bounds
|
||||
|
||||
fn trait_method_where<T>(&self)
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Should NOT lint
|
||||
|
||||
fn where_only<T>()
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
}
|
||||
|
||||
fn no_bounds<T, U>() {}
|
||||
|
||||
fn no_generics() {}
|
||||
|
||||
struct InlineStruct<T: Clone>(T);
|
||||
|
||||
enum InlineEnum<T: Clone> {
|
||||
A(T),
|
||||
}
|
||||
|
||||
#[allow(invalid_type_param_default)]
|
||||
//~v inline_trait_bounds
|
||||
fn with_default_value<T: Clone = u32>(x: T) -> T {
|
||||
x
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:5:17
|
||||
|
|
||||
LL | fn inline_simple<T: Clone>() {}
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::inline-trait-bounds` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::inline_trait_bounds)]`
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn inline_simple<T: Clone>() {}
|
||||
LL + fn inline_simple<T>() where T: Clone {}
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:8:19
|
||||
|
|
||||
LL | fn inline_multiple<T: Clone + Copy, U: core::fmt::Debug>() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn inline_multiple<T: Clone + Copy, U: core::fmt::Debug>() {}
|
||||
LL + fn inline_multiple<T, U>() where T: Clone + Copy, U: core::fmt::Debug {}
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:11:19
|
||||
|
|
||||
LL | fn inline_lifetime<'a: 'b, 'b>(x: &'a str, y: &'b str) -> &'b str {
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn inline_lifetime<'a: 'b, 'b>(x: &'a str, y: &'b str) -> &'b str {
|
||||
LL + fn inline_lifetime<'a, 'b>(x: &'a str, y: &'b str) -> &'b str where 'a: 'b {
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:17:21
|
||||
|
|
||||
LL | fn inline_with_where<T: Clone>()
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL ~ fn inline_with_where<T>()
|
||||
LL |
|
||||
LL | where
|
||||
LL ~ T: core::fmt::Debug, T: Clone,
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:24:21
|
||||
|
|
||||
LL | fn inline_with_const<T: Clone, const N: usize>() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn inline_with_const<T: Clone, const N: usize>() {}
|
||||
LL + fn inline_with_const<T, const N: usize>() where T: Clone {}
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:27:22
|
||||
|
|
||||
LL | fn inline_with_return<T: Clone>(val: T) -> T {
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn inline_with_return<T: Clone>(val: T) -> T {
|
||||
LL + fn inline_with_return<T>(val: T) -> T where T: Clone {
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:35:27
|
||||
|
|
||||
LL | fn trait_method_inline<T: Clone>(&self);
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn trait_method_inline<T: Clone>(&self);
|
||||
LL + fn trait_method_inline<T>(&self) where T: Clone;
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:38:28
|
||||
|
|
||||
LL | fn trait_method_default<T: Clone + Copy>(&self) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn trait_method_default<T: Clone + Copy>(&self) {}
|
||||
LL + fn trait_method_default<T>(&self) where T: Clone + Copy {}
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:51:26
|
||||
|
|
||||
LL | fn impl_method_inline<T: Clone>(&self) {}
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn impl_method_inline<T: Clone>(&self) {}
|
||||
LL + fn impl_method_inline<T>(&self) where T: Clone {}
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:54:28
|
||||
|
|
||||
LL | fn impl_method_multiple<T: Clone, U: core::fmt::Debug>(&self) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn impl_method_multiple<T: Clone, U: core::fmt::Debug>(&self) {}
|
||||
LL + fn impl_method_multiple<T, U>(&self) where T: Clone, U: core::fmt::Debug {}
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:59:27
|
||||
|
|
||||
LL | fn trait_method_inline<T: Clone>(&self) {}
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn trait_method_inline<T: Clone>(&self) {}
|
||||
LL + fn trait_method_inline<T>(&self) where T: Clone {}
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:62:28
|
||||
|
|
||||
LL | fn trait_method_default<T: Clone + Copy>(&self) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn trait_method_default<T: Clone + Copy>(&self) {}
|
||||
LL + fn trait_method_default<T>(&self) where T: Clone + Copy {}
|
||||
|
|
||||
|
||||
error: inline trait bounds used
|
||||
--> tests/ui/inline_trait_bounds.rs:92:22
|
||||
|
|
||||
LL | fn with_default_value<T: Clone = u32>(x: T) -> T {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: move bounds to a `where` clause
|
||||
|
|
||||
LL - fn with_default_value<T: Clone = u32>(x: T) -> T {
|
||||
LL + fn with_default_value<T = u32>(x: T) -> T where T: Clone {
|
||||
|
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#![feature(os_string_truncate)]
|
||||
#![warn(clippy::manual_clear)]
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::OsString;
|
||||
|
||||
struct CustomTruncate(String);
|
||||
|
||||
impl CustomTruncate {
|
||||
fn truncate(&mut self, len: usize) {
|
||||
self.0.truncate(len);
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut v = vec![1, 2, 3];
|
||||
v.clear(); //~ manual_clear
|
||||
|
||||
let mut d: VecDeque<i32> = VecDeque::from([1, 2, 3]);
|
||||
d.clear(); //~ manual_clear
|
||||
|
||||
// lint: macro receiver
|
||||
macro_rules! get_vec {
|
||||
($e:expr) => {
|
||||
$e
|
||||
};
|
||||
}
|
||||
get_vec!(v).clear(); //~ manual_clear
|
||||
|
||||
// no lint: other args
|
||||
v.truncate(1);
|
||||
|
||||
// no lint: `0` from a different context
|
||||
{
|
||||
// `0` inside a block expression should not be changed into `clear()`
|
||||
v.truncate({ 0 });
|
||||
}
|
||||
|
||||
// lint: String
|
||||
let mut s = String::from("abc");
|
||||
s.clear(); //~ manual_clear
|
||||
|
||||
// lint: OsString
|
||||
let mut os = OsString::from("abc");
|
||||
os.clear(); //~ manual_clear
|
||||
|
||||
// no lint: custom type
|
||||
let mut c = CustomTruncate(String::from("abc"));
|
||||
c.truncate(0);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#![feature(os_string_truncate)]
|
||||
#![warn(clippy::manual_clear)]
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::OsString;
|
||||
|
||||
struct CustomTruncate(String);
|
||||
|
||||
impl CustomTruncate {
|
||||
fn truncate(&mut self, len: usize) {
|
||||
self.0.truncate(len);
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut v = vec![1, 2, 3];
|
||||
v.truncate(0); //~ manual_clear
|
||||
|
||||
let mut d: VecDeque<i32> = VecDeque::from([1, 2, 3]);
|
||||
d.truncate(0); //~ manual_clear
|
||||
|
||||
// lint: macro receiver
|
||||
macro_rules! get_vec {
|
||||
($e:expr) => {
|
||||
$e
|
||||
};
|
||||
}
|
||||
get_vec!(v).truncate(0); //~ manual_clear
|
||||
|
||||
// no lint: other args
|
||||
v.truncate(1);
|
||||
|
||||
// no lint: `0` from a different context
|
||||
{
|
||||
// `0` inside a block expression should not be changed into `clear()`
|
||||
v.truncate({ 0 });
|
||||
}
|
||||
|
||||
// lint: String
|
||||
let mut s = String::from("abc");
|
||||
s.truncate(0); //~ manual_clear
|
||||
|
||||
// lint: OsString
|
||||
let mut os = OsString::from("abc");
|
||||
os.truncate(0); //~ manual_clear
|
||||
|
||||
// no lint: custom type
|
||||
let mut c = CustomTruncate(String::from("abc"));
|
||||
c.truncate(0);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
error: truncating to zero length
|
||||
--> tests/ui/manual_clear.rs:21:5
|
||||
|
|
||||
LL | v.truncate(0);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::manual-clear` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::manual_clear)]`
|
||||
help: use `clear()` instead
|
||||
|
|
||||
LL - v.truncate(0);
|
||||
LL + v.clear();
|
||||
|
|
||||
|
||||
error: truncating to zero length
|
||||
--> tests/ui/manual_clear.rs:24:5
|
||||
|
|
||||
LL | d.truncate(0);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `clear()` instead
|
||||
|
|
||||
LL - d.truncate(0);
|
||||
LL + d.clear();
|
||||
|
|
||||
|
||||
error: truncating to zero length
|
||||
--> tests/ui/manual_clear.rs:32:5
|
||||
|
|
||||
LL | get_vec!(v).truncate(0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `clear()` instead
|
||||
|
|
||||
LL - get_vec!(v).truncate(0);
|
||||
LL + get_vec!(v).clear();
|
||||
|
|
||||
|
||||
error: truncating to zero length
|
||||
--> tests/ui/manual_clear.rs:45:5
|
||||
|
|
||||
LL | s.truncate(0);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `clear()` instead
|
||||
|
|
||||
LL - s.truncate(0);
|
||||
LL + s.clear();
|
||||
|
|
||||
|
||||
error: truncating to zero length
|
||||
--> tests/ui/manual_clear.rs:49:5
|
||||
|
|
||||
LL | os.truncate(0);
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `clear()` instead
|
||||
|
|
||||
LL - os.truncate(0);
|
||||
LL + os.clear();
|
||||
|
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#![allow(
|
||||
unused_variables,
|
||||
clippy::question_mark,
|
||||
clippy::some_filter,
|
||||
clippy::useless_vec,
|
||||
clippy::nonminimal_bool
|
||||
)]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#![allow(
|
||||
unused_variables,
|
||||
clippy::question_mark,
|
||||
clippy::some_filter,
|
||||
clippy::useless_vec,
|
||||
clippy::nonminimal_bool
|
||||
)]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:10:5
|
||||
--> tests/ui/manual_filter.rs:11:5
|
||||
|
|
||||
LL | / match Some(0) {
|
||||
LL | |
|
||||
@@ -14,7 +14,7 @@ LL | | };
|
||||
= help: to override `-D warnings` add `#[allow(clippy::manual_filter)]`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:22:5
|
||||
--> tests/ui/manual_filter.rs:23:5
|
||||
|
|
||||
LL | / match Some(1) {
|
||||
LL | |
|
||||
@@ -26,7 +26,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(1).filter(|&x| x <= 0)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:34:5
|
||||
--> tests/ui/manual_filter.rs:35:5
|
||||
|
|
||||
LL | / match Some(2) {
|
||||
LL | |
|
||||
@@ -38,7 +38,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(2).filter(|&x| x <= 0)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:46:5
|
||||
--> tests/ui/manual_filter.rs:47:5
|
||||
|
|
||||
LL | / match Some(3) {
|
||||
LL | |
|
||||
@@ -50,7 +50,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(3).filter(|&x| x > 0)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:59:5
|
||||
--> tests/ui/manual_filter.rs:60:5
|
||||
|
|
||||
LL | / match y {
|
||||
LL | |
|
||||
@@ -62,7 +62,7 @@ LL | | };
|
||||
| |_____^ help: try: `y.filter(|&x| x <= 0)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:72:5
|
||||
--> tests/ui/manual_filter.rs:73:5
|
||||
|
|
||||
LL | / match Some(5) {
|
||||
LL | |
|
||||
@@ -74,7 +74,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(5).filter(|&x| x > 0)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:84:5
|
||||
--> tests/ui/manual_filter.rs:85:5
|
||||
|
|
||||
LL | / match Some(6) {
|
||||
LL | |
|
||||
@@ -86,7 +86,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(6).as_ref().filter(|&x| x > &0)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:97:5
|
||||
--> tests/ui/manual_filter.rs:98:5
|
||||
|
|
||||
LL | / match Some(String::new()) {
|
||||
LL | |
|
||||
@@ -98,7 +98,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(String::new()).filter(|x| external_cond)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:109:5
|
||||
--> tests/ui/manual_filter.rs:110:5
|
||||
|
|
||||
LL | / if let Some(x) = Some(7) {
|
||||
LL | |
|
||||
@@ -109,7 +109,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(7).filter(|&x| external_cond)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:116:5
|
||||
--> tests/ui/manual_filter.rs:117:5
|
||||
|
|
||||
LL | / match &Some(8) {
|
||||
LL | |
|
||||
@@ -121,7 +121,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(8).filter(|&x| x != 0)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:128:5
|
||||
--> tests/ui/manual_filter.rs:129:5
|
||||
|
|
||||
LL | / match Some(9) {
|
||||
LL | |
|
||||
@@ -133,7 +133,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(9).filter(|&x| x > 10 && x < 100)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:155:5
|
||||
--> tests/ui/manual_filter.rs:156:5
|
||||
|
|
||||
LL | / match Some(11) {
|
||||
LL | |
|
||||
@@ -153,7 +153,7 @@ LL ~ });
|
||||
|
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:200:13
|
||||
--> tests/ui/manual_filter.rs:201:13
|
||||
|
|
||||
LL | let _ = match Some(14) {
|
||||
| _____________^
|
||||
@@ -166,7 +166,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(14).filter(|&x| unsafe { f(x) })`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:211:13
|
||||
--> tests/ui/manual_filter.rs:212:13
|
||||
|
|
||||
LL | let _ = match Some(15) {
|
||||
| _____________^
|
||||
@@ -177,7 +177,7 @@ LL | | };
|
||||
| |_____^ help: try: `Some(15).filter(|&x| unsafe { f(x) })`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:220:12
|
||||
--> tests/ui/manual_filter.rs:221:12
|
||||
|
|
||||
LL | } else if let Some(x) = Some(16) {
|
||||
| ____________^
|
||||
@@ -190,31 +190,31 @@ LL | | };
|
||||
| |_____^ help: try: `{ Some(16).filter(|&x| x % 2 == 0) }`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:303:9
|
||||
--> tests/ui/manual_filter.rs:304:9
|
||||
|
|
||||
LL | opt.and_then(|x| if x == 0 { None } else { Some(x) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter(|&x| x != 0)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:307:9
|
||||
--> tests/ui/manual_filter.rs:308:9
|
||||
|
|
||||
LL | opt.and_then(move |x| if x == y { Some(x) } else { None });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter(move |&x| x == y)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:311:10
|
||||
--> tests/ui/manual_filter.rs:312:10
|
||||
|
|
||||
LL | opt1.and_then(|s| if s.len() > 2 { Some(s) } else { None });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter(|s| s.len() > 2)`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:317:9
|
||||
--> tests/ui/manual_filter.rs:318:9
|
||||
|
|
||||
LL | opt.and_then(|x| if unsafe { f(x as u32) } { Some(x) } else { None });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter(|&x| unsafe { f(x as u32) })`
|
||||
|
||||
error: manual implementation of `Option::filter`
|
||||
--> tests/ui/manual_filter.rs:319:9
|
||||
--> tests/ui/manual_filter.rs:320:9
|
||||
|
|
||||
LL | opt.and_then(|x| unsafe { if f(x as u32) { Some(x) } else { None } });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter(|&x| unsafe { f(x as u32) })`
|
||||
|
||||
@@ -116,3 +116,10 @@ impl NotOption {
|
||||
self.0.and_then(f)
|
||||
}
|
||||
}
|
||||
|
||||
fn issue16968() {
|
||||
let a = Some(1);
|
||||
|
||||
let opts = [1, 2];
|
||||
let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b)));
|
||||
}
|
||||
|
||||
@@ -116,3 +116,10 @@ fn and_then<U>(self, f: impl FnOnce(i32) -> Option<U>) -> Option<U> {
|
||||
self.0.and_then(f)
|
||||
}
|
||||
}
|
||||
|
||||
fn issue16968() {
|
||||
let a = Some(1);
|
||||
|
||||
let opts = [1, 2];
|
||||
let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b)));
|
||||
}
|
||||
|
||||
@@ -128,3 +128,29 @@ fn general_return() {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn async_fn() -> Result<(), ()> {
|
||||
if true {
|
||||
Err(())?;
|
||||
//~^ needless_return_with_question_mark
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn async_block() -> Result<(), ()> {
|
||||
async {
|
||||
if true {
|
||||
Err(())?;
|
||||
//~^ needless_return_with_question_mark
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
async fn async_block_final_stmt() -> Result<(), ()> {
|
||||
async {
|
||||
return Err(())?;
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -128,3 +128,29 @@ fn foo(ok: bool) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn async_fn() -> Result<(), ()> {
|
||||
if true {
|
||||
return Err(())?;
|
||||
//~^ needless_return_with_question_mark
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn async_block() -> Result<(), ()> {
|
||||
async {
|
||||
if true {
|
||||
return Err(())?;
|
||||
//~^ needless_return_with_question_mark
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
async fn async_block_final_stmt() -> Result<(), ()> {
|
||||
async {
|
||||
return Err(())?;
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -13,5 +13,17 @@ error: unneeded `return` statement with `?` operator
|
||||
LL | return Err(())?;
|
||||
| ^^^^^^^ help: remove it
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: unneeded `return` statement with `?` operator
|
||||
--> tests/ui/needless_return_with_question_mark.rs:134:9
|
||||
|
|
||||
LL | return Err(())?;
|
||||
| ^^^^^^^ help: remove it
|
||||
|
||||
error: unneeded `return` statement with `?` operator
|
||||
--> tests/ui/needless_return_with_question_mark.rs:143:13
|
||||
|
|
||||
LL | return Err(())?;
|
||||
| ^^^^^^^ help: remove it
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
extern crate proc_macros;
|
||||
use proc_macros::inline_macros;
|
||||
|
||||
// lint
|
||||
|
||||
struct A(u32);
|
||||
|
||||
impl Clone for A {
|
||||
@@ -31,12 +29,10 @@ impl Clone for B {
|
||||
impl Copy for B {}
|
||||
|
||||
// do not lint derived (clone's implementation is `*self` here anyway)
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct C(u32);
|
||||
|
||||
// do not lint derived (fr this time)
|
||||
|
||||
struct D(u32);
|
||||
|
||||
#[automatically_derived]
|
||||
@@ -54,7 +50,6 @@ impl Clone for D {
|
||||
impl Copy for D {}
|
||||
|
||||
// do not lint if clone is not manually implemented
|
||||
|
||||
struct E(u32);
|
||||
|
||||
#[automatically_derived]
|
||||
@@ -83,7 +78,6 @@ impl Clone for F {
|
||||
}
|
||||
|
||||
// do not lint since copy has more restrictive bounds
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
struct Uwu<A: Copy>(A);
|
||||
|
||||
@@ -104,7 +98,7 @@ impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {}
|
||||
mod issue12788 {
|
||||
use proc_macros::{external, with_span};
|
||||
|
||||
// lint -- not an external macro
|
||||
// lint non-external macro
|
||||
inline!(
|
||||
#[derive(Copy)]
|
||||
pub struct A;
|
||||
@@ -114,7 +108,7 @@ mod issue12788 {
|
||||
}
|
||||
);
|
||||
|
||||
// do not lint -- should skip external macros
|
||||
// do not lint external macros
|
||||
external!(
|
||||
#[derive(Copy)]
|
||||
pub struct B;
|
||||
@@ -126,7 +120,7 @@ mod issue12788 {
|
||||
}
|
||||
);
|
||||
|
||||
// do not lint -- should skip proc macros
|
||||
// do not lint proc macros
|
||||
#[derive(proc_macro_derive::NonCanonicalClone)]
|
||||
pub struct C;
|
||||
|
||||
@@ -142,3 +136,36 @@ mod issue12788 {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
struct N(u32);
|
||||
|
||||
impl Clone for N {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
|
||||
impl Copy for N {}
|
||||
|
||||
/// Test for corner cases with `implicit_return` enabled
|
||||
mod with_implicit_return {
|
||||
#![warn(clippy::implicit_return)]
|
||||
#![allow(clippy::needless_return)]
|
||||
|
||||
// Don't lint `return *self` under `implicit_return`
|
||||
struct G(u32);
|
||||
|
||||
impl Clone for G {
|
||||
fn clone(&self) -> Self {
|
||||
return *self;
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for G {}
|
||||
|
||||
struct H(u32);
|
||||
|
||||
impl Clone for H {
|
||||
fn clone(&self) -> Self { return *self; }
|
||||
}
|
||||
|
||||
impl Copy for H {}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
extern crate proc_macros;
|
||||
use proc_macros::inline_macros;
|
||||
|
||||
// lint
|
||||
|
||||
struct A(u32);
|
||||
|
||||
impl Clone for A {
|
||||
@@ -38,12 +36,10 @@ fn clone(&self) -> Self {
|
||||
impl Copy for B {}
|
||||
|
||||
// do not lint derived (clone's implementation is `*self` here anyway)
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct C(u32);
|
||||
|
||||
// do not lint derived (fr this time)
|
||||
|
||||
struct D(u32);
|
||||
|
||||
#[automatically_derived]
|
||||
@@ -61,7 +57,6 @@ fn clone_from(&mut self, source: &Self) {
|
||||
impl Copy for D {}
|
||||
|
||||
// do not lint if clone is not manually implemented
|
||||
|
||||
struct E(u32);
|
||||
|
||||
#[automatically_derived]
|
||||
@@ -97,7 +92,6 @@ fn clone_from(&mut self, source: &Self) {
|
||||
}
|
||||
|
||||
// do not lint since copy has more restrictive bounds
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
struct Uwu<A: Copy>(A);
|
||||
|
||||
@@ -118,7 +112,7 @@ impl<A: std::fmt::Debug + Copy + Clone> Copy for Uwu<A> {}
|
||||
mod issue12788 {
|
||||
use proc_macros::{external, with_span};
|
||||
|
||||
// lint -- not an external macro
|
||||
// lint non-external macro
|
||||
inline!(
|
||||
#[derive(Copy)]
|
||||
pub struct A;
|
||||
@@ -131,7 +125,7 @@ fn clone(&self) -> Self {
|
||||
}
|
||||
);
|
||||
|
||||
// do not lint -- should skip external macros
|
||||
// do not lint external macros
|
||||
external!(
|
||||
#[derive(Copy)]
|
||||
pub struct B;
|
||||
@@ -143,7 +137,7 @@ fn clone(&self) -> Self {
|
||||
}
|
||||
);
|
||||
|
||||
// do not lint -- should skip proc macros
|
||||
// do not lint proc macros
|
||||
#[derive(proc_macro_derive::NonCanonicalClone)]
|
||||
pub struct C;
|
||||
|
||||
@@ -159,3 +153,44 @@ fn clone(&self) -> Self {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
struct N(u32);
|
||||
|
||||
impl Clone for N {
|
||||
fn clone(&self) -> Self {
|
||||
//~^ non_canonical_clone_impl
|
||||
{ *self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for N {}
|
||||
|
||||
/// Test for corner cases with `implicit_return` enabled
|
||||
mod with_implicit_return {
|
||||
#![warn(clippy::implicit_return)]
|
||||
#![allow(clippy::needless_return)]
|
||||
|
||||
// Don't lint `return *self` under `implicit_return`
|
||||
struct G(u32);
|
||||
|
||||
impl Clone for G {
|
||||
fn clone(&self) -> Self {
|
||||
return *self;
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for G {}
|
||||
|
||||
struct H(u32);
|
||||
|
||||
impl Clone for H {
|
||||
fn clone(&self) -> Self {
|
||||
//~^ non_canonical_clone_impl
|
||||
{
|
||||
return *self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for H {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error: non-canonical implementation of `clone` on a `Copy` type
|
||||
--> tests/ui/non_canonical_clone_impl.rs:14:29
|
||||
--> tests/ui/non_canonical_clone_impl.rs:12:29
|
||||
|
|
||||
LL | fn clone(&self) -> Self {
|
||||
| _____________________________^
|
||||
@@ -12,7 +12,7 @@ LL | | }
|
||||
= help: to override `-D warnings` add `#[allow(clippy::non_canonical_clone_impl)]`
|
||||
|
||||
error: unnecessary implementation of `clone_from` on a `Copy` type
|
||||
--> tests/ui/non_canonical_clone_impl.rs:19:5
|
||||
--> tests/ui/non_canonical_clone_impl.rs:17:5
|
||||
|
|
||||
LL | / fn clone_from(&mut self, source: &Self) {
|
||||
LL | |
|
||||
@@ -22,7 +22,7 @@ LL | | }
|
||||
| |_____^ help: remove it
|
||||
|
||||
error: non-canonical implementation of `clone` on a `Copy` type
|
||||
--> tests/ui/non_canonical_clone_impl.rs:87:29
|
||||
--> tests/ui/non_canonical_clone_impl.rs:82:29
|
||||
|
|
||||
LL | fn clone(&self) -> Self {
|
||||
| _____________________________^
|
||||
@@ -32,7 +32,7 @@ LL | | }
|
||||
| |_____^ help: change this to: `{ *self }`
|
||||
|
||||
error: unnecessary implementation of `clone_from` on a `Copy` type
|
||||
--> tests/ui/non_canonical_clone_impl.rs:92:5
|
||||
--> tests/ui/non_canonical_clone_impl.rs:87:5
|
||||
|
|
||||
LL | / fn clone_from(&mut self, source: &Self) {
|
||||
LL | |
|
||||
@@ -42,7 +42,7 @@ LL | | }
|
||||
| |_____^ help: remove it
|
||||
|
||||
error: non-canonical implementation of `clone` on a `Copy` type
|
||||
--> tests/ui/non_canonical_clone_impl.rs:127:37
|
||||
--> tests/ui/non_canonical_clone_impl.rs:121:37
|
||||
|
|
||||
LL | fn clone(&self) -> Self {
|
||||
| _____________________________________^
|
||||
@@ -53,5 +53,27 @@ LL | | }
|
||||
|
|
||||
= note: this error originates in the macro `__inline_mac_mod_issue12788` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
error: non-canonical implementation of `clone` on a `Copy` type
|
||||
--> tests/ui/non_canonical_clone_impl.rs:160:29
|
||||
|
|
||||
LL | fn clone(&self) -> Self {
|
||||
| _____________________________^
|
||||
LL | |
|
||||
LL | | { *self }
|
||||
LL | | }
|
||||
| |_____^ help: change this to: `{ *self }`
|
||||
|
||||
error: non-canonical implementation of `clone` on a `Copy` type
|
||||
--> tests/ui/non_canonical_clone_impl.rs:187:33
|
||||
|
|
||||
LL | fn clone(&self) -> Self {
|
||||
| _________________________________^
|
||||
LL | |
|
||||
LL | | {
|
||||
LL | | return *self;
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_________^ help: change this to: `{ return *self; }`
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![warn(clippy::option_filter_map)]
|
||||
#![allow(clippy::map_flatten, clippy::unnecessary_map_on_constructor)]
|
||||
#![allow(clippy::map_flatten, clippy::some_filter, clippy::unnecessary_map_on_constructor)]
|
||||
|
||||
fn main() {
|
||||
let _ = Some(Some(1)).flatten();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![warn(clippy::option_filter_map)]
|
||||
#![allow(clippy::map_flatten, clippy::unnecessary_map_on_constructor)]
|
||||
#![allow(clippy::map_flatten, clippy::some_filter, clippy::unnecessary_map_on_constructor)]
|
||||
|
||||
fn main() {
|
||||
let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#![warn(clippy::some_filter)]
|
||||
#![allow(clippy::const_is_empty)]
|
||||
|
||||
macro_rules! unchanged {
|
||||
($result:expr) => {
|
||||
$result
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! condition {
|
||||
($condition:expr) => {
|
||||
$condition || false
|
||||
};
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.61"]
|
||||
fn older() {
|
||||
let _ = Some(0).filter(|_| false);
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.62"]
|
||||
fn newer() {
|
||||
let _ = false.then_some(0);
|
||||
//~^ some_filter
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = false.then_some(0);
|
||||
//~^ some_filter
|
||||
|
||||
// The condition contains an operator. The program should add parentheses.
|
||||
let _ = (1 == 0).then_some(0);
|
||||
//~^ some_filter
|
||||
|
||||
let _ = match 0 {
|
||||
//~^ some_filter
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => true,
|
||||
}.then_some(0);
|
||||
|
||||
// The argument to filter requires the value in the option. The program
|
||||
// can't figure out how to change it. It should leave it alone for now.
|
||||
let _ = Some(0).filter(|x| *x == 0);
|
||||
|
||||
// The expression is a macro argument. The program should change the macro
|
||||
// argument. It should not expand the macro.
|
||||
let _ = unchanged!(false.then_some(0));
|
||||
//~^ some_filter
|
||||
let _ = vec![false].is_empty().then_some(0);
|
||||
//~^ some_filter
|
||||
|
||||
// The condition is a macro that expands to an expression containing an
|
||||
// operator. The program should not add parentheses.
|
||||
let _ = condition!(false).then_some(0);
|
||||
//~^ some_filter
|
||||
|
||||
(1 == 0).then_some(String::from(
|
||||
//~^ some_filter
|
||||
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
|
||||
));
|
||||
{
|
||||
//~^ some_filter
|
||||
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong".is_empty()
|
||||
}.then_some(5);
|
||||
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong".is_empty().then_some(String::from(
|
||||
//~^ some_filter
|
||||
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#![warn(clippy::some_filter)]
|
||||
#![allow(clippy::const_is_empty)]
|
||||
|
||||
macro_rules! unchanged {
|
||||
($result:expr) => {
|
||||
$result
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! condition {
|
||||
($condition:expr) => {
|
||||
$condition || false
|
||||
};
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.61"]
|
||||
fn older() {
|
||||
let _ = Some(0).filter(|_| false);
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.62"]
|
||||
fn newer() {
|
||||
let _ = Some(0).filter(|_| false);
|
||||
//~^ some_filter
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = Some(0).filter(|_| false);
|
||||
//~^ some_filter
|
||||
|
||||
// The condition contains an operator. The program should add parentheses.
|
||||
let _ = Some(0).filter(|_| 1 == 0);
|
||||
//~^ some_filter
|
||||
|
||||
let _ = Some(0).filter(|_| match 0 {
|
||||
//~^ some_filter
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => true,
|
||||
});
|
||||
|
||||
// The argument to filter requires the value in the option. The program
|
||||
// can't figure out how to change it. It should leave it alone for now.
|
||||
let _ = Some(0).filter(|x| *x == 0);
|
||||
|
||||
// The expression is a macro argument. The program should change the macro
|
||||
// argument. It should not expand the macro.
|
||||
let _ = unchanged!(Some(0).filter(|_| false));
|
||||
//~^ some_filter
|
||||
let _ = Some(0).filter(|_| vec![false].is_empty());
|
||||
//~^ some_filter
|
||||
|
||||
// The condition is a macro that expands to an expression containing an
|
||||
// operator. The program should not add parentheses.
|
||||
let _ = Some(0).filter(|_| condition!(false));
|
||||
//~^ some_filter
|
||||
|
||||
Some(String::from(
|
||||
//~^ some_filter
|
||||
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
|
||||
))
|
||||
.filter(|_| 1 == 0);
|
||||
Some(5).filter(|_| {
|
||||
//~^ some_filter
|
||||
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong".is_empty()
|
||||
});
|
||||
Some(String::from(
|
||||
//~^ some_filter
|
||||
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
|
||||
))
|
||||
.filter(|_| {
|
||||
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong".is_empty()
|
||||
});
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user