Skip to content

Commit fc2d35c

Browse files
fix: exclude core from external crates
1 parent 7f4be12 commit fc2d35c

File tree

5 files changed

+150
-33
lines changed

5 files changed

+150
-33
lines changed

clippy_lints/src/methods/unnecessary_min_or_max.rs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,15 @@ use std::cmp::Ordering;
33
use super::UNNECESSARY_MIN_OR_MAX;
44
use clippy_utils::diagnostics::span_lint_and_sugg;
55

6-
use clippy_utils::consts::{constant, Constant, FullInt};
7-
use clippy_utils::macros::HirNode;
6+
use clippy_utils::consts::{constant, ConstEvalLateContext, Constant, FullInt};
87
use clippy_utils::source::snippet;
9-
use hir::{Expr, ExprKind};
108

119
use rustc_errors::Applicability;
12-
use rustc_hir as hir;
10+
use rustc_hir::Expr;
1311
use rustc_lint::LateContext;
1412

1513
use rustc_middle::ty;
16-
use rustc_span::Span;
14+
use rustc_span::{sym, Span};
1715

1816
pub(super) fn check<'tcx>(
1917
cx: &LateContext<'tcx>,
@@ -93,12 +91,6 @@ fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
9391
}
9492

9593
fn is_external_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
96-
let ExprKind::Path(ref qpath) = expr.kind else {
97-
return false;
98-
};
99-
100-
let Some(def_id) = cx.qpath_res(qpath, expr.hir_id()).opt_def_id() else {
101-
return false;
102-
};
103-
!def_id.is_local()
94+
let mut ctxt = ConstEvalLateContext::new(cx, cx.typeck_results());
95+
ctxt.expr(expr).is_some() && ctxt.expr_is_external(expr, &[sym::core])
10496
}

clippy_utils/src/consts.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(clippy::float_cmp)]
22

3+
use crate::macros::HirNode;
34
use crate::source::{get_source_text, walk_span_to_context};
45
use crate::{clip, is_direct_expn_of, sext, unsext};
56

@@ -529,6 +530,60 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
529530
}
530531
}
531532

533+
/// Simple constant folding to determine if an expression depends on an external crate
534+
/// excluding crates listed in exceptions
535+
pub fn expr_is_external(&mut self, e: &Expr<'_>, exceptions: &[Symbol]) -> bool {
536+
match e.kind {
537+
ExprKind::ConstBlock(ConstBlock { body, .. }) => {
538+
self.expr_is_external(self.lcx.tcx.hir().body(body).value, exceptions)
539+
},
540+
ExprKind::DropTemps(e) => self.expr_is_external(e, exceptions),
541+
ExprKind::Path(ref qpath) => {
542+
if let Some(def_id) = self.lcx.qpath_res(qpath, e.hir_id()).opt_def_id() {
543+
def_id.is_local() || exceptions.iter().all(|e| self.lcx.tcx.crate_name(def_id.krate) != *e)
544+
} else {
545+
true
546+
}
547+
},
548+
ExprKind::Block(block, _) => {
549+
if block.stmts.is_empty()
550+
&& let Some(expr) = block.expr
551+
{
552+
self.expr_is_external(expr, exceptions)
553+
} else {
554+
false
555+
}
556+
},
557+
ExprKind::Array(vec) => vec.iter().any(|elem| self.expr_is_external(elem, exceptions)),
558+
ExprKind::Tup(tup) => tup.iter().any(|elem| self.expr_is_external(elem, exceptions)),
559+
ExprKind::Repeat(value, _) => self.expr_is_external(value, exceptions),
560+
ExprKind::Unary(_, operand) => self.expr_is_external(operand, exceptions),
561+
ExprKind::If(cond, then, ref otherwise) => {
562+
if let Some(Constant::Bool(b)) = self.expr(cond) {
563+
if b {
564+
self.expr_is_external(then, exceptions)
565+
} else {
566+
otherwise
567+
.as_ref()
568+
.map(|expr| self.expr_is_external(expr, exceptions))
569+
.unwrap_or(false)
570+
}
571+
} else {
572+
false
573+
}
574+
},
575+
ExprKind::Binary(_, left, right) => {
576+
self.expr_is_external(left, exceptions) || self.expr_is_external(right, exceptions)
577+
},
578+
ExprKind::Index(arr, index, _) => {
579+
self.expr_is_external(arr, exceptions) || self.expr_is_external(index, exceptions)
580+
},
581+
ExprKind::AddrOf(_, _, inner) => self.expr_is_external(inner, exceptions),
582+
ExprKind::Field(local_expr, _) => self.expr_is_external(local_expr, exceptions),
583+
_ => false,
584+
}
585+
}
586+
532587
#[expect(clippy::cast_possible_wrap)]
533588
fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option<Constant<'tcx>> {
534589
use self::Constant::{Bool, Int};

