forked from rust-lang/rust
-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#5952 - 1c3t3a:1c3t3a-dev-5819, r=Manishearth
Added a lint which corrects expressions like (a - b) < f32::EPSILON, according to rust-lang#5819 Fixes rust-lang#5819 changelog: none
- Loading branch information
Showing
7 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; | ||
use if_chain::if_chain; | ||
use rustc_errors::Applicability; | ||
use rustc_hir::{BinOpKind, Expr, ExprKind}; | ||
use rustc_lint::{LateContext, LateLintPass}; | ||
use rustc_middle::ty; | ||
use rustc_session::{declare_lint_pass, declare_tool_lint}; | ||
use rustc_span::source_map::Spanned; | ||
|
||
declare_clippy_lint! { | ||
/// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or | ||
/// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`. | ||
/// | ||
/// **Why is this bad?** The code without `.abs()` is more likely to have a bug. | ||
/// | ||
/// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is | ||
/// technically unneccessary. However, it will make the code more robust and doesn't have any | ||
/// large performance implications. If the abs call was deliberately left out for performance | ||
/// reasons, it is probably better to state this explicitly in the code, which then can be done | ||
/// with an allow. | ||
/// | ||
/// **Example:** | ||
/// | ||
/// ```rust | ||
/// pub fn is_roughly_equal(a: f32, b: f32) -> bool { | ||
/// (a - b) < f32::EPSILON | ||
/// } | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// pub fn is_roughly_equal(a: f32, b: f32) -> bool { | ||
/// (a - b).abs() < f32::EPSILON | ||
/// } | ||
/// ``` | ||
pub FLOAT_EQUALITY_WITHOUT_ABS, | ||
correctness, | ||
"float equality check without `.abs()`" | ||
} | ||
|
||
declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]); | ||
|
||
impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { | ||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | ||
let lhs; | ||
let rhs; | ||
|
||
// check if expr is a binary expression with a lt or gt operator | ||
if let ExprKind::Binary(op, ref left, ref right) = expr.kind { | ||
match op.node { | ||
BinOpKind::Lt => { | ||
lhs = left; | ||
rhs = right; | ||
}, | ||
BinOpKind::Gt => { | ||
lhs = right; | ||
rhs = left; | ||
}, | ||
_ => return, | ||
}; | ||
} else { | ||
return; | ||
} | ||
|
||
if_chain! { | ||
|
||
// left hand side is a substraction | ||
if let ExprKind::Binary( | ||
Spanned { | ||
node: BinOpKind::Sub, | ||
.. | ||
}, | ||
val_l, | ||
val_r, | ||
) = lhs.kind; | ||
|
||
// right hand side matches either f32::EPSILON or f64::EPSILON | ||
if let ExprKind::Path(ref epsilon_path) = rhs.kind; | ||
if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON); | ||
|
||
// values of the substractions on the left hand side are of the type float | ||
let t_val_l = cx.typeck_results().expr_ty(val_l); | ||
let t_val_r = cx.typeck_results().expr_ty(val_r); | ||
if let ty::Float(_) = t_val_l.kind; | ||
if let ty::Float(_) = t_val_r.kind; | ||
|
||
then { | ||
// get the snippet string | ||
let lhs_string = snippet( | ||
cx, | ||
lhs.span, | ||
"(...)", | ||
); | ||
// format the suggestion | ||
let suggestion = if lhs_string.starts_with('(') { | ||
format!("{}.abs()", lhs_string) | ||
} else { | ||
format!("({}).abs()", lhs_string) | ||
}; | ||
// spans the lint | ||
span_lint_and_sugg( | ||
cx, | ||
FLOAT_EQUALITY_WITHOUT_ABS, | ||
expr.span, | ||
"float equality check without `.abs()`", | ||
"add `.abs()`", | ||
suggestion, | ||
Applicability::MaybeIncorrect, | ||
); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#![warn(clippy::float_equality_without_abs)] | ||
|
||
pub fn is_roughly_equal(a: f32, b: f32) -> bool { | ||
(a - b) < f32::EPSILON | ||
} | ||
|
||
pub fn main() { | ||
// all errors | ||
is_roughly_equal(1.0, 2.0); | ||
let a = 0.05; | ||
let b = 0.0500001; | ||
|
||
let _ = (a - b) < f32::EPSILON; | ||
let _ = a - b < f32::EPSILON; | ||
let _ = a - b.abs() < f32::EPSILON; | ||
let _ = (a as f64 - b as f64) < f64::EPSILON; | ||
let _ = 1.0 - 2.0 < f32::EPSILON; | ||
|
||
let _ = f32::EPSILON > (a - b); | ||
let _ = f32::EPSILON > a - b; | ||
let _ = f32::EPSILON > a - b.abs(); | ||
let _ = f64::EPSILON > (a as f64 - b as f64); | ||
let _ = f32::EPSILON > 1.0 - 2.0; | ||
|
||
// those are correct | ||
let _ = (a - b).abs() < f32::EPSILON; | ||
let _ = (a as f64 - b as f64).abs() < f64::EPSILON; | ||
|
||
let _ = f32::EPSILON > (a - b).abs(); | ||
let _ = f64::EPSILON > (a as f64 - b as f64).abs(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:4:5 | ||
| | ||
LL | (a - b) < f32::EPSILON | ||
| ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` | ||
| | ||
= note: `-D clippy::float-equality-without-abs` implied by `-D warnings` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:13:13 | ||
| | ||
LL | let _ = (a - b) < f32::EPSILON; | ||
| ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:14:13 | ||
| | ||
LL | let _ = a - b < f32::EPSILON; | ||
| ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:15:13 | ||
| | ||
LL | let _ = a - b.abs() < f32::EPSILON; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:16:13 | ||
| | ||
LL | let _ = (a as f64 - b as f64) < f64::EPSILON; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:17:13 | ||
| | ||
LL | let _ = 1.0 - 2.0 < f32::EPSILON; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:19:13 | ||
| | ||
LL | let _ = f32::EPSILON > (a - b); | ||
| ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:20:13 | ||
| | ||
LL | let _ = f32::EPSILON > a - b; | ||
| ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:21:13 | ||
| | ||
LL | let _ = f32::EPSILON > a - b.abs(); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:22:13 | ||
| | ||
LL | let _ = f64::EPSILON > (a as f64 - b as f64); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` | ||
|
||
error: float equality check without `.abs()` | ||
--> $DIR/float_equality_without_abs.rs:23:13 | ||
| | ||
LL | let _ = f32::EPSILON > 1.0 - 2.0; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` | ||
|
||
error: aborting due to 11 previous errors | ||
|