mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-22 20:03:37 +00:00
Improve SpanlessEq
* Don't consider expansions of different macros to be the same, even if they expand to the same tokens * Don't consider `cfg!` expansions to be equal if they check different configs.
This commit is contained in:
parent
5351170744
commit
58132cb3b0
@ -1,6 +1,6 @@
|
||||
#![allow(clippy::float_cmp)]
|
||||
|
||||
use crate::source::{span_source_range, walk_span_to_context};
|
||||
use crate::source::{get_source_text, walk_span_to_context};
|
||||
use crate::{clip, is_direct_expn_of, sext, unsext};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{self, LitFloatType, LitKind};
|
||||
@ -516,7 +516,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
|
||||
&& let expr_lo = expr_span.lo()
|
||||
&& expr_lo >= span.lo
|
||||
&& let Some(src) = span_source_range(self.lcx, span.lo..expr_lo)
|
||||
&& let Some(src) = get_source_text(self.lcx, span.lo..expr_lo)
|
||||
&& let Some(src) = src.as_str()
|
||||
{
|
||||
use rustc_lexer::TokenKind::{Whitespace, LineComment, BlockComment, Semi, OpenBrace};
|
||||
|
@ -14,7 +14,7 @@ use rustc_hir::{
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::TypeckResults;
|
||||
use rustc_span::{sym, BytePos, Symbol, SyntaxContext};
|
||||
use rustc_span::{sym, BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Range;
|
||||
|
||||
@ -67,6 +67,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
|
||||
HirEqInterExpr {
|
||||
inner: self,
|
||||
left_ctxt: SyntaxContext::root(),
|
||||
right_ctxt: SyntaxContext::root(),
|
||||
locals: HirIdMap::default(),
|
||||
}
|
||||
}
|
||||
@ -94,6 +96,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
|
||||
pub struct HirEqInterExpr<'a, 'b, 'tcx> {
|
||||
inner: &'a mut SpanlessEq<'b, 'tcx>,
|
||||
left_ctxt: SyntaxContext,
|
||||
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 }`,
|
||||
@ -128,6 +132,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
}
|
||||
|
||||
/// Checks whether two blocks are the same.
|
||||
#[expect(clippy::similar_names)]
|
||||
fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
|
||||
use TokenKind::{BlockComment, LineComment, Semi, Whitespace};
|
||||
if left.stmts.len() != right.stmts.len() {
|
||||
@ -166,7 +171,8 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
// Can happen when macros expand to multiple statements, or rearrange statements.
|
||||
// Nothing in between the statements to check in this case.
|
||||
continue;
|
||||
} else if lstmt_span.lo < lstart || rstmt_span.lo < rstart {
|
||||
}
|
||||
if lstmt_span.lo < lstart || rstmt_span.lo < rstart {
|
||||
// Only one of the blocks had a weird macro.
|
||||
return false;
|
||||
}
|
||||
@ -243,7 +249,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
|
||||
#[expect(clippy::similar_names)]
|
||||
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
|
||||
if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -476,6 +482,45 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
|
||||
left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty())
|
||||
}
|
||||
|
||||
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() {
|
||||
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)
|
||||
&& !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| {
|
||||
!matches!(t, Whitespace | LineComment { .. } | BlockComment { .. })
|
||||
}))
|
||||
{
|
||||
// Either a different chain of macro calls, or different arguments to the `cfg` macro.
|
||||
return 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();
|
||||
}
|
||||
}
|
||||
self.left_ctxt = left;
|
||||
self.right_ctxt = right;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Some simple reductions like `{ return }` => `return`
|
||||
@ -1075,6 +1120,7 @@ pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
|
||||
h.finish()
|
||||
}
|
||||
|
||||
#[expect(clippy::similar_names)]
|
||||
fn eq_span_tokens(
|
||||
cx: &LateContext<'_>,
|
||||
left: impl SpanRange,
|
||||
|
@ -1499,7 +1499,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
|
||||
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree()))
|
||||
&& let min_const_kind = ConstantKind::from_value(const_val, bnd_ty)
|
||||
&& let Some(min_const) = miri_to_const(cx.tcx, min_const_kind)
|
||||
&& let Some((start_const, _)) = constant(cx, cx.typeck_results(), start)
|
||||
&& let Some(start_const) = constant(cx, cx.typeck_results(), start)
|
||||
{
|
||||
start_const == min_const
|
||||
} else {
|
||||
@ -1515,7 +1515,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
|
||||
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree()))
|
||||
&& let max_const_kind = ConstantKind::from_value(const_val, bnd_ty)
|
||||
&& let Some(max_const) = miri_to_const(cx.tcx, max_const_kind)
|
||||
&& let Some((end_const, _)) = constant(cx, cx.typeck_results(), end)
|
||||
&& let Some(end_const) = constant(cx, cx.typeck_results(), end)
|
||||
{
|
||||
end_const == max_const
|
||||
} else {
|
||||
|
@ -1,5 +1,10 @@
|
||||
//@run-rustfix
|
||||
#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
|
||||
#![allow(
|
||||
clippy::assertions_on_constants,
|
||||
clippy::equatable_if_let,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::eq_op
|
||||
)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[warn(clippy::collapsible_if)]
|
||||
|
@ -1,5 +1,10 @@
|
||||
//@run-rustfix
|
||||
#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
|
||||
#![allow(
|
||||
clippy::assertions_on_constants,
|
||||
clippy::equatable_if_let,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::eq_op
|
||||
)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[warn(clippy::collapsible_if)]
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:9:5
|
||||
--> $DIR/collapsible_if.rs:14:5
|
||||
|
|
||||
LL | / if x == "hello" {
|
||||
LL | | if y == "world" {
|
||||
@ -17,7 +17,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:15:5
|
||||
--> $DIR/collapsible_if.rs:20:5
|
||||
|
|
||||
LL | / if x == "hello" || x == "world" {
|
||||
LL | | if y == "world" || y == "hello" {
|
||||
@ -34,7 +34,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:21:5
|
||||
--> $DIR/collapsible_if.rs:26:5
|
||||
|
|
||||
LL | / if x == "hello" && x == "world" {
|
||||
LL | | if y == "world" || y == "hello" {
|
||||
@ -51,7 +51,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:27:5
|
||||
--> $DIR/collapsible_if.rs:32:5
|
||||
|
|
||||
LL | / if x == "hello" || x == "world" {
|
||||
LL | | if y == "world" && y == "hello" {
|
||||
@ -68,7 +68,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:33:5
|
||||
--> $DIR/collapsible_if.rs:38:5
|
||||
|
|
||||
LL | / if x == "hello" && x == "world" {
|
||||
LL | | if y == "world" && y == "hello" {
|
||||
@ -85,7 +85,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:39:5
|
||||
--> $DIR/collapsible_if.rs:44:5
|
||||
|
|
||||
LL | / if 42 == 1337 {
|
||||
LL | | if 'a' != 'A' {
|
||||
@ -102,7 +102,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:95:5
|
||||
--> $DIR/collapsible_if.rs:100:5
|
||||
|
|
||||
LL | / if x == "hello" {
|
||||
LL | | if y == "world" { // Collapsible
|
||||
@ -119,7 +119,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:154:5
|
||||
--> $DIR/collapsible_if.rs:159:5
|
||||
|
|
||||
LL | / if matches!(true, true) {
|
||||
LL | | if matches!(true, true) {}
|
||||
@ -127,7 +127,7 @@ LL | | }
|
||||
| |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}`
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:159:5
|
||||
--> $DIR/collapsible_if.rs:164:5
|
||||
|
|
||||
LL | / if matches!(true, true) && truth() {
|
||||
LL | | if matches!(true, true) {}
|
||||
|
@ -57,6 +57,16 @@ macro_rules! m {
|
||||
(foo) => {};
|
||||
(bar) => {};
|
||||
}
|
||||
macro_rules! foo {
|
||||
() => {
|
||||
1
|
||||
};
|
||||
}
|
||||
macro_rules! bar {
|
||||
() => {
|
||||
1
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 0;
|
||||
@ -111,4 +121,16 @@ fn main() {
|
||||
},
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => foo!(),
|
||||
1 => bar!(),
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => cfg!(not_enabled),
|
||||
1 => cfg!(also_not_enabled),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
@ -239,4 +239,10 @@ fn main() {
|
||||
3 => core::convert::identity::<u32>(todo!()),
|
||||
_ => 5,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => cfg!(not_enable),
|
||||
1 => cfg!(not_enable),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
@ -192,5 +192,20 @@ note: other arm here
|
||||
LL | Some(Bar { x: 0, y: 5, .. }) => 1,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:245:9
|
||||
|
|
||||
LL | 1 => cfg!(not_enable),
|
||||
| -^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `1 | 0`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:244:9
|
||||
|
|
||||
LL | 0 => cfg!(not_enable),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
//@run-rustfix
|
||||
#![warn(clippy::partialeq_to_none)]
|
||||
#![allow(clippy::eq_op)]
|
||||
|
||||
struct Foobar;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
//@run-rustfix
|
||||
#![warn(clippy::partialeq_to_none)]
|
||||
#![allow(clippy::eq_op)]
|
||||
|
||||
struct Foobar;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:14:8
|
||||
--> $DIR/partialeq_to_none.rs:15:8
|
||||
|
|
||||
LL | if f != None { "yay" } else { "nay" }
|
||||
| ^^^^^^^^^ help: use `Option::is_some()` instead: `f.is_some()`
|
||||
@ -7,55 +7,55 @@ LL | if f != None { "yay" } else { "nay" }
|
||||
= note: `-D clippy::partialeq-to-none` implied by `-D warnings`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:44:13
|
||||
--> $DIR/partialeq_to_none.rs:45:13
|
||||
|
|
||||
LL | let _ = x == None;
|
||||
| ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:45:13
|
||||
--> $DIR/partialeq_to_none.rs:46:13
|
||||
|
|
||||
LL | let _ = x != None;
|
||||
| ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:46:13
|
||||
--> $DIR/partialeq_to_none.rs:47:13
|
||||
|
|
||||
LL | let _ = None == x;
|
||||
| ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:47:13
|
||||
--> $DIR/partialeq_to_none.rs:48:13
|
||||
|
|
||||
LL | let _ = None != x;
|
||||
| ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:49:8
|
||||
--> $DIR/partialeq_to_none.rs:50:8
|
||||
|
|
||||
LL | if foobar() == None {}
|
||||
| ^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `foobar().is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:51:8
|
||||
--> $DIR/partialeq_to_none.rs:52:8
|
||||
|
|
||||
LL | if bar().ok() != None {}
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `bar().ok().is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:53:13
|
||||
--> $DIR/partialeq_to_none.rs:54:13
|
||||
|
|
||||
LL | let _ = Some(1 + 2) != None;
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `Some(1 + 2).is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:55:13
|
||||
--> $DIR/partialeq_to_none.rs:56:13
|
||||
|
|
||||
LL | let _ = { Some(0) } == None;
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `{ Some(0) }.is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:57:13
|
||||
--> $DIR/partialeq_to_none.rs:58:13
|
||||
|
|
||||
LL | let _ = {
|
||||
| _____________^
|
||||
@ -77,31 +77,31 @@ LL ~ }.is_some();
|
||||
|
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:67:13
|
||||
--> $DIR/partialeq_to_none.rs:68:13
|
||||
|
|
||||
LL | let _ = optref() == &&None;
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:68:13
|
||||
--> $DIR/partialeq_to_none.rs:69:13
|
||||
|
|
||||
LL | let _ = &&None != optref();
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:69:13
|
||||
--> $DIR/partialeq_to_none.rs:70:13
|
||||
|
|
||||
LL | let _ = **optref() == None;
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:70:13
|
||||
--> $DIR/partialeq_to_none.rs:71:13
|
||||
|
|
||||
LL | let _ = &None != *optref();
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:73:13
|
||||
--> $DIR/partialeq_to_none.rs:74:13
|
||||
|
|
||||
LL | let _ = None != *x;
|
||||
| ^^^^^^^^^^ help: use `Option::is_some()` instead: `(*x).is_some()`
|
||||
|
Loading…
Reference in New Issue
Block a user