Skip to content

Commit a0af2f8

Browse files
committed
Make suggestion give multiple alternatives
1 parent 1aa3f21 commit a0af2f8

File tree

4 files changed

+117
-65
lines changed

4 files changed

+117
-65
lines changed

clippy_lints/src/manual_float_methods.rs

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use clippy_utils::{
2-
consts::constant, diagnostics::span_lint_and_sugg, is_from_proc_macro, path_to_local, source::snippet_opt,
2+
consts::constant, diagnostics::span_lint_and_then, is_from_proc_macro, path_to_local, source::snippet_opt,
33
};
44
use rustc_errors::Applicability;
55
use rustc_hir::{BinOpKind, Expr, ExprKind};
6-
use rustc_lint::{LateContext, LateLintPass, LintContext};
6+
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
77
use rustc_middle::lint::in_external_macro;
88
use rustc_session::{declare_lint_pass, declare_tool_lint};
99

@@ -53,12 +53,37 @@ declare_clippy_lint! {
5353
}
5454
declare_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]);
5555

56+
#[derive(Clone, Copy)]
57+
enum Variant {
58+
ManualIsInfinite,
59+
ManualIsFinite,
60+
}
61+
62+
impl Variant {
63+
pub fn lint(self) -> &'static Lint {
64+
match self {
65+
Self::ManualIsInfinite => MANUAL_IS_INFINITE,
66+
Self::ManualIsFinite => MANUAL_IS_FINITE,
67+
}
68+
}
69+
70+
pub fn msg(self) -> &'static str {
71+
match self {
72+
Self::ManualIsInfinite => "manually checking if a float is infinite",
73+
Self::ManualIsFinite => "manually checking if a float is finite",
74+
}
75+
}
76+
}
77+
5678
impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
5779
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
5880
if !in_external_macro(cx.sess(), expr.span)
81+
&& (!cx.param_env.is_const() || cx.tcx.features().active(sym!(const_float_classify)))
5982
&& let ExprKind::Binary(kind, lhs, rhs) = expr.kind
6083
&& let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind
6184
&& let ExprKind::Binary(rhs_kind, rhs_lhs, rhs_rhs) = rhs.kind
85+
// Checking all possible scenarios using a function would be a hopeless task, as we have
86+
// 16 possible alignments of constants/operands. For now, let's use `partition`.
6287
&& let (operands, consts) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
6388
.into_iter()
6489
.partition::<Vec<&Expr<'_>>, _>(|i| path_to_local(i).is_some())
@@ -74,24 +99,51 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
7499
&& let Some(local_snippet) = snippet_opt(cx, first.span)
75100
&& !is_from_proc_macro(cx, expr)
76101
{
77-
let (msg, lint, sugg_fn) = match (kind.node, lhs_kind.node, rhs_kind.node) {
78-
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => {
79-
("manually checking if a float is infinite", MANUAL_IS_INFINITE, "is_infinite")
80-
},
81-
(BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => {
82-
("manually checking if a float is finite", MANUAL_IS_FINITE, "is_finite")
83-
},
102+
let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
103+
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite,
104+
(BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => Variant::ManualIsFinite,
84105
_ => return,
85106
};
86107

87-
span_lint_and_sugg(
108+
span_lint_and_then(
88109
cx,
89-
lint,
110+
variant.lint(),
90111
expr.span,
91-
msg,
92-
"try",
93-
format!("{local_snippet}.{sugg_fn}()"),
94-
Applicability::MachineApplicable,
112+
variant.msg(),
113+
|diag| {
114+
match variant {
115+
Variant::ManualIsInfinite => {
116+
diag.span_suggestion(
117+
expr.span,
118+
"use the dedicated method instead",
119+
format!("{local_snippet}.is_infinite()"),
120+
Applicability::MachineApplicable,
121+
);
122+
},
123+
Variant::ManualIsFinite => {
124+
// TODO: There's probably some better way to do this, i.e., create
125+
// multiple suggestions with notes between each of them
126+
diag.span_suggestion_verbose(
127+
expr.span,
128+
"use the dedicated method instead",
129+
format!("{local_snippet}.is_finite()"),
130+
Applicability::MaybeIncorrect,
131+
);
132+
diag.span_suggestion_verbose(
133+
expr.span,
134+
"this will alter how it handles NaN; if that is a problem, use instead",
135+
format!("{local_snippet}.is_finite() || {local_snippet}.is_nan()"),
136+
Applicability::MaybeIncorrect,
137+
);
138+
diag.span_suggestion_verbose(
139+
expr.span,
140+
"or, for conciseness",
141+
format!("!{local_snippet}.is_infinite()"),
142+
Applicability::MaybeIncorrect,
143+
);
144+
}
145+
}
146+
}
95147
);
96148
}
97149
}

tests/ui/manual_float_methods.fixed

Lines changed: 0 additions & 37 deletions
This file was deleted.

tests/ui/manual_float_methods.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
//@run-rustfix
21
//@aux-build:proc_macros.rs:proc-macro
32
#![allow(clippy::needless_if, unused)]
43
#![warn(clippy::manual_is_infinite, clippy::manual_is_finite)]

tests/ui/manual_float_methods.stderr

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,80 @@
11
error: manually checking if a float is infinite
2-
--> $DIR/manual_float_methods.rs:14:8
2+
--> $DIR/manual_float_methods.rs:13:8
33
|
44
LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {}
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_infinite()`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()`
66
|
77
= note: `-D clippy::manual-is-infinite` implied by `-D warnings`
88

99
error: manually checking if a float is finite
10-
--> $DIR/manual_float_methods.rs:15:8
10+
--> $DIR/manual_float_methods.rs:14:8
1111
|
1212
LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {}
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_finite()`
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1414
|
1515
= note: `-D clippy::manual-is-finite` implied by `-D warnings`
16+
help: use the dedicated method instead
17+
|
18+
LL | if x.is_finite() {}
19+
| ~~~~~~~~~~~~~
20+
help: this will alter how it handles NaN; if that is a problem, use instead
21+
|
22+
LL | if x.is_finite() || x.is_nan() {}
23+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
24+
help: or, for conciseness
25+
|
26+
LL | if !x.is_infinite() {}
27+
| ~~~~~~~~~~~~~~~~
1628

1729
error: manually checking if a float is infinite
18-
--> $DIR/manual_float_methods.rs:16:8
30+
--> $DIR/manual_float_methods.rs:15:8
1931
|
2032
LL | if x == INFINITE || x == NEG_INFINITE {}
21-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_infinite()`
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()`
2234

2335
error: manually checking if a float is finite
24-
--> $DIR/manual_float_methods.rs:17:8
36+
--> $DIR/manual_float_methods.rs:16:8
2537
|
2638
LL | if x != INFINITE && x != NEG_INFINITE {}
27-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_finite()`
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40+
|
41+
help: use the dedicated method instead
42+
|
43+
LL | if x.is_finite() {}
44+
| ~~~~~~~~~~~~~
45+
help: this will alter how it handles NaN; if that is a problem, use instead
46+
|
47+
LL | if x.is_finite() || x.is_nan() {}
48+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
49+
help: or, for conciseness
50+
|
51+
LL | if !x.is_infinite() {}
52+
| ~~~~~~~~~~~~~~~~
2853

2954
error: manually checking if a float is infinite
30-
--> $DIR/manual_float_methods.rs:19:8
55+
--> $DIR/manual_float_methods.rs:18:8
3156
|
3257
LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {}
33-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_infinite()`
58+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()`
3459

3560
error: manually checking if a float is finite
36-
--> $DIR/manual_float_methods.rs:20:8
61+
--> $DIR/manual_float_methods.rs:19:8
3762
|
3863
LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {}
39-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_finite()`
64+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
65+
|
66+
help: use the dedicated method instead
67+
|
68+
LL | if x.is_finite() {}
69+
| ~~~~~~~~~~~~~
70+
help: this will alter how it handles NaN; if that is a problem, use instead
71+
|
72+
LL | if x.is_finite() || x.is_nan() {}
73+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
74+
help: or, for conciseness
75+
|
76+
LL | if !x.is_infinite() {}
77+
| ~~~~~~~~~~~~~~~~
4078

4179
error: aborting due to 6 previous errors
4280

0 commit comments

Comments
 (0)