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,
/// ```rust
/// # let x = 1.0;
///
+ /// // 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!(
is_named_constant(cx, left) || is_named_constant(cx, right),
is_comparing_arrays,
);
- span_lint_and_then(cx, lint, expr.span, msg, |db| {
+ span_lint_and_then(cx, lint, expr.span, msg, |diag| {
let lhs = Sugg::hir(cx, left, "..");
let rhs = Sugg::hir(cx, right, "..");
if !is_comparing_arrays {
- db.span_suggestion(
+ diag.span_suggestion(
expr.span,
"consider comparing them within some error",
format!(
Applicability::HasPlaceholders, // snippet
);
}
- db.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`");
+ 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 {
}
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 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,