tests/ui/unnecessary_min_or_max.fixed

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(unused)]
22
#![warn(clippy::unnecessary_min_or_max)]
3-
3+
#![allow(clippy::identity_op)]
44
const X: i32 = 1;
55

66
fn main() {
@@ -12,25 +12,39 @@ fn main() {
1212
let _ = 6;
1313
let _ = 7_u8;
1414

15+
let _ = X.max(12);
16+
let _ = X.min(12);
17+
let _ = 12.min(X);
18+
let _ = 12.max(X);
19+
let _ = (X + 1).max(12);
20+
let _ = (X + 1).min(12);
21+
let _ = 12.min(X - 1);
22+
let _ = 12.max(X - 1);
23+
1524
let x: u32 = 42;
1625
// unsigned with zero
1726
let _ = 0;
1827
let _ = x;
1928
let _ = 0_u32;
2029
let _ = x;
2130

31+
let x: i32 = 42;
32+
// signed MIN
33+
let _ = i32::MIN;
34+
let _ = x;
35+
let _ = i32::MIN;
36+
let _ = x;
37+
38+
let _ = i32::MIN - 0;
39+
let _ = x;
40+
41+
let _ = i32::MIN - 0;
42+
2243
// The below cases shouldn't be lint
2344
let mut min = u32::MAX;
2445
for _ in 0..1000 {
2546
min = min.min(random_u32());
2647
}
27-
28-
let x: i32 = 42;
29-
// signed MIN
30-
let _ = i32::MIN.min(x);
31-
let _ = i32::MIN.max(x);
32-
let _ = x.min(i32::MIN);
33-
let _ = x.max(i32::MIN);
3448
}
3549
fn random_u32() -> u32 {
3650
// random number generator

tests/ui/unnecessary_min_or_max.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(unused)]
22
#![warn(clippy::unnecessary_min_or_max)]
3-
3+
#![allow(clippy::identity_op)]
44
const X: i32 = 1;
55

66
fn main() {
@@ -12,25 +12,39 @@ fn main() {
1212
let _ = 6.min(7_u8);
1313
let _ = 6.max(7_u8);
1414

15+
let _ = X.max(12);
16+
let _ = X.min(12);
17+
let _ = 12.min(X);
18+
let _ = 12.max(X);
19+
let _ = (X + 1).max(12);
20+
let _ = (X + 1).min(12);
21+
let _ = 12.min(X - 1);
22+
let _ = 12.max(X - 1);
23+
1524
let x: u32 = 42;
1625
// unsigned with zero
1726
let _ = 0.min(x);
1827
let _ = 0.max(x);
1928
let _ = x.min(0_u32);
2029
let _ = x.max(0_u32);
2130

22-
// The below cases shouldn't be lint
23-
let mut min = u32::MAX;
24-
for _ in 0..1000 {
25-
min = min.min(random_u32());
26-
}
27-
2831
let x: i32 = 42;
2932
// signed MIN
3033
let _ = i32::MIN.min(x);
3134
let _ = i32::MIN.max(x);
3235
let _ = x.min(i32::MIN);
3336
let _ = x.max(i32::MIN);
37+
38+
let _ = x.min(i32::MIN - 0);
39+
let _ = x.max(i32::MIN);
40+
41+
let _ = x.min(i32::MIN - 0);
42+
43+
// The below cases shouldn't be lint
44+
let mut min = u32::MAX;
45+
for _ in 0..1000 {
46+
min = min.min(random_u32());
47+
}
3448
}
3549
fn random_u32() -> u32 {
3650
// random number generator

tests/ui/unnecessary_min_or_max.stderr

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,70 @@ LL | let _ = 6.max(7_u8);
3838
| ^^^^^^^^^^^ help: try: `7_u8`
3939

4040
error: `0` is never greater than `x` and has therefore no effect
41-
--> tests/ui/unnecessary_min_or_max.rs:17:13
41+
--> tests/ui/unnecessary_min_or_max.rs:26:13
4242
|
4343
LL | let _ = 0.min(x);
4444
| ^^^^^^^^ help: try: `0`
4545

4646
error: `0` is never greater than `x` and has therefore no effect
47-
--> tests/ui/unnecessary_min_or_max.rs:18:13
47+
--> tests/ui/unnecessary_min_or_max.rs:27:13
4848
|
4949
LL | let _ = 0.max(x);
5050
| ^^^^^^^^ help: try: `x`
5151

5252
error: `x` is never smaller than `0_u32` and has therefore no effect
53-
--> tests/ui/unnecessary_min_or_max.rs:19:13
53+
--> tests/ui/unnecessary_min_or_max.rs:28:13
5454
|
5555
LL | let _ = x.min(0_u32);
5656
| ^^^^^^^^^^^^ help: try: `0_u32`
5757

5858
error: `x` is never smaller than `0_u32` and has therefore no effect
59-
--> tests/ui/unnecessary_min_or_max.rs:20:13
59+
--> tests/ui/unnecessary_min_or_max.rs:29:13
6060
|
6161
LL | let _ = x.max(0_u32);
6262
| ^^^^^^^^^^^^ help: try: `x`
6363

64-
error: aborting due to 10 previous errors
64+
error: `i32::MIN` is never greater than `x` and has therefore no effect
65+
--> tests/ui/unnecessary_min_or_max.rs:33:13
66+
|
67+
LL | let _ = i32::MIN.min(x);
68+
| ^^^^^^^^^^^^^^^ help: try: `i32::MIN`
69+
70+
error: `i32::MIN` is never greater than `x` and has therefore no effect
71+
--> tests/ui/unnecessary_min_or_max.rs:34:13
72+
|
73+
LL | let _ = i32::MIN.max(x);
74+
| ^^^^^^^^^^^^^^^ help: try: `x`
75+
76+
error: `x` is never smaller than `i32::MIN` and has therefore no effect
77+
--> tests/ui/unnecessary_min_or_max.rs:35:13
78+
|
79+
LL | let _ = x.min(i32::MIN);
80+
| ^^^^^^^^^^^^^^^ help: try: `i32::MIN`
81+
82+
error: `x` is never smaller than `i32::MIN` and has therefore no effect
83+
--> tests/ui/unnecessary_min_or_max.rs:36:13
84+
|
85+
LL | let _ = x.max(i32::MIN);
86+
| ^^^^^^^^^^^^^^^ help: try: `x`
87+
88+
error: `x` is never smaller than `i32::MIN - 0` and has therefore no effect
89+
--> tests/ui/unnecessary_min_or_max.rs:38:13
90+
|
91+
LL | let _ = x.min(i32::MIN - 0);
92+
| ^^^^^^^^^^^^^^^^^^^ help: try: `i32::MIN - 0`
93+
94+
error: `x` is never smaller than `i32::MIN` and has therefore no effect
95+
--> tests/ui/unnecessary_min_or_max.rs:39:13
96+
|
97+
LL | let _ = x.max(i32::MIN);
98+
| ^^^^^^^^^^^^^^^ help: try: `x`
99+
100+
error: `x` is never smaller than `i32::MIN - 0` and has therefore no effect
101+
--> tests/ui/unnecessary_min_or_max.rs:41:13
102+
|
103+
LL | let _ = x.min(i32::MIN - 0);
104+
| ^^^^^^^^^^^^^^^^^^^ help: try: `i32::MIN - 0`
105+
106+
error: aborting due to 17 previous errors
65107

0 commit comments

Comments
 (0)