use if_chain::if_chain;
-use matches::matches;
-use rustc::ty;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::{ExpnKind, Span};
use crate::consts::{constant, Constant};
use crate::utils::sugg::Sugg;
use crate::utils::{
- get_item_name, get_parent_expr, implements_trait, in_constant, is_integer_const, iter_input_pats,
+ get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq,
};
/// dereferences, e.g., changing `*x` to `x` within the function.
///
/// **Example:**
- /// ```rust
+ /// ```rust,ignore
+ /// // Bad
/// fn foo(ref x: u8) -> bool {
/// true
/// }
+ ///
+ /// // Good
+ /// fn foo(x: &u8) -> bool {
+ /// true
+ /// }
/// ```
pub TOPLEVEL_REF_ARG,
style,
///
/// **Example:**
/// ```rust
- /// # use core::f32::NAN;
/// # let x = 1.0;
///
- /// if x == NAN { }
+ /// // Bad
+ /// if x == f32::NAN { }
+ ///
+ /// // Good
+ /// if x.is_nan() { }
/// ```
pub CMP_NAN,
correctness,
/// ```rust
/// let x = 1.2331f64;
/// let y = 1.2332f64;
+ ///
+ /// // Bad
/// if y == 1.23f64 { }
/// if y != x {} // where both are floats
+ ///
+ /// // Good
+ /// let error = 0.01f64; // Use an epsilon for comparison
+ /// if (y - 1.23f64).abs() < error { }
+ /// if (y - x).abs() > error { }
/// ```
pub FLOAT_CMP,
correctness,
/// **Example:**
///
/// ```rust
+ /// // Bad
/// let a = 0 as *const u32;
+ ///
+ /// // Good
+ /// let a = std::ptr::null::<u32>();
/// ```
pub ZERO_PTR,
style,
/// ```rust
/// let x: f64 = 1.0;
/// const ONE: f64 = 1.00;
- /// x == ONE; // where both are floats
+ ///
+ /// // Bad
+ /// if x == ONE { } // where both are floats
+ ///
+ /// // Good
+ /// let error = 0.1f64; // Use an epsilon for comparison
+ /// if (x - ONE).abs() < error { }
/// ```
pub FLOAT_CMP_CONST,
restriction,
return;
}
for arg in iter_input_pats(decl, body) {
- match arg.pat.kind {
- PatKind::Binding(BindingAnnotation::Ref, ..) | PatKind::Binding(BindingAnnotation::RefMut, ..) => {
- span_lint(
- cx,
- TOPLEVEL_REF_ARG,
- arg.pat.span,
- "`ref` directly on a function argument is ignored. Consider using a reference type \
- instead.",
- );
- },
- _ => {},
+ if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
+ span_lint(
+ cx,
+ TOPLEVEL_REF_ARG,
+ arg.pat.span,
+ "`ref` directly on a function argument is ignored. \
+ Consider using a reference type instead.",
+ );
}
}
}
if let StmtKind::Local(ref local) = stmt.kind;
if let PatKind::Binding(an, .., name, None) = local.pat.kind;
if let Some(ref init) = local.init;
+ if !higher::is_from_for_desugar(local);
then {
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
let sugg_init = if init.span.from_expansion() {
init.hir_id,
local.pat.span,
"`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
- |db| {
- db.span_suggestion(
+ |diag| {
+ diag.span_suggestion(
stmt.span,
"try",
format!(
SHORT_CIRCUIT_STATEMENT,
stmt.span,
"boolean short circuit operator in statement may be clearer using an explicit test",
- |db| {
+ |diag| {
let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
- db.span_suggestion(
+ diag.span_suggestion(
stmt.span,
"replace it with",
format!(
return;
}
}
- let (lint, msg) = if is_named_constant(cx, left) || is_named_constant(cx, right) {
- (FLOAT_CMP_CONST, "strict comparison of `f32` or `f64` constant")
- } else {
- (FLOAT_CMP, "strict comparison of `f32` or `f64`")
- };
- span_lint_and_then(cx, lint, expr.span, msg, |db| {
+ let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
+ let (lint, msg) = get_lint_and_message(
+ is_named_constant(cx, left) || is_named_constant(cx, right),
+ is_comparing_arrays,
+ );
+ span_lint_and_then(cx, lint, expr.span, msg, |diag| {
let lhs = Sugg::hir(cx, left, "..");
let rhs = Sugg::hir(cx, right, "..");
- db.span_suggestion(
- expr.span,
- "consider comparing them within some error",
- format!(
- "({}).abs() {} error",
- lhs - rhs,
- if op == BinOpKind::Eq { '<' } else { '>' }
- ),
- Applicability::HasPlaceholders, // snippet
- );
- db.span_note(expr.span, "`std::f32::EPSILON` and `std::f64::EPSILON` are available.");
+ if !is_comparing_arrays {
+ diag.span_suggestion(
+ expr.span,
+ "consider comparing them within some error",
+ format!(
+ "({}).abs() {} error",
+ lhs - rhs,
+ if op == BinOpKind::Eq { '<' } else { '>' }
+ ),
+ Applicability::HasPlaceholders, // snippet
+ );
+ }
+ diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`");
});
} else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) {
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
},
_ => {},
}
- if in_attributes_expansion(expr) {
- // Don't lint things expanded by #[derive(...)], etc
+ if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
+ // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
return;
}
let binding = match expr.kind {
}
}
+fn get_lint_and_message(
+ is_comparing_constants: bool,
+ is_comparing_arrays: bool,
+) -> (&'static rustc_lint::Lint, &'static str) {
+ if is_comparing_constants {
+ (
+ FLOAT_CMP_CONST,
+ if is_comparing_arrays {
+ "strict comparison of `f32` or `f64` constant arrays"
+ } else {
+ "strict comparison of `f32` or `f64` constant"
+ },
+ )
+ } else {
+ (
+ FLOAT_CMP,
+ if is_comparing_arrays {
+ "strict comparison of `f32` or `f64` arrays"
+ } else {
+ "strict comparison of `f32` or `f64`"
+ },
+ )
+ }
+}
+
fn check_nan(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
if_chain! {
if !in_constant(cx, cmp_expr.hir_id);
cx,
CMP_NAN,
cmp_expr.span,
- "doomed comparison with `NAN`, use `std::{f32,f64}::is_nan()` instead",
+ "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
);
}
}
match constant(cx, cx.tables, expr) {
Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
+ Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
+ Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
+ Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
+ _ => false,
+ }),
_ => false,
}
}
}
if_chain! {
- if let ExprKind::MethodCall(ref method_name, _, ref expressions) = expr.kind;
+ if let ExprKind::MethodCall(ref method_name, _, ref expressions, _) = expr.kind;
if sym!(signum) == method_name.ident.name;
// Check that the receiver of the signum() is a float (expressions[0] is the receiver of
// the method call)
}
fn is_float(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
- matches!(walk_ptrs_ty(cx.tables.expr_ty(expr)).kind, ty::Float(_))
+ let value = &walk_ptrs_ty(cx.tables.expr_ty(expr)).kind;
+
+ if let ty::Array(arr_ty, _) = value {
+ return matches!(arr_ty.kind, ty::Float(_));
+ };
+
+ matches!(value, ty::Float(_))
+}
+
+fn is_array(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
+ matches!(&walk_ptrs_ty(cx.tables.expr_ty(expr)).kind, ty::Array(_, _))
}
fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) {
let (arg_ty, snip) = match expr.kind {
- ExprKind::MethodCall(.., ref args) if args.len() == 1 => {
+ ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => {
if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) {
(cx.tables.expr_ty_adjusted(&args[0]), snippet(cx, args[0].span, ".."))
} else {
CMP_OWNED,
lint_span,
"this creates an owned instance just for comparison",
- |db| {
+ |diag| {
// This also catches `PartialEq` implementations that call `to_owned`.
if other_gets_derefed {
- db.span_label(lint_span, "try implementing the comparison without allocating");
+ diag.span_label(lint_span, "try implementing the comparison without allocating");
return;
}
snip.to_string()
};
- db.span_suggestion(
+ diag.span_suggestion(
lint_span,
"try",
try_hint,