mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
Auto merge of #8921 - Jarcho:merge_passes, r=llogiq
Add `Operators` lint pass changelog: None
This commit is contained in:
commit
23c6765276
@ -1,235 +0,0 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{binop_traits, sugg};
|
||||
use clippy_utils::{eq_expr_value, trait_ref_of_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `a = a op b` or `a = b commutative_op a`
|
||||
/// patterns.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// These can be written as the shorter `a op= b`.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// While forbidden by the spec, `OpAssign` traits may have
|
||||
/// implementations that differ from the regular `Op` impl.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// let b = 0;
|
||||
/// // ...
|
||||
///
|
||||
/// a = a + b;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// let b = 0;
|
||||
/// // ...
|
||||
///
|
||||
/// a += b;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub ASSIGN_OP_PATTERN,
|
||||
style,
|
||||
"assigning the result of an operation on a variable to that same variable"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `a op= a op b` or `a op= b op a` patterns.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Most likely these are bugs where one meant to write `a
|
||||
/// op= b`.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Clippy cannot know for sure if `a op= a op b` should have
|
||||
/// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
|
||||
/// If `a op= a op b` is really the correct behavior it should be
|
||||
/// written as `a = a op a op b` as it's less confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// let b = 2;
|
||||
/// // ...
|
||||
/// a += a + b;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MISREFACTORED_ASSIGN_OP,
|
||||
suspicious,
|
||||
"having a variable on both sides of an assign op"
|
||||
}
|
||||
|
||||
declare_lint_pass!(AssignOps => [ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AssignOps {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
match &expr.kind {
|
||||
hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
|
||||
if op.node != binop.node {
|
||||
return;
|
||||
}
|
||||
// lhs op= l op r
|
||||
if eq_expr_value(cx, lhs, l) {
|
||||
lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
|
||||
}
|
||||
// lhs op= l commutative_op r
|
||||
if is_commutative(op.node) && eq_expr_value(cx, lhs, r) {
|
||||
lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Assign(assignee, e, _) => {
|
||||
if let hir::ExprKind::Binary(op, l, r) = &e.kind {
|
||||
let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
|
||||
let ty = cx.typeck_results().expr_ty(assignee);
|
||||
let rty = cx.typeck_results().expr_ty(rhs);
|
||||
if_chain! {
|
||||
if let Some((_, lang_item)) = binop_traits(op.node);
|
||||
if let Ok(trait_id) = cx.tcx.lang_items().require(lang_item);
|
||||
let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
|
||||
if trait_ref_of_method(cx, parent_fn)
|
||||
.map_or(true, |t| t.path.res.def_id() != trait_id);
|
||||
if implements_trait(cx, ty, trait_id, &[rty.into()]);
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
ASSIGN_OP_PATTERN,
|
||||
expr.span,
|
||||
"manual implementation of an assign operation",
|
||||
|diag| {
|
||||
if let (Some(snip_a), Some(snip_r)) =
|
||||
(snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
|
||||
{
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"replace it with",
|
||||
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut visitor = ExprVisitor {
|
||||
assignee,
|
||||
counter: 0,
|
||||
cx,
|
||||
};
|
||||
|
||||
walk_expr(&mut visitor, e);
|
||||
|
||||
if visitor.counter == 1 {
|
||||
// a = a op b
|
||||
if eq_expr_value(cx, assignee, l) {
|
||||
lint(assignee, r);
|
||||
}
|
||||
// a = b commutative_op a
|
||||
// Limited to primitive type as these ops are know to be commutative
|
||||
if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
|
||||
match op.node {
|
||||
hir::BinOpKind::Add
|
||||
| hir::BinOpKind::Mul
|
||||
| hir::BinOpKind::And
|
||||
| hir::BinOpKind::Or
|
||||
| hir::BinOpKind::BitXor
|
||||
| hir::BinOpKind::BitAnd
|
||||
| hir::BinOpKind::BitOr => {
|
||||
lint(assignee, l);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_misrefactored_assign_op(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
op: hir::BinOp,
|
||||
rhs: &hir::Expr<'_>,
|
||||
assignee: &hir::Expr<'_>,
|
||||
rhs_other: &hir::Expr<'_>,
|
||||
) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MISREFACTORED_ASSIGN_OP,
|
||||
expr.span,
|
||||
"variable appears on both sides of an assignment operation",
|
||||
|diag| {
|
||||
if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
|
||||
let a = &sugg::Sugg::hir(cx, assignee, "..");
|
||||
let r = &sugg::Sugg::hir(cx, rhs, "..");
|
||||
let long = format!("{} = {}", snip_a, sugg::make_binop(op.node.into(), a, r));
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
&format!(
|
||||
"did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
|
||||
snip_a,
|
||||
snip_a,
|
||||
op.node.as_str(),
|
||||
snip_r,
|
||||
long
|
||||
),
|
||||
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"or",
|
||||
long,
|
||||
Applicability::MaybeIncorrect, // snippet
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn is_commutative(op: hir::BinOpKind) -> bool {
|
||||
use rustc_hir::BinOpKind::{
|
||||
Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
|
||||
};
|
||||
match op {
|
||||
Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
|
||||
Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
|
||||
}
|
||||
}
|
||||
|
||||
struct ExprVisitor<'a, 'tcx> {
|
||||
assignee: &'a hir::Expr<'a>,
|
||||
counter: u8,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if eq_expr_value(self.cx, self.assignee, expr) {
|
||||
self.counter += 1;
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
//! Lint on unnecessary double comparisons. Some examples:
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eq_expr_value;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for double comparisons that could be simplified to a single expression.
|
||||
///
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// # let y = 2;
|
||||
/// if x == y || x < y {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// # let y = 2;
|
||||
/// if x <= y {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DOUBLE_COMPARISONS,
|
||||
complexity,
|
||||
"unnecessary double comparisons that can be simplified"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]);
|
||||
|
||||
impl<'tcx> DoubleComparisons {
|
||||
#[expect(clippy::similar_names)]
|
||||
fn check_binop(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
|
||||
let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
|
||||
(ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
|
||||
(lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) {
|
||||
return;
|
||||
}
|
||||
macro_rules! lint_double_comparison {
|
||||
($op:tt) => {{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability);
|
||||
let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability);
|
||||
let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DOUBLE_COMPARISONS,
|
||||
span,
|
||||
"this binary expression can be simplified",
|
||||
"try",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}};
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
match (op, lkind, rkind) {
|
||||
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
|
||||
lint_double_comparison!(<=);
|
||||
},
|
||||
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => {
|
||||
lint_double_comparison!(>=);
|
||||
},
|
||||
(BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => {
|
||||
lint_double_comparison!(!=);
|
||||
},
|
||||
(BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
|
||||
lint_double_comparison!(==);
|
||||
},
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DoubleComparisons {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(ref kind, lhs, rhs) = expr.kind {
|
||||
Self::check_binop(cx, kind.node, lhs, rhs, expr.span);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calculation of subsecond microseconds or milliseconds
|
||||
/// from other `Duration` methods.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's more concise to call `Duration::subsec_micros()` or
|
||||
/// `Duration::subsec_millis()` than to calculate them.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::time::Duration;
|
||||
/// # let duration = Duration::new(5, 0);
|
||||
/// let micros = duration.subsec_nanos() / 1_000;
|
||||
/// let millis = duration.subsec_nanos() / 1_000_000;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::time::Duration;
|
||||
/// # let duration = Duration::new(5, 0);
|
||||
/// let micros = duration.subsec_micros();
|
||||
/// let millis = duration.subsec_millis();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DURATION_SUBSEC,
|
||||
complexity,
|
||||
"checks for calculation of subsecond microseconds or milliseconds"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DurationSubsec {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, left, right) = expr.kind;
|
||||
if let ExprKind::MethodCall(method_path, args, _) = left.kind;
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), sym::Duration);
|
||||
if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right);
|
||||
then {
|
||||
let suggested_fn = match (method_path.ident.as_str(), divisor) {
|
||||
("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
|
||||
("subsec_nanos", 1_000) => "subsec_micros",
|
||||
_ => return,
|
||||
};
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DURATION_SUBSEC,
|
||||
expr.span,
|
||||
&format!("calling `{}()` is more concise than this calculation", suggested_fn),
|
||||
"try",
|
||||
format!(
|
||||
"{}.{}()",
|
||||
snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
|
||||
suggested_fn
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,319 +0,0 @@
|
||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
|
||||
use clippy_utils::get_enclosing_block;
|
||||
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for equal operands to comparison, logical and
|
||||
/// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
|
||||
/// `||`, `&`, `|`, `^`, `-` and `/`).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is usually just a typo or a copy and paste error.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False negatives: We had some false positives regarding
|
||||
/// calls (notably [racer](https://github.com/phildawes/racer) had one instance
|
||||
/// of `x.pop() && x.pop()`), so we removed matching any function or method
|
||||
/// calls. We may introduce a list of known pure functions in the future.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// if x + 1 == x + 1 {}
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// # let a = 3;
|
||||
/// # let b = 4;
|
||||
/// assert_eq!(a, a);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EQ_OP,
|
||||
correctness,
|
||||
"equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for arguments to `==` which have their address
|
||||
/// taken to satisfy a bound
|
||||
/// and suggests to dereference the other argument instead
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is more idiomatic to dereference the other argument.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// &x == y
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// x == *y
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub OP_REF,
|
||||
style,
|
||||
"taking a reference to satisfy the type constraints on `==`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for EqOp {
|
||||
#[expect(clippy::similar_names, clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
|
||||
let name = cx.tcx.item_name(macro_call.def_id);
|
||||
matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
|
||||
.then(|| (macro_call, name))
|
||||
});
|
||||
if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn);
|
||||
if eq_expr_value(cx, lhs, rhs);
|
||||
if macro_call.is_local();
|
||||
if !is_in_test_function(cx.tcx, e.hir_id);
|
||||
then {
|
||||
span_lint(
|
||||
cx,
|
||||
EQ_OP,
|
||||
lhs.span.to(rhs.span),
|
||||
&format!("identical args used in this `{}!` macro call", macro_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
if let ExprKind::Binary(op, left, right) = e.kind {
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
|
||||
if let ExprKind::Unary(_, expr) = *expr_kind {
|
||||
expr.span.from_expansion()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
|
||||
return;
|
||||
}
|
||||
if is_useless_with_eq_exprs(op.node.into())
|
||||
&& eq_expr_value(cx, left, right)
|
||||
&& !is_in_test_function(cx.tcx, e.hir_id)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
EQ_OP,
|
||||
e.span,
|
||||
&format!("equal expressions as operands to `{}`", op.node.as_str()),
|
||||
);
|
||||
return;
|
||||
}
|
||||
let (trait_id, requires_ref) = match op.node {
|
||||
BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
|
||||
BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
|
||||
BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
|
||||
BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
|
||||
BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
|
||||
// don't lint short circuiting ops
|
||||
BinOpKind::And | BinOpKind::Or => return,
|
||||
BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
|
||||
BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
|
||||
BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
|
||||
BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
|
||||
BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
|
||||
BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
|
||||
BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
|
||||
(cx.tcx.lang_items().partial_ord_trait(), true)
|
||||
},
|
||||
};
|
||||
if let Some(trait_id) = trait_id {
|
||||
match (&left.kind, &right.kind) {
|
||||
// do not suggest to dereference literals
|
||||
(&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
|
||||
// &foo == &bar
|
||||
(&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
|
||||
let lty = cx.typeck_results().expr_ty(l);
|
||||
let rty = cx.typeck_results().expr_ty(r);
|
||||
let lcpy = is_copy(cx, lty);
|
||||
let rcpy = is_copy(cx, rty);
|
||||
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
|
||||
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|
||||
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
|
||||
{
|
||||
return; // Don't lint
|
||||
}
|
||||
}
|
||||
// either operator autorefs or both args are copyable
|
||||
if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OP_REF,
|
||||
e.span,
|
||||
"needlessly taken reference of both operands",
|
||||
|diag| {
|
||||
let lsnip = snippet(cx, l.span, "...").to_string();
|
||||
let rsnip = snippet(cx, r.span, "...").to_string();
|
||||
multispan_sugg(
|
||||
diag,
|
||||
"use the values directly",
|
||||
vec![(left.span, lsnip), (right.span, rsnip)],
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if lcpy
|
||||
&& !rcpy
|
||||
&& implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OP_REF,
|
||||
e.span,
|
||||
"needlessly taken reference of left operand",
|
||||
|diag| {
|
||||
let lsnip = snippet(cx, l.span, "...").to_string();
|
||||
diag.span_suggestion(
|
||||
left.span,
|
||||
"use the left value directly",
|
||||
lsnip,
|
||||
Applicability::MaybeIncorrect, // FIXME #2597
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if !lcpy
|
||||
&& rcpy
|
||||
&& implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OP_REF,
|
||||
e.span,
|
||||
"needlessly taken reference of right operand",
|
||||
|diag| {
|
||||
let rsnip = snippet(cx, r.span, "...").to_string();
|
||||
diag.span_suggestion(
|
||||
right.span,
|
||||
"use the right value directly",
|
||||
rsnip,
|
||||
Applicability::MaybeIncorrect, // FIXME #2597
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
// &foo == bar
|
||||
(&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => {
|
||||
let lty = cx.typeck_results().expr_ty(l);
|
||||
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
|
||||
let rty = cx.typeck_results().expr_ty(right);
|
||||
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|
||||
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
|
||||
{
|
||||
return; // Don't lint
|
||||
}
|
||||
}
|
||||
let lcpy = is_copy(cx, lty);
|
||||
if (requires_ref || lcpy)
|
||||
&& implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OP_REF,
|
||||
e.span,
|
||||
"needlessly taken reference of left operand",
|
||||
|diag| {
|
||||
let lsnip = snippet(cx, l.span, "...").to_string();
|
||||
diag.span_suggestion(
|
||||
left.span,
|
||||
"use the left value directly",
|
||||
lsnip,
|
||||
Applicability::MaybeIncorrect, // FIXME #2597
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
// foo == &bar
|
||||
(_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
|
||||
let rty = cx.typeck_results().expr_ty(r);
|
||||
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
|
||||
let lty = cx.typeck_results().expr_ty(left);
|
||||
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|
||||
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
|
||||
{
|
||||
return; // Don't lint
|
||||
}
|
||||
}
|
||||
let rcpy = is_copy(cx, rty);
|
||||
if (requires_ref || rcpy)
|
||||
&& implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
|
||||
{
|
||||
span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
|
||||
let rsnip = snippet(cx, r.span, "...").to_string();
|
||||
diag.span_suggestion(
|
||||
right.span,
|
||||
"use the right value directly",
|
||||
rsnip,
|
||||
Applicability::MaybeIncorrect, // FIXME #2597
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn in_impl<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
bin_op: DefId,
|
||||
) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> {
|
||||
if_chain! {
|
||||
if let Some(block) = get_enclosing_block(cx, e.hir_id);
|
||||
if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id());
|
||||
let item = cx.tcx.hir().expect_item(impl_def_id.expect_local());
|
||||
if let ItemKind::Impl(item) = &item.kind;
|
||||
if let Some(of_trait) = &item.of_trait;
|
||||
if let Some(seg) = of_trait.path.segments.last();
|
||||
if let Some(Res::Def(_, trait_id)) = seg.res;
|
||||
if trait_id == bin_op;
|
||||
if let Some(generic_args) = seg.args;
|
||||
if let Some(GenericArg::Type(other_ty)) = generic_args.args.last();
|
||||
|
||||
then {
|
||||
Some((item.self_ty, other_ty))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let ty::Adt(adt_def, _) = middle_ty.kind();
|
||||
if let Some(local_did) = adt_def.did().as_local();
|
||||
let item = cx.tcx.hir().expect_item(local_did);
|
||||
let middle_ty_id = item.def_id.to_def_id();
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
|
||||
if let Res::Def(_, hir_ty_id) = path.res;
|
||||
|
||||
then {
|
||||
hir_ty_id == middle_ty_id
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
use clippy_utils::consts::{constant_simple, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::same_type_and_consts;
|
||||
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TypeckResults;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for erasing operations, e.g., `x * 0`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The whole expression can be replaced by zero.
|
||||
/// This is most likely not the intended outcome and should probably be
|
||||
/// corrected
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = 1;
|
||||
/// 0 / x;
|
||||
/// 0 * x;
|
||||
/// x & 0;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub ERASING_OP,
|
||||
correctness,
|
||||
"using erasing operations, e.g., `x * 0` or `y & 0`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ErasingOp => [ERASING_OP]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ErasingOp {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::Binary(ref cmp, left, right) = e.kind {
|
||||
let tck = cx.typeck_results();
|
||||
match cmp.node {
|
||||
BinOpKind::Mul | BinOpKind::BitAnd => {
|
||||
check(cx, tck, left, right, e);
|
||||
check(cx, tck, right, left, e);
|
||||
},
|
||||
BinOpKind::Div => check(cx, tck, left, right, e),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
|
||||
let input_ty = tck.expr_ty(input).peel_refs();
|
||||
let output_ty = tck.expr_ty(output).peel_refs();
|
||||
!same_type_and_consts(input_ty, output_ty)
|
||||
}
|
||||
|
||||
fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
tck: &TypeckResults<'tcx>,
|
||||
op: &Expr<'tcx>,
|
||||
other: &Expr<'tcx>,
|
||||
parent: &Expr<'tcx>,
|
||||
) {
|
||||
if constant_simple(cx, tck, op) == Some(Constant::Int(0)) {
|
||||
if different_types(tck, other, parent) {
|
||||
return;
|
||||
}
|
||||
span_lint(
|
||||
cx,
|
||||
ERASING_OP,
|
||||
parent.span,
|
||||
"this operation will always return zero. This is likely not the intended outcome",
|
||||
);
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{match_def_path, paths, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::util::parser::AssocOp;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
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 unnecessary. 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
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub FLOAT_EQUALITY_WITHOUT_ABS,
|
||||
suspicious,
|
||||
"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, left, 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 subtraction
|
||||
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 let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
|
||||
if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
|
||||
|
||||
// values of the subtractions 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 {
|
||||
let sug_l = sugg::Sugg::hir(cx, val_l, "..");
|
||||
let sug_r = sugg::Sugg::hir(cx, val_r, "..");
|
||||
// format the suggestion
|
||||
let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par());
|
||||
// spans the lint
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
FLOAT_EQUALITY_WITHOUT_ABS,
|
||||
expr.span,
|
||||
"float equality check without `.abs()`",
|
||||
| diag | {
|
||||
diag.span_suggestion(
|
||||
lhs.span,
|
||||
"add `.abs()`",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for division of integers
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// When outside of some very specific algorithms,
|
||||
/// integer division is very often a mistake because it discards the
|
||||
/// remainder.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = 3 / 2;
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = 3f32 / 2f32;
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
#[clippy::version = "1.37.0"]
|
||||
pub INTEGER_DIVISION,
|
||||
restriction,
|
||||
"integer division may cause loss of precision"
|
||||
}
|
||||
|
||||
declare_lint_pass!(IntegerDivision => [INTEGER_DIVISION]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IntegerDivision {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if is_integer_division(cx, expr) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
INTEGER_DIVISION,
|
||||
expr.span,
|
||||
"integer division",
|
||||
None,
|
||||
"division of integers may cause loss of precision. consider using floats",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_integer_division<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Binary(binop, left, right) = &expr.kind;
|
||||
if binop.node == hir::BinOpKind::Div;
|
||||
then {
|
||||
let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right));
|
||||
return left_ty.is_integral() && right_ty.is_integral();
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
@ -3,12 +3,9 @@
|
||||
// Manual edits will be overwritten.
|
||||
|
||||
store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
|
||||
LintId::of(approx_const::APPROX_CONSTANT),
|
||||
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||
LintId::of(assign_ops::ASSIGN_OP_PATTERN),
|
||||
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
|
||||
LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
|
||||
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(attrs::DEPRECATED_CFG_ATTR),
|
||||
@ -18,8 +15,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
|
||||
LintId::of(bit_mask::BAD_BIT_MASK),
|
||||
LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
|
||||
LintId::of(blacklisted_name::BLACKLISTED_NAME),
|
||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
@ -53,7 +48,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(disallowed_types::DISALLOWED_TYPES),
|
||||
LintId::of(doc::MISSING_SAFETY_DOC),
|
||||
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
|
||||
LintId::of(double_comparison::DOUBLE_COMPARISONS),
|
||||
LintId::of(double_parens::DOUBLE_PARENS),
|
||||
LintId::of(drop_forget_ref::DROP_COPY),
|
||||
LintId::of(drop_forget_ref::DROP_NON_DROP),
|
||||
@ -63,18 +57,13 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(drop_forget_ref::FORGET_REF),
|
||||
LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
|
||||
LintId::of(duplicate_mod::DUPLICATE_MOD),
|
||||
LintId::of(duration_subsec::DURATION_SUBSEC),
|
||||
LintId::of(entry::MAP_ENTRY),
|
||||
LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
|
||||
LintId::of(enum_variants::ENUM_VARIANT_NAMES),
|
||||
LintId::of(enum_variants::MODULE_INCEPTION),
|
||||
LintId::of(eq_op::EQ_OP),
|
||||
LintId::of(eq_op::OP_REF),
|
||||
LintId::of(erasing_op::ERASING_OP),
|
||||
LintId::of(escape::BOXED_LOCAL),
|
||||
LintId::of(eta_reduction::REDUNDANT_CLOSURE),
|
||||
LintId::of(explicit_write::EXPLICIT_WRITE),
|
||||
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
|
||||
LintId::of(float_literal::EXCESSIVE_PRECISION),
|
||||
LintId::of(format::USELESS_FORMAT),
|
||||
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
|
||||
@ -94,7 +83,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
||||
LintId::of(get_first::GET_FIRST),
|
||||
LintId::of(identity_op::IDENTITY_OP),
|
||||
LintId::of(if_let_mutex::IF_LET_MUTEX),
|
||||
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
|
||||
LintId::of(infinite_iter::INFINITE_ITER),
|
||||
@ -224,9 +212,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(methods::WRONG_SELF_CONVENTION),
|
||||
LintId::of(methods::ZST_OFFSET),
|
||||
LintId::of(minmax::MIN_MAX),
|
||||
LintId::of(misc::CMP_NAN),
|
||||
LintId::of(misc::CMP_OWNED),
|
||||
LintId::of(misc::MODULO_ONE),
|
||||
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
|
||||
LintId::of(misc::TOPLEVEL_REF_ARG),
|
||||
LintId::of(misc::ZERO_PTR),
|
||||
@ -260,6 +245,23 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(operators::ASSIGN_OP_PATTERN),
|
||||
LintId::of(operators::BAD_BIT_MASK),
|
||||
LintId::of(operators::CMP_NAN),
|
||||
LintId::of(operators::CMP_OWNED),
|
||||
LintId::of(operators::DOUBLE_COMPARISONS),
|
||||
LintId::of(operators::DURATION_SUBSEC),
|
||||
LintId::of(operators::EQ_OP),
|
||||
LintId::of(operators::ERASING_OP),
|
||||
LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
|
||||
LintId::of(operators::IDENTITY_OP),
|
||||
LintId::of(operators::INEFFECTIVE_BIT_MASK),
|
||||
LintId::of(operators::MISREFACTORED_ASSIGN_OP),
|
||||
LintId::of(operators::MODULO_ONE),
|
||||
LintId::of(operators::OP_REF),
|
||||
LintId::of(operators::PTR_EQ),
|
||||
LintId::of(operators::SELF_ASSIGNMENT),
|
||||
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
|
||||
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
|
||||
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
|
||||
@ -268,7 +270,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
|
||||
LintId::of(ptr::MUT_FROM_REF),
|
||||
LintId::of(ptr::PTR_ARG),
|
||||
LintId::of(ptr_eq::PTR_EQ),
|
||||
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(question_mark::QUESTION_MARK),
|
||||
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
|
||||
@ -286,7 +287,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(repeat_once::REPEAT_ONCE),
|
||||
LintId::of(returns::LET_AND_RETURN),
|
||||
LintId::of(returns::NEEDLESS_RETURN),
|
||||
LintId::of(self_assignment::SELF_ASSIGNMENT),
|
||||
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
|
||||
LintId::of(serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
|
@ -10,13 +10,10 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(casts::CHAR_LIT_AS_U8),
|
||||
LintId::of(casts::UNNECESSARY_CAST),
|
||||
LintId::of(derivable_impls::DERIVABLE_IMPLS),
|
||||
LintId::of(double_comparison::DOUBLE_COMPARISONS),
|
||||
LintId::of(double_parens::DOUBLE_PARENS),
|
||||
LintId::of(duration_subsec::DURATION_SUBSEC),
|
||||
LintId::of(explicit_write::EXPLICIT_WRITE),
|
||||
LintId::of(format::USELESS_FORMAT),
|
||||
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
||||
LintId::of(identity_op::IDENTITY_OP),
|
||||
LintId::of(int_plus_one::INT_PLUS_ONE),
|
||||
LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
|
||||
LintId::of(lifetimes::NEEDLESS_LIFETIMES),
|
||||
@ -71,6 +68,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
|
||||
LintId::of(no_effect::NO_EFFECT),
|
||||
LintId::of(no_effect::UNNECESSARY_OPERATION),
|
||||
LintId::of(operators::DOUBLE_COMPARISONS),
|
||||
LintId::of(operators::DURATION_SUBSEC),
|
||||
LintId::of(operators::IDENTITY_OP),
|
||||
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
|
||||
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
|
||||
LintId::of(precedence::PRECEDENCE),
|
||||
|
@ -3,14 +3,11 @@
|
||||
// Manual edits will be overwritten.
|
||||
|
||||
store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
|
||||
LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(approx_const::APPROX_CONSTANT),
|
||||
LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
|
||||
LintId::of(attrs::DEPRECATED_SEMVER),
|
||||
LintId::of(attrs::MISMATCHED_TARGET_OS),
|
||||
LintId::of(attrs::USELESS_ATTRIBUTE),
|
||||
LintId::of(bit_mask::BAD_BIT_MASK),
|
||||
LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
|
||||
LintId::of(booleans::LOGIC_BUG),
|
||||
LintId::of(casts::CAST_REF_TO_MUT),
|
||||
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
|
||||
@ -24,8 +21,6 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
||||
LintId::of(drop_forget_ref::FORGET_REF),
|
||||
LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
|
||||
LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
|
||||
LintId::of(eq_op::EQ_OP),
|
||||
LintId::of(erasing_op::ERASING_OP),
|
||||
LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
|
||||
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
|
||||
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
|
||||
@ -47,17 +42,22 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
||||
LintId::of(methods::UNINIT_ASSUMED_INIT),
|
||||
LintId::of(methods::ZST_OFFSET),
|
||||
LintId::of(minmax::MIN_MAX),
|
||||
LintId::of(misc::CMP_NAN),
|
||||
LintId::of(misc::MODULO_ONE),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(operators::BAD_BIT_MASK),
|
||||
LintId::of(operators::CMP_NAN),
|
||||
LintId::of(operators::EQ_OP),
|
||||
LintId::of(operators::ERASING_OP),
|
||||
LintId::of(operators::INEFFECTIVE_BIT_MASK),
|
||||
LintId::of(operators::MODULO_ONE),
|
||||
LintId::of(operators::SELF_ASSIGNMENT),
|
||||
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
|
||||
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
|
||||
LintId::of(ptr::MUT_FROM_REF),
|
||||
LintId::of(ranges::REVERSED_EMPTY_RANGES),
|
||||
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
|
||||
LintId::of(regex::INVALID_REGEX),
|
||||
LintId::of(self_assignment::SELF_ASSIGNMENT),
|
||||
LintId::of(serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(swap::ALMOST_SWAPPED),
|
||||
|
@ -35,7 +35,6 @@ store.register_lints(&[
|
||||
utils::internal_lints::PRODUCE_ICE,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::UNNECESSARY_SYMBOL_STR,
|
||||
absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
|
||||
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
|
||||
approx_const::APPROX_CONSTANT,
|
||||
as_conversions::AS_CONVERSIONS,
|
||||
@ -43,8 +42,6 @@ store.register_lints(&[
|
||||
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
|
||||
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
|
||||
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
|
||||
assign_ops::ASSIGN_OP_PATTERN,
|
||||
assign_ops::MISREFACTORED_ASSIGN_OP,
|
||||
async_yields_async::ASYNC_YIELDS_ASYNC,
|
||||
attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
@ -57,9 +54,6 @@ store.register_lints(&[
|
||||
await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
|
||||
await_holding_invalid::AWAIT_HOLDING_LOCK,
|
||||
await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
|
||||
bit_mask::BAD_BIT_MASK,
|
||||
bit_mask::INEFFECTIVE_BIT_MASK,
|
||||
bit_mask::VERBOSE_BIT_MASK,
|
||||
blacklisted_name::BLACKLISTED_NAME,
|
||||
blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
|
||||
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
|
||||
@ -128,7 +122,6 @@ store.register_lints(&[
|
||||
doc::MISSING_SAFETY_DOC,
|
||||
doc::NEEDLESS_DOCTEST_MAIN,
|
||||
doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
|
||||
double_comparison::DOUBLE_COMPARISONS,
|
||||
double_parens::DOUBLE_PARENS,
|
||||
drop_forget_ref::DROP_COPY,
|
||||
drop_forget_ref::DROP_NON_DROP,
|
||||
@ -138,7 +131,6 @@ store.register_lints(&[
|
||||
drop_forget_ref::FORGET_REF,
|
||||
drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
|
||||
duplicate_mod::DUPLICATE_MOD,
|
||||
duration_subsec::DURATION_SUBSEC,
|
||||
else_if_without_else::ELSE_IF_WITHOUT_ELSE,
|
||||
empty_drop::EMPTY_DROP,
|
||||
empty_enum::EMPTY_ENUM,
|
||||
@ -148,10 +140,7 @@ store.register_lints(&[
|
||||
enum_variants::ENUM_VARIANT_NAMES,
|
||||
enum_variants::MODULE_INCEPTION,
|
||||
enum_variants::MODULE_NAME_REPETITIONS,
|
||||
eq_op::EQ_OP,
|
||||
eq_op::OP_REF,
|
||||
equatable_if_let::EQUATABLE_IF_LET,
|
||||
erasing_op::ERASING_OP,
|
||||
escape::BOXED_LOCAL,
|
||||
eta_reduction::REDUNDANT_CLOSURE,
|
||||
eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
|
||||
@ -162,7 +151,6 @@ store.register_lints(&[
|
||||
exit::EXIT,
|
||||
explicit_write::EXPLICIT_WRITE,
|
||||
fallible_impl_from::FALLIBLE_IMPL_FROM,
|
||||
float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
|
||||
float_literal::EXCESSIVE_PRECISION,
|
||||
float_literal::LOSSY_FLOAT_LITERAL,
|
||||
floating_point_arithmetic::IMPRECISE_FLOPS,
|
||||
@ -188,7 +176,6 @@ store.register_lints(&[
|
||||
functions::TOO_MANY_LINES,
|
||||
future_not_send::FUTURE_NOT_SEND,
|
||||
get_first::GET_FIRST,
|
||||
identity_op::IDENTITY_OP,
|
||||
if_let_mutex::IF_LET_MUTEX,
|
||||
if_not_else::IF_NOT_ELSE,
|
||||
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
|
||||
@ -207,7 +194,6 @@ store.register_lints(&[
|
||||
init_numbered_fields::INIT_NUMBERED_FIELDS,
|
||||
inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
|
||||
int_plus_one::INT_PLUS_ONE,
|
||||
integer_division::INTEGER_DIVISION,
|
||||
invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
|
||||
items_after_statements::ITEMS_AFTER_STATEMENTS,
|
||||
iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
|
||||
@ -370,11 +356,6 @@ store.register_lints(&[
|
||||
methods::WRONG_SELF_CONVENTION,
|
||||
methods::ZST_OFFSET,
|
||||
minmax::MIN_MAX,
|
||||
misc::CMP_NAN,
|
||||
misc::CMP_OWNED,
|
||||
misc::FLOAT_CMP,
|
||||
misc::FLOAT_CMP_CONST,
|
||||
misc::MODULO_ONE,
|
||||
misc::SHORT_CIRCUIT_STATEMENT,
|
||||
misc::TOPLEVEL_REF_ARG,
|
||||
misc::USED_UNDERSCORE_BINDING,
|
||||
@ -398,7 +379,6 @@ store.register_lints(&[
|
||||
mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
|
||||
module_style::MOD_MODULE_FILES,
|
||||
module_style::SELF_NAMED_MODULE_FILES,
|
||||
modulo_arithmetic::MODULO_ARITHMETIC,
|
||||
mut_key::MUTABLE_KEY_TYPE,
|
||||
mut_mut::MUT_MUT,
|
||||
mut_mutex_lock::MUT_MUTEX_LOCK,
|
||||
@ -407,7 +387,6 @@ store.register_lints(&[
|
||||
mutex_atomic::MUTEX_ATOMIC,
|
||||
mutex_atomic::MUTEX_INTEGER,
|
||||
needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
|
||||
needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
|
||||
needless_bool::BOOL_COMPARISON,
|
||||
needless_bool::NEEDLESS_BOOL,
|
||||
needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
|
||||
@ -432,11 +411,34 @@ store.register_lints(&[
|
||||
non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
|
||||
non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
|
||||
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
|
||||
numeric_arithmetic::FLOAT_ARITHMETIC,
|
||||
numeric_arithmetic::INTEGER_ARITHMETIC,
|
||||
octal_escapes::OCTAL_ESCAPES,
|
||||
only_used_in_recursion::ONLY_USED_IN_RECURSION,
|
||||
open_options::NONSENSICAL_OPEN_OPTIONS,
|
||||
operators::ABSURD_EXTREME_COMPARISONS,
|
||||
operators::ASSIGN_OP_PATTERN,
|
||||
operators::BAD_BIT_MASK,
|
||||
operators::CMP_NAN,
|
||||
operators::CMP_OWNED,
|
||||
operators::DOUBLE_COMPARISONS,
|
||||
operators::DURATION_SUBSEC,
|
||||
operators::EQ_OP,
|
||||
operators::ERASING_OP,
|
||||
operators::FLOAT_ARITHMETIC,
|
||||
operators::FLOAT_CMP,
|
||||
operators::FLOAT_CMP_CONST,
|
||||
operators::FLOAT_EQUALITY_WITHOUT_ABS,
|
||||
operators::IDENTITY_OP,
|
||||
operators::INEFFECTIVE_BIT_MASK,
|
||||
operators::INTEGER_ARITHMETIC,
|
||||
operators::INTEGER_DIVISION,
|
||||
operators::MISREFACTORED_ASSIGN_OP,
|
||||
operators::MODULO_ARITHMETIC,
|
||||
operators::MODULO_ONE,
|
||||
operators::NEEDLESS_BITWISE_BOOL,
|
||||
operators::OP_REF,
|
||||
operators::PTR_EQ,
|
||||
operators::SELF_ASSIGNMENT,
|
||||
operators::VERBOSE_BIT_MASK,
|
||||
option_env_unwrap::OPTION_ENV_UNWRAP,
|
||||
option_if_let_else::OPTION_IF_LET_ELSE,
|
||||
overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
|
||||
@ -455,7 +457,6 @@ store.register_lints(&[
|
||||
ptr::INVALID_NULL_PTR_USAGE,
|
||||
ptr::MUT_FROM_REF,
|
||||
ptr::PTR_ARG,
|
||||
ptr_eq::PTR_EQ,
|
||||
ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
|
||||
pub_use::PUB_USE,
|
||||
question_mark::QUESTION_MARK,
|
||||
@ -483,7 +484,6 @@ store.register_lints(&[
|
||||
returns::LET_AND_RETURN,
|
||||
returns::NEEDLESS_RETURN,
|
||||
same_name_method::SAME_NAME_METHOD,
|
||||
self_assignment::SELF_ASSIGNMENT,
|
||||
self_named_constructors::SELF_NAMED_CONSTRUCTORS,
|
||||
semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
|
||||
serde_api::SERDE_API_MISUSE,
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(attrs::INLINE_ALWAYS),
|
||||
LintId::of(bit_mask::VERBOSE_BIT_MASK),
|
||||
LintId::of(borrow_as_ptr::BORROW_AS_PTR),
|
||||
LintId::of(bytecount::NAIVE_BYTECOUNT),
|
||||
LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
@ -65,17 +64,18 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(methods::INEFFICIENT_TO_STRING),
|
||||
LintId::of(methods::MAP_UNWRAP_OR),
|
||||
LintId::of(methods::UNNECESSARY_JOIN),
|
||||
LintId::of(misc::FLOAT_CMP),
|
||||
LintId::of(misc::USED_UNDERSCORE_BINDING),
|
||||
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
|
||||
LintId::of(mut_mut::MUT_MUT),
|
||||
LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
|
||||
LintId::of(needless_continue::NEEDLESS_CONTINUE),
|
||||
LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
|
||||
LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
|
||||
LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
|
||||
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
|
||||
LintId::of(non_expressive_names::SIMILAR_NAMES),
|
||||
LintId::of(operators::FLOAT_CMP),
|
||||
LintId::of(operators::NEEDLESS_BITWISE_BOOL),
|
||||
LintId::of(operators::VERBOSE_BIT_MASK),
|
||||
LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
|
||||
LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
|
||||
LintId::of(ranges::RANGE_MINUS_ONE),
|
||||
|
@ -22,7 +22,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
||||
LintId::of(methods::OR_FUN_CALL),
|
||||
LintId::of(methods::SINGLE_CHAR_PATTERN),
|
||||
LintId::of(methods::UNNECESSARY_TO_OWNED),
|
||||
LintId::of(misc::CMP_OWNED),
|
||||
LintId::of(operators::CMP_OWNED),
|
||||
LintId::of(redundant_clone::REDUNDANT_CLONE),
|
||||
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||
LintId::of(types::BOX_COLLECTION),
|
||||
|
@ -25,7 +25,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(implicit_return::IMPLICIT_RETURN),
|
||||
LintId::of(indexing_slicing::INDEXING_SLICING),
|
||||
LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
|
||||
LintId::of(integer_division::INTEGER_DIVISION),
|
||||
LintId::of(large_include_file::LARGE_INCLUDE_FILE),
|
||||
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
|
||||
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
|
||||
@ -39,7 +38,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(methods::FILETYPE_IS_FILE),
|
||||
LintId::of(methods::GET_UNWRAP),
|
||||
LintId::of(methods::UNWRAP_USED),
|
||||
LintId::of(misc::FLOAT_CMP_CONST),
|
||||
LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
|
||||
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
|
||||
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
|
||||
@ -49,9 +47,11 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
|
||||
LintId::of(module_style::MOD_MODULE_FILES),
|
||||
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
|
||||
LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
|
||||
LintId::of(numeric_arithmetic::FLOAT_ARITHMETIC),
|
||||
LintId::of(numeric_arithmetic::INTEGER_ARITHMETIC),
|
||||
LintId::of(operators::FLOAT_ARITHMETIC),
|
||||
LintId::of(operators::FLOAT_CMP_CONST),
|
||||
LintId::of(operators::INTEGER_ARITHMETIC),
|
||||
LintId::of(operators::INTEGER_DIVISION),
|
||||
LintId::of(operators::MODULO_ARITHMETIC),
|
||||
LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
|
||||
LintId::of(panic_unimplemented::PANIC),
|
||||
LintId::of(panic_unimplemented::TODO),
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||
LintId::of(assign_ops::ASSIGN_OP_PATTERN),
|
||||
LintId::of(blacklisted_name::BLACKLISTED_NAME),
|
||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
@ -23,7 +22,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
|
||||
LintId::of(enum_variants::ENUM_VARIANT_NAMES),
|
||||
LintId::of(enum_variants::MODULE_INCEPTION),
|
||||
LintId::of(eq_op::OP_REF),
|
||||
LintId::of(eta_reduction::REDUNDANT_CLOSURE),
|
||||
LintId::of(float_literal::EXCESSIVE_PRECISION),
|
||||
LintId::of(from_over_into::FROM_OVER_INTO),
|
||||
@ -98,9 +96,11 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
|
||||
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
|
||||
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
|
||||
LintId::of(operators::ASSIGN_OP_PATTERN),
|
||||
LintId::of(operators::OP_REF),
|
||||
LintId::of(operators::PTR_EQ),
|
||||
LintId::of(ptr::CMP_NULL),
|
||||
LintId::of(ptr::PTR_ARG),
|
||||
LintId::of(ptr_eq::PTR_EQ),
|
||||
LintId::of(question_mark::QUESTION_MARK),
|
||||
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
|
||||
LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
|
||||
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
|
||||
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
|
||||
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
|
||||
@ -16,7 +15,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
||||
LintId::of(drop_forget_ref::DROP_NON_DROP),
|
||||
LintId::of(drop_forget_ref::FORGET_NON_DROP),
|
||||
LintId::of(duplicate_mod::DUPLICATE_MOD),
|
||||
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
|
||||
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
|
||||
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
|
||||
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
|
||||
@ -29,6 +27,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
||||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||
LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
|
||||
LintId::of(operators::MISREFACTORED_ASSIGN_OP),
|
||||
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
|
@ -168,18 +168,15 @@ mod utils;
|
||||
mod renamed_lints;
|
||||
|
||||
// begin lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
mod absurd_extreme_comparisons;
|
||||
mod almost_complete_letter_range;
|
||||
mod approx_const;
|
||||
mod as_conversions;
|
||||
mod as_underscore;
|
||||
mod asm_syntax;
|
||||
mod assertions_on_constants;
|
||||
mod assign_ops;
|
||||
mod async_yields_async;
|
||||
mod attrs;
|
||||
mod await_holding_invalid;
|
||||
mod bit_mask;
|
||||
mod blacklisted_name;
|
||||
mod blocks_in_if_conditions;
|
||||
mod bool_assert_comparison;
|
||||
@ -212,11 +209,9 @@ mod disallowed_script_idents;
|
||||
mod disallowed_types;
|
||||
mod doc;
|
||||
mod doc_link_with_quotes;
|
||||
mod double_comparison;
|
||||
mod double_parens;
|
||||
mod drop_forget_ref;
|
||||
mod duplicate_mod;
|
||||
mod duration_subsec;
|
||||
mod else_if_without_else;
|
||||
mod empty_drop;
|
||||
mod empty_enum;
|
||||
@ -224,9 +219,7 @@ mod empty_structs_with_brackets;
|
||||
mod entry;
|
||||
mod enum_clike;
|
||||
mod enum_variants;
|
||||
mod eq_op;
|
||||
mod equatable_if_let;
|
||||
mod erasing_op;
|
||||
mod escape;
|
||||
mod eta_reduction;
|
||||
mod excessive_bools;
|
||||
@ -234,7 +227,6 @@ mod exhaustive_items;
|
||||
mod exit;
|
||||
mod explicit_write;
|
||||
mod fallible_impl_from;
|
||||
mod float_equality_without_abs;
|
||||
mod float_literal;
|
||||
mod floating_point_arithmetic;
|
||||
mod format;
|
||||
@ -247,7 +239,6 @@ mod from_str_radix_10;
|
||||
mod functions;
|
||||
mod future_not_send;
|
||||
mod get_first;
|
||||
mod identity_op;
|
||||
mod if_let_mutex;
|
||||
mod if_not_else;
|
||||
mod if_then_some_else_none;
|
||||
@ -263,7 +254,6 @@ mod inherent_to_string;
|
||||
mod init_numbered_fields;
|
||||
mod inline_fn_without_body;
|
||||
mod int_plus_one;
|
||||
mod integer_division;
|
||||
mod invalid_upcast_comparisons;
|
||||
mod items_after_statements;
|
||||
mod iter_not_returning_iterator;
|
||||
@ -305,7 +295,6 @@ mod missing_enforced_import_rename;
|
||||
mod missing_inline;
|
||||
mod mixed_read_write_in_expression;
|
||||
mod module_style;
|
||||
mod modulo_arithmetic;
|
||||
mod mut_key;
|
||||
mod mut_mut;
|
||||
mod mut_mutex_lock;
|
||||
@ -313,7 +302,6 @@ mod mut_reference;
|
||||
mod mutable_debug_assertion;
|
||||
mod mutex_atomic;
|
||||
mod needless_arbitrary_self_type;
|
||||
mod needless_bitwise_bool;
|
||||
mod needless_bool;
|
||||
mod needless_borrowed_ref;
|
||||
mod needless_continue;
|
||||
@ -332,10 +320,10 @@ mod non_expressive_names;
|
||||
mod non_octal_unix_permissions;
|
||||
mod non_send_fields_in_send_ty;
|
||||
mod nonstandard_macro_braces;
|
||||
mod numeric_arithmetic;
|
||||
mod octal_escapes;
|
||||
mod only_used_in_recursion;
|
||||
mod open_options;
|
||||
mod operators;
|
||||
mod option_env_unwrap;
|
||||
mod option_if_let_else;
|
||||
mod overflow_check_conditional;
|
||||
@ -347,7 +335,6 @@ mod path_buf_push_overwrite;
|
||||
mod pattern_type_mismatch;
|
||||
mod precedence;
|
||||
mod ptr;
|
||||
mod ptr_eq;
|
||||
mod ptr_offset_with_cast;
|
||||
mod pub_use;
|
||||
mod question_mark;
|
||||
@ -368,7 +355,6 @@ mod repeat_once;
|
||||
mod return_self_not_must_use;
|
||||
mod returns;
|
||||
mod same_name_method;
|
||||
mod self_assignment;
|
||||
mod self_named_constructors;
|
||||
mod semicolon_if_nothing_returned;
|
||||
mod serde_api;
|
||||
@ -580,21 +566,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|| Box::new(booleans::NonminimalBool));
|
||||
store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool));
|
||||
store.register_late_pass(|| Box::new(eq_op::EqOp));
|
||||
store.register_late_pass(|| Box::new(enum_clike::UnportableVariant));
|
||||
store.register_late_pass(|| Box::new(float_literal::FloatLiteral));
|
||||
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
|
||||
store.register_late_pass(move || Box::new(bit_mask::BitMask::new(verbose_bit_mask_threshold)));
|
||||
store.register_late_pass(|| Box::new(ptr::Ptr));
|
||||
store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
|
||||
store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
|
||||
store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
|
||||
store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
|
||||
store.register_late_pass(|| Box::new(misc::MiscLints));
|
||||
store.register_late_pass(|| Box::new(eta_reduction::EtaReduction));
|
||||
store.register_late_pass(|| Box::new(identity_op::IdentityOp));
|
||||
store.register_late_pass(|| Box::new(erasing_op::ErasingOp));
|
||||
store.register_late_pass(|| Box::new(mut_mut::MutMut));
|
||||
store.register_late_pass(|| Box::new(mut_reference::UnnecessaryMutPassed));
|
||||
store.register_late_pass(|| Box::new(len_zero::LenZero));
|
||||
@ -683,7 +662,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
|
||||
store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
|
||||
store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
|
||||
store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
|
||||
store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
|
||||
store.register_late_pass(|| Box::new(regex::Regex));
|
||||
store.register_late_pass(|| Box::new(copies::CopyAndPaste));
|
||||
@ -706,8 +684,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
|
||||
store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
|
||||
store.register_late_pass(|| Box::new(mem_forget::MemForget));
|
||||
store.register_late_pass(|| Box::new(numeric_arithmetic::NumericArithmetic::default()));
|
||||
store.register_late_pass(|| Box::new(assign_ops::AssignOps));
|
||||
store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq));
|
||||
store.register_late_pass(|| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
|
||||
store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
|
||||
@ -734,7 +710,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
|
||||
store.register_late_pass(|| Box::new(implicit_hasher::ImplicitHasher));
|
||||
store.register_late_pass(|| Box::new(fallible_impl_from::FallibleImplFrom));
|
||||
store.register_late_pass(|| Box::new(double_comparison::DoubleComparisons));
|
||||
store.register_late_pass(|| Box::new(question_mark::QuestionMark));
|
||||
store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
|
||||
store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl));
|
||||
@ -742,7 +717,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl));
|
||||
store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
|
||||
store.register_late_pass(|| Box::new(unwrap::Unwrap));
|
||||
store.register_late_pass(|| Box::new(duration_subsec::DurationSubsec));
|
||||
store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing));
|
||||
store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst));
|
||||
store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
|
||||
@ -753,13 +727,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
|
||||
store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
|
||||
store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
|
||||
store.register_late_pass(|| Box::new(integer_division::IntegerDivision));
|
||||
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
|
||||
let max_trait_bounds = conf.max_trait_bounds;
|
||||
store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
|
||||
store.register_late_pass(|| Box::new(comparison_chain::ComparisonChain));
|
||||
store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
|
||||
store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
|
||||
store.register_early_pass(|| Box::new(reference::DerefAddrOf));
|
||||
store.register_early_pass(|| Box::new(double_parens::DoubleParens));
|
||||
store.register_late_pass(|| Box::new(format_impl::FormatImpl::new()));
|
||||
@ -856,9 +828,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
|
||||
store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
|
||||
store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
|
||||
store.register_late_pass(|| Box::new(self_assignment::SelfAssignment));
|
||||
store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
|
||||
store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
|
||||
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
|
||||
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
|
||||
let disallowed_methods = conf.disallowed_methods.clone();
|
||||
@ -941,6 +911,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
|
||||
store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(manual_retain::ManualRetain::new(msrv)));
|
||||
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
|
||||
store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
@ -1,28 +1,21 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
|
||||
StmtKind, TyKind, UnOp,
|
||||
StmtKind, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
use rustc_span::source_map::{ExpnKind, Span};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{
|
||||
get_item_name, get_parent_expr, in_constant, is_integer_const, iter_input_pats, last_path_segment,
|
||||
match_any_def_paths, path_def_id, paths, unsext, SpanlessEq,
|
||||
};
|
||||
use clippy_utils::{get_parent_expr, in_constant, iter_input_pats, last_path_segment, SpanlessEq};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -58,122 +51,6 @@ declare_clippy_lint! {
|
||||
style,
|
||||
"an entire binding declared as `ref`, in a function argument or a `let` statement"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for comparisons to NaN.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// NaN does not compare meaningfully to anything – not
|
||||
/// even itself – so those comparisons are simply wrong.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1.0;
|
||||
/// if x == f32::NAN { }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = 1.0f32;
|
||||
/// if x.is_nan() { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CMP_NAN,
|
||||
correctness,
|
||||
"comparisons to `NAN`, which will always return false, probably not intended"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for (in-)equality comparisons on floating-point
|
||||
/// values (apart from zero), except in functions called `*eq*` (which probably
|
||||
/// implement equality for a type involving floats).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Floating point calculations are usually imprecise, so
|
||||
/// asking if two values are *exactly* equal is asking for trouble. For a good
|
||||
/// guide on what to do, see [the floating point
|
||||
/// guide](http://www.floating-point-gui.de/errors/comparison).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = 1.2331f64;
|
||||
/// let y = 1.2332f64;
|
||||
///
|
||||
/// if y == 1.23f64 { }
|
||||
/// if y != x {} // where both are floats
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = 1.2331f64;
|
||||
/// # let y = 1.2332f64;
|
||||
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
|
||||
/// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
|
||||
/// // let error_margin = std::f64::EPSILON;
|
||||
/// if (y - 1.23f64).abs() < error_margin { }
|
||||
/// if (y - x).abs() > error_margin { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub FLOAT_CMP,
|
||||
pedantic,
|
||||
"using `==` or `!=` on float values instead of comparing difference with an epsilon"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for conversions to owned values just for the sake
|
||||
/// of a comparison.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The comparison can operate on a reference, so creating
|
||||
/// an owned value effectively throws it away directly afterwards, which is
|
||||
/// needlessly consuming code and heap space.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = "foo";
|
||||
/// # let y = String::from("foo");
|
||||
/// if x.to_owned() == y {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = "foo";
|
||||
/// # let y = String::from("foo");
|
||||
/// if x == y {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CMP_OWNED,
|
||||
perf,
|
||||
"creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for getting the remainder of a division by one or minus
|
||||
/// one.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The result for a divisor of one can only ever be zero; for
|
||||
/// minus one it can cause panic/overflow (if the left operand is the minimal value of
|
||||
/// the respective integer type) or results in zero. No one will write such code
|
||||
/// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
|
||||
/// contest, it's probably a bad idea. Use something more underhanded.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// let a = x % 1;
|
||||
/// let a = x % -1;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MODULO_ONE,
|
||||
correctness,
|
||||
"taking a number modulo +/-1, which can either panic/overflow or always returns 0"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the use of bindings with a single leading
|
||||
@ -244,51 +121,11 @@ declare_clippy_lint! {
|
||||
"using `0 as *{const, mut} T`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for (in-)equality comparisons on floating-point
|
||||
/// value and constant, except in functions called `*eq*` (which probably
|
||||
/// implement equality for a type involving floats).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Floating point calculations are usually imprecise, so
|
||||
/// asking if two values are *exactly* equal is asking for trouble. For a good
|
||||
/// guide on what to do, see [the floating point
|
||||
/// guide](http://www.floating-point-gui.de/errors/comparison).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x: f64 = 1.0;
|
||||
/// const ONE: f64 = 1.00;
|
||||
///
|
||||
/// if x == ONE { } // where both are floats
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x: f64 = 1.0;
|
||||
/// # const ONE: f64 = 1.00;
|
||||
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
|
||||
/// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
|
||||
/// // let error_margin = std::f64::EPSILON;
|
||||
/// if (x - ONE).abs() < error_margin { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub FLOAT_CMP_CONST,
|
||||
restriction,
|
||||
"using `==` or `!=` on float constants instead of comparing difference with an epsilon"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MiscLints => [
|
||||
TOPLEVEL_REF_ARG,
|
||||
CMP_NAN,
|
||||
FLOAT_CMP,
|
||||
CMP_OWNED,
|
||||
MODULO_ONE,
|
||||
USED_UNDERSCORE_BINDING,
|
||||
SHORT_CIRCUIT_STATEMENT,
|
||||
ZERO_PTR,
|
||||
FLOAT_CMP_CONST
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
||||
@ -398,16 +235,9 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
match expr.kind {
|
||||
ExprKind::Cast(e, ty) => {
|
||||
check_cast(cx, expr.span, e, ty);
|
||||
return;
|
||||
},
|
||||
ExprKind::Binary(ref cmp, left, right) => {
|
||||
check_binary(cx, expr, cmp, left, right);
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
if let ExprKind::Cast(e, ty) = expr.kind {
|
||||
check_cast(cx, expr.span, e, ty);
|
||||
return;
|
||||
}
|
||||
if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
|
||||
// Don't lint things expanded by #[derive(...)], etc or `await` desugaring
|
||||
@ -455,236 +285,6 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), expr);
|
||||
if match value {
|
||||
Constant::F32(num) => num.is_nan(),
|
||||
Constant::F64(num) => num.is_nan(),
|
||||
_ => false,
|
||||
};
|
||||
then {
|
||||
span_lint(
|
||||
cx,
|
||||
CMP_NAN,
|
||||
cmp_expr.span,
|
||||
"doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
|
||||
res
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
match constant(cx, cx.typeck_results(), 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,
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if `expr` is the result of `signum()` invoked on a float value.
|
||||
fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
// The negation of a signum is still a signum
|
||||
if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
|
||||
return is_signum(cx, child_expr);
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = 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)
|
||||
then {
|
||||
return is_float(cx, self_arg);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let value = &cx.typeck_results().expr_ty(expr).peel_refs().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!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
|
||||
#[derive(Default)]
|
||||
struct EqImpl {
|
||||
ty_eq_other: bool,
|
||||
other_eq_ty: bool,
|
||||
}
|
||||
|
||||
impl EqImpl {
|
||||
fn is_implemented(&self) -> bool {
|
||||
self.ty_eq_other || self.other_eq_ty
|
||||
}
|
||||
}
|
||||
|
||||
fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
|
||||
cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
|
||||
ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
|
||||
other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
|
||||
})
|
||||
}
|
||||
|
||||
let typeck = cx.typeck_results();
|
||||
let (arg, arg_span) = match expr.kind {
|
||||
ExprKind::MethodCall(.., [arg], _)
|
||||
if typeck
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|id| cx.tcx.trait_of_item(id))
|
||||
.map_or(false, |id| {
|
||||
matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
|
||||
}) =>
|
||||
{
|
||||
(arg, arg.span)
|
||||
},
|
||||
ExprKind::Call(path, [arg])
|
||||
if path_def_id(cx, path)
|
||||
.and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
|
||||
.map_or(false, |idx| match idx {
|
||||
0 => true,
|
||||
1 => !is_copy(cx, typeck.expr_ty(expr)),
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
(arg, arg.span)
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let arg_ty = typeck.expr_ty(arg);
|
||||
let other_ty = typeck.expr_ty(other);
|
||||
|
||||
let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
|
||||
let with_deref = arg_ty
|
||||
.builtin_deref(true)
|
||||
.and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
|
||||
.unwrap_or_default();
|
||||
|
||||
if !with_deref.is_implemented() && !without_deref.is_implemented() {
|
||||
return;
|
||||
}
|
||||
|
||||
let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
|
||||
|
||||
let lint_span = if other_gets_derefed {
|
||||
expr.span.to(other.span)
|
||||
} else {
|
||||
expr.span
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CMP_OWNED,
|
||||
lint_span,
|
||||
"this creates an owned instance just for comparison",
|
||||
|diag| {
|
||||
// This also catches `PartialEq` implementations that call `to_owned`.
|
||||
if other_gets_derefed {
|
||||
diag.span_label(lint_span, "try implementing the comparison without allocating");
|
||||
return;
|
||||
}
|
||||
|
||||
let arg_snip = snippet(cx, arg_span, "..");
|
||||
let expr_snip;
|
||||
let eq_impl;
|
||||
if with_deref.is_implemented() {
|
||||
expr_snip = format!("*{}", arg_snip);
|
||||
eq_impl = with_deref;
|
||||
} else {
|
||||
expr_snip = arg_snip.to_string();
|
||||
eq_impl = without_deref;
|
||||
};
|
||||
|
||||
let span;
|
||||
let hint;
|
||||
if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
|
||||
span = expr.span;
|
||||
hint = expr_snip;
|
||||
} else {
|
||||
span = expr.span.to(other.span);
|
||||
|
||||
let cmp_span = if other.span < expr.span {
|
||||
other.span.between(expr.span)
|
||||
} else {
|
||||
expr.span.between(other.span)
|
||||
};
|
||||
if eq_impl.ty_eq_other {
|
||||
hint = format!(
|
||||
"{}{}{}",
|
||||
expr_snip,
|
||||
snippet(cx, cmp_span, ".."),
|
||||
snippet(cx, other.span, "..")
|
||||
);
|
||||
} else {
|
||||
hint = format!(
|
||||
"{}{}{}",
|
||||
snippet(cx, other.span, ".."),
|
||||
snippet(cx, cmp_span, ".."),
|
||||
expr_snip
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"try",
|
||||
hint,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Heuristic to see if an expression is used. Should be compatible with
|
||||
/// `unused_variables`'s idea
|
||||
/// of what it means for an expression to be "used".
|
||||
@ -740,74 +340,3 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_binary<'a>(
|
||||
cx: &LateContext<'a>,
|
||||
expr: &Expr<'_>,
|
||||
cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
|
||||
left: &'a Expr<'_>,
|
||||
right: &'a Expr<'_>,
|
||||
) {
|
||||
let op = cmp.node;
|
||||
if op.is_comparison() {
|
||||
check_nan(cx, left, expr);
|
||||
check_nan(cx, right, expr);
|
||||
check_to_owned(cx, left, right, true);
|
||||
check_to_owned(cx, right, left, false);
|
||||
}
|
||||
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
|
||||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow comparing the results of signum()
|
||||
if is_signum(cx, left) && is_signum(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(name) = get_item_name(cx, expr) {
|
||||
let name = name.as_str();
|
||||
if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
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, "..");
|
||||
|
||||
if !is_comparing_arrays {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"consider comparing them within some margin of error",
|
||||
format!(
|
||||
"({}).abs() {} error_margin",
|
||||
lhs - rhs,
|
||||
if op == BinOpKind::Eq { '<' } else { '>' }
|
||||
),
|
||||
Applicability::HasPlaceholders, // snippet
|
||||
);
|
||||
}
|
||||
diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
|
||||
});
|
||||
} else if op == BinOpKind::Rem {
|
||||
if is_integer_const(cx, right, 1) {
|
||||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
|
||||
if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
|
||||
if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
|
||||
span_lint(
|
||||
cx,
|
||||
MODULO_ONE,
|
||||
expr.span,
|
||||
"any number modulo -1 will panic/overflow or result in 0",
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,85 +0,0 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
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};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
|
||||
/// a lazy and.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The bitwise operators do not support short-circuiting, so it may hinder code performance.
|
||||
/// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint evaluates only when the right side is determined to have no side effects. At this time, that
|
||||
/// determination is quite conservative.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let (x,y) = (true, false);
|
||||
/// if x & !y {} // where both x and y are booleans
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let (x,y) = (true, false);
|
||||
/// if x && !y {}
|
||||
/// ```
|
||||
#[clippy::version = "1.54.0"]
|
||||
pub NEEDLESS_BITWISE_BOOL,
|
||||
pedantic,
|
||||
"Boolean expressions that use bitwise rather than lazy operators"
|
||||
}
|
||||
|
||||
declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]);
|
||||
|
||||
fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion();
|
||||
if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind());
|
||||
if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr;
|
||||
if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind;
|
||||
if !right.can_have_side_effects();
|
||||
then {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn suggestion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
|
||||
if let ExprKind::Binary(ref op, left, right) = expr.kind {
|
||||
if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) {
|
||||
let op_snippet = match op.node {
|
||||
BinOpKind::BitAnd => "&&",
|
||||
_ => "||",
|
||||
};
|
||||
return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for NeedlessBitwiseBool {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if is_bitwise_operation(cx, expr) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_BITWISE_BOOL,
|
||||
expr.span,
|
||||
"use of bitwise operator instead of lazy operator between booleans",
|
||||
|diag| {
|
||||
if let Some(sugg) = suggestion_snippet(cx, expr) {
|
||||
diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
use clippy_utils::consts::constant_simple;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for integer arithmetic operations which could overflow or panic.
|
||||
///
|
||||
/// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
|
||||
/// of overflowing according to the [Rust
|
||||
/// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
|
||||
/// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
|
||||
/// attempted.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Integer overflow will trigger a panic in debug builds or will wrap in
|
||||
/// release mode. Division by zero will cause a panic in either mode. In some applications one
|
||||
/// wants explicitly checked, wrapping or saturating arithmetic.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let a = 0;
|
||||
/// a + 1;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INTEGER_ARITHMETIC,
|
||||
restriction,
|
||||
"any integer arithmetic expression which could overflow or panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for float arithmetic.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// For some embedded systems or kernel development, it
|
||||
/// can be useful to rule out floating-point numbers.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let a = 0.0;
|
||||
/// a + 1.0;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub FLOAT_ARITHMETIC,
|
||||
restriction,
|
||||
"any floating-point arithmetic statement"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct NumericArithmetic {
|
||||
expr_span: Option<Span>,
|
||||
/// This field is used to check whether expressions are constants, such as in enum discriminants
|
||||
/// and consts
|
||||
const_span: Option<Span>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(NumericArithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for NumericArithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if self.expr_span.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(span) = self.const_span {
|
||||
if span.contains(expr.span) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
match &expr.kind {
|
||||
hir::ExprKind::Binary(op, l, r) | hir::ExprKind::AssignOp(op, l, r) => {
|
||||
match op.node {
|
||||
hir::BinOpKind::And
|
||||
| hir::BinOpKind::Or
|
||||
| hir::BinOpKind::BitAnd
|
||||
| hir::BinOpKind::BitOr
|
||||
| hir::BinOpKind::BitXor
|
||||
| hir::BinOpKind::Eq
|
||||
| hir::BinOpKind::Lt
|
||||
| hir::BinOpKind::Le
|
||||
| hir::BinOpKind::Ne
|
||||
| hir::BinOpKind::Ge
|
||||
| hir::BinOpKind::Gt => return,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
|
||||
if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
|
||||
match op.node {
|
||||
hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
|
||||
hir::ExprKind::Lit(_lit) => (),
|
||||
hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
|
||||
if let hir::ExprKind::Lit(lit) = &expr.kind {
|
||||
if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
},
|
||||
}
|
||||
} else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
|
||||
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Unary(hir::UnOp::Neg, arg) => {
|
||||
let ty = cx.typeck_results().expr_ty(arg);
|
||||
if constant_simple(cx, cx.typeck_results(), expr).is_none() {
|
||||
if ty.is_integral() {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
} else if ty.is_floating_point() {
|
||||
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if Some(expr.span) == self.expr_span {
|
||||
self.expr_span = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||
let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
|
||||
|
||||
match cx.tcx.hir().body_owner_kind(body_owner) {
|
||||
hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
|
||||
let body_span = cx.tcx.def_span(body_owner);
|
||||
|
||||
if let Some(span) = self.const_span {
|
||||
if span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.const_span = Some(body_span);
|
||||
},
|
||||
hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||
let body_owner = cx.tcx.hir().body_owner(body.id());
|
||||
let body_span = cx.tcx.hir().span(body_owner);
|
||||
|
||||
if let Some(span) = self.const_span {
|
||||
if span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.const_span = None;
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use clippy_utils::comparisons::{normalize_comparison, Rel};
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
@ -10,73 +9,41 @@ use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_isize_or_usize;
|
||||
use clippy_utils::{clip, int_bits, unsext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for comparisons where one side of the relation is
|
||||
/// either the minimum or maximum value for its type and warns if it involves a
|
||||
/// case that is always true or always false. Only integer and boolean types are
|
||||
/// checked.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// An expression like `min <= x` may misleadingly imply
|
||||
/// that it is possible for `x` to be less than the minimum. Expressions like
|
||||
/// `max < x` are probably mistakes.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// For `usize` the size of the current compile target will
|
||||
/// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
|
||||
/// a comparison to detect target pointer width will trigger this lint. One can
|
||||
/// use `mem::sizeof` and compare its value or conditional compilation
|
||||
/// attributes
|
||||
/// like `#[cfg(target_pointer_width = "64")] ..` instead.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let vec: Vec<isize> = Vec::new();
|
||||
/// if vec.len() <= 0 {}
|
||||
/// if 100 > i32::MAX {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub ABSURD_EXTREME_COMPARISONS,
|
||||
correctness,
|
||||
"a comparison with a maximum or minimum value that is always true or false"
|
||||
}
|
||||
use super::ABSURD_EXTREME_COMPARISONS;
|
||||
|
||||
declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]);
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
lhs: &'tcx Expr<'_>,
|
||||
rhs: &'tcx Expr<'_>,
|
||||
) {
|
||||
if let Some((culprit, result)) = detect_absurd_comparison(cx, op, lhs, rhs) {
|
||||
let msg = "this comparison involving the minimum or maximum element for this \
|
||||
type contains a case that is always true or always false";
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
|
||||
if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
|
||||
if !expr.span.from_expansion() {
|
||||
let msg = "this comparison involving the minimum or maximum element for this \
|
||||
type contains a case that is always true or always false";
|
||||
let conclusion = match result {
|
||||
AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
|
||||
AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
|
||||
AbsurdComparisonResult::InequalityImpossible => format!(
|
||||
"the case where the two sides are not equal never occurs, consider using `{} == {}` \
|
||||
instead",
|
||||
snippet(cx, lhs.span, "lhs"),
|
||||
snippet(cx, rhs.span, "rhs")
|
||||
),
|
||||
};
|
||||
|
||||
let conclusion = match result {
|
||||
AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
|
||||
AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
|
||||
AbsurdComparisonResult::InequalityImpossible => format!(
|
||||
"the case where the two sides are not equal never occurs, consider using `{} == {}` \
|
||||
instead",
|
||||
snippet(cx, lhs.span, "lhs"),
|
||||
snippet(cx, rhs.span, "rhs")
|
||||
),
|
||||
};
|
||||
let help = format!(
|
||||
"because `{}` is the {} value for this type, {}",
|
||||
snippet(cx, culprit.expr.span, "x"),
|
||||
match culprit.which {
|
||||
ExtremeType::Minimum => "minimum",
|
||||
ExtremeType::Maximum => "maximum",
|
||||
},
|
||||
conclusion
|
||||
);
|
||||
|
||||
let help = format!(
|
||||
"because `{}` is the {} value for this type, {}",
|
||||
snippet(cx, culprit.expr.span, "x"),
|
||||
match culprit.which {
|
||||
ExtremeType::Minimum => "minimum",
|
||||
ExtremeType::Maximum => "maximum",
|
||||
},
|
||||
conclusion
|
||||
);
|
||||
|
||||
span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
|
||||
}
|
||||
}
|
||||
}
|
||||
span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
|
||||
}
|
||||
}
|
||||
|
101
clippy_lints/src/operators/assign_op_pattern.rs
Normal file
101
clippy_lints/src/operators/assign_op_pattern.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use clippy_utils::binop_traits;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{eq_expr_value, trait_ref_of_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::ASSIGN_OP_PATTERN;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
assignee: &'tcx hir::Expr<'_>,
|
||||
e: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if let hir::ExprKind::Binary(op, l, r) = &e.kind {
|
||||
let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
|
||||
let ty = cx.typeck_results().expr_ty(assignee);
|
||||
let rty = cx.typeck_results().expr_ty(rhs);
|
||||
if_chain! {
|
||||
if let Some((_, lang_item)) = binop_traits(op.node);
|
||||
if let Ok(trait_id) = cx.tcx.lang_items().require(lang_item);
|
||||
let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
|
||||
if trait_ref_of_method(cx, parent_fn)
|
||||
.map_or(true, |t| t.path.res.def_id() != trait_id);
|
||||
if implements_trait(cx, ty, trait_id, &[rty.into()]);
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
ASSIGN_OP_PATTERN,
|
||||
expr.span,
|
||||
"manual implementation of an assign operation",
|
||||
|diag| {
|
||||
if let (Some(snip_a), Some(snip_r)) =
|
||||
(snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
|
||||
{
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"replace it with",
|
||||
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut visitor = ExprVisitor {
|
||||
assignee,
|
||||
counter: 0,
|
||||
cx,
|
||||
};
|
||||
|
||||
walk_expr(&mut visitor, e);
|
||||
|
||||
if visitor.counter == 1 {
|
||||
// a = a op b
|
||||
if eq_expr_value(cx, assignee, l) {
|
||||
lint(assignee, r);
|
||||
}
|
||||
// a = b commutative_op a
|
||||
// Limited to primitive type as these ops are know to be commutative
|
||||
if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
|
||||
match op.node {
|
||||
hir::BinOpKind::Add
|
||||
| hir::BinOpKind::Mul
|
||||
| hir::BinOpKind::And
|
||||
| hir::BinOpKind::Or
|
||||
| hir::BinOpKind::BitXor
|
||||
| hir::BinOpKind::BitAnd
|
||||
| hir::BinOpKind::BitOr => {
|
||||
lint(assignee, l);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ExprVisitor<'a, 'tcx> {
|
||||
assignee: &'a hir::Expr<'a>,
|
||||
counter: u8,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if eq_expr_value(self.cx, self.assignee, expr) {
|
||||
self.counter += 1;
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
@ -1,161 +1,23 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for incompatible bit masks in comparisons.
|
||||
///
|
||||
/// The formula for detecting if an expression of the type `_ <bit_op> m
|
||||
/// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
|
||||
/// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
|
||||
/// table:
|
||||
///
|
||||
/// |Comparison |Bit Op|Example |is always|Formula |
|
||||
/// |------------|------|-------------|---------|----------------------|
|
||||
/// |`==` or `!=`| `&` |`x & 2 == 3` |`false` |`c & m != c` |
|
||||
/// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
|
||||
/// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
|
||||
/// |`==` or `!=`| `\|` |`x \| 1 == 0`|`false` |`c \| m != c` |
|
||||
/// |`<` or `>=`| `\|` |`x \| 1 < 1` |`false` |`m >= c` |
|
||||
/// |`<=` or `>` | `\|` |`x \| 1 > 0` |`true` |`m > c` |
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If the bits that the comparison cares about are always
|
||||
/// set to zero or one by the bit mask, the comparison is constant `true` or
|
||||
/// `false` (depending on mask, compared value, and operators).
|
||||
///
|
||||
/// So the code is actively misleading, and the only reason someone would write
|
||||
/// this intentionally is to win an underhanded Rust contest or create a
|
||||
/// test-case for this lint.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// if (x & 1 == 2) { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub BAD_BIT_MASK,
|
||||
correctness,
|
||||
"expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
|
||||
}
|
||||
use super::{BAD_BIT_MASK, INEFFECTIVE_BIT_MASK};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bit masks in comparisons which can be removed
|
||||
/// without changing the outcome. The basic structure can be seen in the
|
||||
/// following table:
|
||||
///
|
||||
/// |Comparison| Bit Op |Example |equals |
|
||||
/// |----------|----------|------------|-------|
|
||||
/// |`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`|
|
||||
/// |`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`|
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
|
||||
/// but still a bit misleading, because the bit mask is ineffective.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False negatives: This lint will only match instances
|
||||
/// where we have figured out the math (which is for a power-of-two compared
|
||||
/// value). This means things like `x | 1 >= 7` (which would be better written
|
||||
/// as `x >= 6`) will not be reported (but bit masks like this are fairly
|
||||
/// uncommon).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// if (x | 1 > 3) { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INEFFECTIVE_BIT_MASK,
|
||||
correctness,
|
||||
"expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bit masks that can be replaced by a call
|
||||
/// to `trailing_zeros`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `x.trailing_zeros() > 4` is much clearer than `x & 15
|
||||
/// == 0`
|
||||
///
|
||||
/// ### Known problems
|
||||
/// llvm generates better code for `x & 15 == 0` on x86
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// if x & 0b1111 == 0 { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub VERBOSE_BIT_MASK,
|
||||
pedantic,
|
||||
"expressions where a bit mask is less readable than the corresponding method call"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BitMask {
|
||||
verbose_bit_mask_threshold: u64,
|
||||
}
|
||||
|
||||
impl BitMask {
|
||||
#[must_use]
|
||||
pub fn new(verbose_bit_mask_threshold: u64) -> Self {
|
||||
Self {
|
||||
verbose_bit_mask_threshold,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(BitMask => [BAD_BIT_MASK, INEFFECTIVE_BIT_MASK, VERBOSE_BIT_MASK]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BitMask {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(cmp, left, right) = &e.kind {
|
||||
if cmp.node.is_comparison() {
|
||||
if let Some(cmp_opt) = fetch_int_literal(cx, right) {
|
||||
check_compare(cx, left, cmp.node, cmp_opt, e.span);
|
||||
} else if let Some(cmp_val) = fetch_int_literal(cx, left) {
|
||||
check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(op, left, right) = &e.kind
|
||||
&& BinOpKind::Eq == op.node
|
||||
&& let ExprKind::Binary(op1, left1, right1) = &left.kind
|
||||
&& BinOpKind::BitAnd == op1.node
|
||||
&& let ExprKind::Lit(lit) = &right1.kind
|
||||
&& let LitKind::Int(n, _) = lit.node
|
||||
&& let ExprKind::Lit(lit1) = &right.kind
|
||||
&& let LitKind::Int(0, _) = lit1.node
|
||||
&& n.leading_zeros() == n.count_zeros()
|
||||
&& n > u128::from(self.verbose_bit_mask_threshold)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
VERBOSE_BIT_MASK,
|
||||
e.span,
|
||||
"bit mask could be simplified with a call to `trailing_zeros`",
|
||||
|diag| {
|
||||
let sugg = Sugg::hir(cx, left1, "...").maybe_par();
|
||||
diag.span_suggestion(
|
||||
e.span,
|
||||
"try",
|
||||
format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
if op.is_comparison() {
|
||||
if let Some(cmp_opt) = fetch_int_literal(cx, right) {
|
||||
check_compare(cx, left, op, cmp_opt, e.span);
|
||||
} else if let Some(cmp_val) = fetch_int_literal(cx, left) {
|
||||
check_compare(cx, right, invert_cmp(op), cmp_val, e.span);
|
||||
}
|
||||
}
|
||||
}
|
30
clippy_lints/src/operators/cmp_nan.rs
Normal file
30
clippy_lints/src/operators/cmp_nan.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::in_constant;
|
||||
use rustc_hir::{BinOpKind, Expr};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::CMP_NAN;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
|
||||
if op.is_comparison() && !in_constant(cx, e.hir_id) && (is_nan(cx, lhs) || is_nan(cx, rhs)) {
|
||||
span_lint(
|
||||
cx,
|
||||
CMP_NAN,
|
||||
e.span,
|
||||
"doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), e) {
|
||||
match value {
|
||||
Constant::F32(num) => num.is_nan(),
|
||||
Constant::F64(num) => num.is_nan(),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
147
clippy_lints/src/operators/cmp_owned.rs
Normal file
147
clippy_lints/src/operators/cmp_owned.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use clippy_utils::{match_any_def_paths, path_def_id, paths};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::CMP_OWNED;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
|
||||
if op.is_comparison() {
|
||||
check_op(cx, lhs, rhs, true);
|
||||
check_op(cx, rhs, lhs, false);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct EqImpl {
|
||||
ty_eq_other: bool,
|
||||
other_eq_ty: bool,
|
||||
}
|
||||
impl EqImpl {
|
||||
fn is_implemented(&self) -> bool {
|
||||
self.ty_eq_other || self.other_eq_ty
|
||||
}
|
||||
}
|
||||
|
||||
fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
|
||||
cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
|
||||
ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
|
||||
other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
|
||||
})
|
||||
}
|
||||
|
||||
fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
|
||||
let typeck = cx.typeck_results();
|
||||
let (arg, arg_span) = match expr.kind {
|
||||
ExprKind::MethodCall(.., [arg], _)
|
||||
if typeck
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|id| cx.tcx.trait_of_item(id))
|
||||
.map_or(false, |id| {
|
||||
matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
|
||||
}) =>
|
||||
{
|
||||
(arg, arg.span)
|
||||
},
|
||||
ExprKind::Call(path, [arg])
|
||||
if path_def_id(cx, path)
|
||||
.and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
|
||||
.map_or(false, |idx| match idx {
|
||||
0 => true,
|
||||
1 => !is_copy(cx, typeck.expr_ty(expr)),
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
(arg, arg.span)
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let arg_ty = typeck.expr_ty(arg);
|
||||
let other_ty = typeck.expr_ty(other);
|
||||
|
||||
let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
|
||||
let with_deref = arg_ty
|
||||
.builtin_deref(true)
|
||||
.and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
|
||||
.unwrap_or_default();
|
||||
|
||||
if !with_deref.is_implemented() && !without_deref.is_implemented() {
|
||||
return;
|
||||
}
|
||||
|
||||
let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
|
||||
|
||||
let lint_span = if other_gets_derefed {
|
||||
expr.span.to(other.span)
|
||||
} else {
|
||||
expr.span
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CMP_OWNED,
|
||||
lint_span,
|
||||
"this creates an owned instance just for comparison",
|
||||
|diag| {
|
||||
// This also catches `PartialEq` implementations that call `to_owned`.
|
||||
if other_gets_derefed {
|
||||
diag.span_label(lint_span, "try implementing the comparison without allocating");
|
||||
return;
|
||||
}
|
||||
|
||||
let arg_snip = snippet(cx, arg_span, "..");
|
||||
let expr_snip;
|
||||
let eq_impl;
|
||||
if with_deref.is_implemented() {
|
||||
expr_snip = format!("*{}", arg_snip);
|
||||
eq_impl = with_deref;
|
||||
} else {
|
||||
expr_snip = arg_snip.to_string();
|
||||
eq_impl = without_deref;
|
||||
};
|
||||
|
||||
let span;
|
||||
let hint;
|
||||
if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
|
||||
span = expr.span;
|
||||
hint = expr_snip;
|
||||
} else {
|
||||
span = expr.span.to(other.span);
|
||||
|
||||
let cmp_span = if other.span < expr.span {
|
||||
other.span.between(expr.span)
|
||||
} else {
|
||||
expr.span.between(other.span)
|
||||
};
|
||||
if eq_impl.ty_eq_other {
|
||||
hint = format!(
|
||||
"{}{}{}",
|
||||
expr_snip,
|
||||
snippet(cx, cmp_span, ".."),
|
||||
snippet(cx, other.span, "..")
|
||||
);
|
||||
} else {
|
||||
hint = format!(
|
||||
"{}{}{}",
|
||||
snippet(cx, other.span, ".."),
|
||||
snippet(cx, cmp_span, ".."),
|
||||
expr_snip
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"try",
|
||||
hint,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
54
clippy_lints/src/operators/double_comparison.rs
Normal file
54
clippy_lints/src/operators/double_comparison.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eq_expr_value;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
use super::DOUBLE_COMPARISONS;
|
||||
|
||||
#[expect(clippy::similar_names)]
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
|
||||
let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
|
||||
(ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
|
||||
(lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) {
|
||||
return;
|
||||
}
|
||||
macro_rules! lint_double_comparison {
|
||||
($op:tt) => {{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability);
|
||||
let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability);
|
||||
let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DOUBLE_COMPARISONS,
|
||||
span,
|
||||
"this binary expression can be simplified",
|
||||
"try",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}};
|
||||
}
|
||||
match (op, lkind, rkind) {
|
||||
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
|
||||
lint_double_comparison!(<=);
|
||||
},
|
||||
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => {
|
||||
lint_double_comparison!(>=);
|
||||
},
|
||||
(BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => {
|
||||
lint_double_comparison!(!=);
|
||||
},
|
||||
(BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
|
||||
lint_double_comparison!(==);
|
||||
},
|
||||
_ => (),
|
||||
};
|
||||
}
|
44
clippy_lints/src/operators/duration_subsec.rs
Normal file
44
clippy_lints/src/operators/duration_subsec.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::DURATION_SUBSEC;
|
||||
|
||||
pub(crate) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
if op == BinOpKind::Div
|
||||
&& let ExprKind::MethodCall(method_path, [self_arg], _) = left.kind
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration)
|
||||
&& let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right)
|
||||
{
|
||||
let suggested_fn = match (method_path.ident.as_str(), divisor) {
|
||||
("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
|
||||
("subsec_nanos", 1_000) => "subsec_micros",
|
||||
_ => return,
|
||||
};
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DURATION_SUBSEC,
|
||||
expr.span,
|
||||
&format!("calling `{}()` is more concise than this calculation", suggested_fn),
|
||||
"try",
|
||||
format!(
|
||||
"{}.{}()",
|
||||
snippet_with_applicability(cx, self_arg.span, "_", &mut applicability),
|
||||
suggested_fn
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
45
clippy_lints/src/operators/eq_op.rs
Normal file
45
clippy_lints/src/operators/eq_op.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
|
||||
use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
|
||||
use rustc_hir::{BinOpKind, Expr};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::EQ_OP;
|
||||
|
||||
pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let Some((macro_call, macro_name))
|
||||
= first_node_macro_backtrace(cx, e).find_map(|macro_call| {
|
||||
let name = cx.tcx.item_name(macro_call.def_id);
|
||||
matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
|
||||
.then(|| (macro_call, name))
|
||||
})
|
||||
&& let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
|
||||
&& eq_expr_value(cx, lhs, rhs)
|
||||
&& macro_call.is_local()
|
||||
&& !is_in_test_function(cx.tcx, e.hir_id)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
EQ_OP,
|
||||
lhs.span.to(rhs.span),
|
||||
&format!("identical args used in this `{}!` macro call", macro_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
if is_useless_with_eq_exprs(op.into()) && eq_expr_value(cx, left, right) && !is_in_test_function(cx.tcx, e.hir_id) {
|
||||
span_lint(
|
||||
cx,
|
||||
EQ_OP,
|
||||
e.span,
|
||||
&format!("equal expressions as operands to `{}`", op.as_str()),
|
||||
);
|
||||
}
|
||||
}
|
53
clippy_lints/src/operators/erasing_op.rs
Normal file
53
clippy_lints/src/operators/erasing_op.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use clippy_utils::consts::{constant_simple, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::same_type_and_consts;
|
||||
|
||||
use rustc_hir::{BinOpKind, Expr};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::TypeckResults;
|
||||
|
||||
use super::ERASING_OP;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
let tck = cx.typeck_results();
|
||||
match op {
|
||||
BinOpKind::Mul | BinOpKind::BitAnd => {
|
||||
check_op(cx, tck, left, right, e);
|
||||
check_op(cx, tck, right, left, e);
|
||||
},
|
||||
BinOpKind::Div => check_op(cx, tck, left, right, e),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
|
||||
let input_ty = tck.expr_ty(input).peel_refs();
|
||||
let output_ty = tck.expr_ty(output).peel_refs();
|
||||
!same_type_and_consts(input_ty, output_ty)
|
||||
}
|
||||
|
||||
fn check_op<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
tck: &TypeckResults<'tcx>,
|
||||
op: &Expr<'tcx>,
|
||||
other: &Expr<'tcx>,
|
||||
parent: &Expr<'tcx>,
|
||||
) {
|
||||
if constant_simple(cx, tck, op) == Some(Constant::Int(0)) {
|
||||
if different_types(tck, other, parent) {
|
||||
return;
|
||||
}
|
||||
span_lint(
|
||||
cx,
|
||||
ERASING_OP,
|
||||
parent.span,
|
||||
"this operation will always return zero. This is likely not the intended outcome",
|
||||
);
|
||||
}
|
||||
}
|
139
clippy_lints/src/operators/float_cmp.rs
Normal file
139
clippy_lints/src/operators/float_cmp.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::get_item_name;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
||||
use super::{FLOAT_CMP, FLOAT_CMP_CONST};
|
||||
|
||||
pub(crate) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
|
||||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow comparing the results of signum()
|
||||
if is_signum(cx, left) && is_signum(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(name) = get_item_name(cx, expr) {
|
||||
let name = name.as_str();
|
||||
if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
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, "..");
|
||||
|
||||
if !is_comparing_arrays {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"consider comparing them within some margin of error",
|
||||
format!(
|
||||
"({}).abs() {} error_margin",
|
||||
lhs - rhs,
|
||||
if op == BinOpKind::Eq { '<' } else { '>' }
|
||||
),
|
||||
Applicability::HasPlaceholders, // snippet
|
||||
);
|
||||
}
|
||||
diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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 is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
|
||||
res
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
match constant(cx, cx.typeck_results(), 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,
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if `expr` is the result of `signum()` invoked on a float value.
|
||||
fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
// The negation of a signum is still a signum
|
||||
if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
|
||||
return is_signum(cx, child_expr);
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = 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)
|
||||
then {
|
||||
return is_float(cx, self_arg);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let value = &cx.typeck_results().expr_ty(expr).peel_refs().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!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
|
||||
}
|
71
clippy_lints/src/operators/float_equality_without_abs.rs
Normal file
71
clippy_lints/src/operators/float_equality_without_abs.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{match_def_path, paths, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::util::parser::AssocOp;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::FLOAT_EQUALITY_WITHOUT_ABS;
|
||||
|
||||
pub(crate) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
lhs: &'tcx Expr<'_>,
|
||||
rhs: &'tcx Expr<'_>,
|
||||
) {
|
||||
let (lhs, rhs) = match op {
|
||||
BinOpKind::Lt => (lhs, rhs),
|
||||
BinOpKind::Gt => (rhs, lhs),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if_chain! {
|
||||
// left hand side is a subtraction
|
||||
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 let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
|
||||
if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
|
||||
|
||||
// values of the subtractions 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 {
|
||||
let sug_l = sugg::Sugg::hir(cx, val_l, "..");
|
||||
let sug_r = sugg::Sugg::hir(cx, val_r, "..");
|
||||
// format the suggestion
|
||||
let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par());
|
||||
// spans the lint
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
FLOAT_EQUALITY_WITHOUT_ABS,
|
||||
expr.span,
|
||||
"float equality check without `.abs()`",
|
||||
| diag | {
|
||||
diag.span_suggestion(
|
||||
lhs.span,
|
||||
"add `.abs()`",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,61 +3,40 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{clip, unsext};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for identity operations, e.g., `x + 0`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This code can be removed without changing the
|
||||
/// meaning. So it just obscures what's going on. Delete it mercilessly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// x / 1 + 0 * 1 - 0 | 0;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub IDENTITY_OP,
|
||||
complexity,
|
||||
"using identity operations, e.g., `x + 0` or `y / 1`"
|
||||
}
|
||||
use super::IDENTITY_OP;
|
||||
|
||||
declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IdentityOp {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::Binary(cmp, left, right) = &expr.kind {
|
||||
if !is_allowed(cx, *cmp, left, right) {
|
||||
match cmp.node {
|
||||
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
|
||||
check(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right));
|
||||
check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
|
||||
check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Mul => {
|
||||
check(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right));
|
||||
check(cx, right, 1, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Div => check(cx, right, 1, expr.span, left.span, Parens::Unneeded),
|
||||
BinOpKind::BitAnd => {
|
||||
check(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right));
|
||||
check(cx, right, -1, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
pub(crate) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
if !is_allowed(cx, op, left, right) {
|
||||
match op {
|
||||
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
|
||||
check_op(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right));
|
||||
check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
|
||||
check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Mul => {
|
||||
check_op(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right));
|
||||
check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Div => check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded),
|
||||
BinOpKind::BitAnd => {
|
||||
check_op(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right));
|
||||
check_op(cx, right, -1, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,12 +87,12 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>)
|
||||
Parens::Needed
|
||||
}
|
||||
|
||||
fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
// This lint applies to integers
|
||||
!cx.typeck_results().expr_ty(left).peel_refs().is_integral()
|
||||
|| !cx.typeck_results().expr_ty(right).peel_refs().is_integral()
|
||||
// `1 << 0` is a common pattern in bit manipulation code
|
||||
|| (cmp.node == BinOpKind::Shl
|
||||
|| (cmp == BinOpKind::Shl
|
||||
&& constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
|
||||
&& constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
|
||||
}
|
||||
@ -130,7 +109,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
|
||||
}
|
||||
}
|
||||
|
||||
fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
|
||||
fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
|
||||
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
|
||||
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
|
||||
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
|
27
clippy_lints/src/operators/integer_division.rs
Normal file
27
clippy_lints/src/operators/integer_division.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::INTEGER_DIVISION;
|
||||
|
||||
pub(crate) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
op: hir::BinOpKind,
|
||||
left: &'tcx hir::Expr<'_>,
|
||||
right: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if op == hir::BinOpKind::Div
|
||||
&& cx.typeck_results().expr_ty(left).is_integral()
|
||||
&& cx.typeck_results().expr_ty(right).is_integral()
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
INTEGER_DIVISION,
|
||||
expr.span,
|
||||
"integer division",
|
||||
None,
|
||||
"division of integers may cause loss of precision. consider using floats",
|
||||
);
|
||||
}
|
||||
}
|
84
clippy_lints/src/operators/misrefactored_assign_op.rs
Normal file
84
clippy_lints/src/operators/misrefactored_assign_op.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::eq_expr_value;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::MISREFACTORED_ASSIGN_OP;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
op: hir::BinOpKind,
|
||||
lhs: &'tcx hir::Expr<'_>,
|
||||
rhs: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
|
||||
if op != binop.node {
|
||||
return;
|
||||
}
|
||||
// lhs op= l op r
|
||||
if eq_expr_value(cx, lhs, l) {
|
||||
lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, r);
|
||||
}
|
||||
// lhs op= l commutative_op r
|
||||
if is_commutative(op) && eq_expr_value(cx, lhs, r) {
|
||||
lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_misrefactored_assign_op(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
op: hir::BinOpKind,
|
||||
rhs: &hir::Expr<'_>,
|
||||
assignee: &hir::Expr<'_>,
|
||||
rhs_other: &hir::Expr<'_>,
|
||||
) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MISREFACTORED_ASSIGN_OP,
|
||||
expr.span,
|
||||
"variable appears on both sides of an assignment operation",
|
||||
|diag| {
|
||||
if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
|
||||
let a = &sugg::Sugg::hir(cx, assignee, "..");
|
||||
let r = &sugg::Sugg::hir(cx, rhs, "..");
|
||||
let long = format!("{} = {}", snip_a, sugg::make_binop(op.into(), a, r));
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
&format!(
|
||||
"did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
|
||||
snip_a,
|
||||
snip_a,
|
||||
op.as_str(),
|
||||
snip_r,
|
||||
long
|
||||
),
|
||||
format!("{} {}= {}", snip_a, op.as_str(), snip_r),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"or",
|
||||
long,
|
||||
Applicability::MaybeIncorrect, // snippet
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn is_commutative(op: hir::BinOpKind) -> bool {
|
||||
use rustc_hir::BinOpKind::{
|
||||
Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
|
||||
};
|
||||
match op {
|
||||
Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
|
||||
Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
|
||||
}
|
||||
}
|
849
clippy_lints/src/operators/mod.rs
Normal file
849
clippy_lints/src/operators/mod.rs
Normal file
@ -0,0 +1,849 @@
|
||||
use rustc_hir::{Body, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
mod absurd_extreme_comparisons;
|
||||
mod assign_op_pattern;
|
||||
mod bit_mask;
|
||||
mod cmp_nan;
|
||||
mod cmp_owned;
|
||||
mod double_comparison;
|
||||
mod duration_subsec;
|
||||
mod eq_op;
|
||||
mod erasing_op;
|
||||
mod float_cmp;
|
||||
mod float_equality_without_abs;
|
||||
mod identity_op;
|
||||
mod integer_division;
|
||||
mod misrefactored_assign_op;
|
||||
mod modulo_arithmetic;
|
||||
mod modulo_one;
|
||||
mod needless_bitwise_bool;
|
||||
mod numeric_arithmetic;
|
||||
mod op_ref;
|
||||
mod ptr_eq;
|
||||
mod self_assignment;
|
||||
mod verbose_bit_mask;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for comparisons where one side of the relation is
|
||||
/// either the minimum or maximum value for its type and warns if it involves a
|
||||
/// case that is always true or always false. Only integer and boolean types are
|
||||
/// checked.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// An expression like `min <= x` may misleadingly imply
|
||||
/// that it is possible for `x` to be less than the minimum. Expressions like
|
||||
/// `max < x` are probably mistakes.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// For `usize` the size of the current compile target will
|
||||
/// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
|
||||
/// a comparison to detect target pointer width will trigger this lint. One can
|
||||
/// use `mem::sizeof` and compare its value or conditional compilation
|
||||
/// attributes
|
||||
/// like `#[cfg(target_pointer_width = "64")] ..` instead.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let vec: Vec<isize> = Vec::new();
|
||||
/// if vec.len() <= 0 {}
|
||||
/// if 100 > i32::MAX {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub ABSURD_EXTREME_COMPARISONS,
|
||||
correctness,
|
||||
"a comparison with a maximum or minimum value that is always true or false"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for integer arithmetic operations which could overflow or panic.
|
||||
///
|
||||
/// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
|
||||
/// of overflowing according to the [Rust
|
||||
/// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
|
||||
/// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
|
||||
/// attempted.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Integer overflow will trigger a panic in debug builds or will wrap in
|
||||
/// release mode. Division by zero will cause a panic in either mode. In some applications one
|
||||
/// wants explicitly checked, wrapping or saturating arithmetic.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let a = 0;
|
||||
/// a + 1;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INTEGER_ARITHMETIC,
|
||||
restriction,
|
||||
"any integer arithmetic expression which could overflow or panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for float arithmetic.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// For some embedded systems or kernel development, it
|
||||
/// can be useful to rule out floating-point numbers.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let a = 0.0;
|
||||
/// a + 1.0;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub FLOAT_ARITHMETIC,
|
||||
restriction,
|
||||
"any floating-point arithmetic statement"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `a = a op b` or `a = b commutative_op a`
|
||||
/// patterns.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// These can be written as the shorter `a op= b`.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// While forbidden by the spec, `OpAssign` traits may have
|
||||
/// implementations that differ from the regular `Op` impl.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// let b = 0;
|
||||
/// // ...
|
||||
///
|
||||
/// a = a + b;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// let b = 0;
|
||||
/// // ...
|
||||
///
|
||||
/// a += b;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub ASSIGN_OP_PATTERN,
|
||||
style,
|
||||
"assigning the result of an operation on a variable to that same variable"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `a op= a op b` or `a op= b op a` patterns.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Most likely these are bugs where one meant to write `a
|
||||
/// op= b`.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Clippy cannot know for sure if `a op= a op b` should have
|
||||
/// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
|
||||
/// If `a op= a op b` is really the correct behavior it should be
|
||||
/// written as `a = a op a op b` as it's less confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// let b = 2;
|
||||
/// // ...
|
||||
/// a += a + b;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MISREFACTORED_ASSIGN_OP,
|
||||
suspicious,
|
||||
"having a variable on both sides of an assign op"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for incompatible bit masks in comparisons.
|
||||
///
|
||||
/// The formula for detecting if an expression of the type `_ <bit_op> m
|
||||
/// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
|
||||
/// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
|
||||
/// table:
|
||||
///
|
||||
/// |Comparison |Bit Op|Example |is always|Formula |
|
||||
/// |------------|------|-------------|---------|----------------------|
|
||||
/// |`==` or `!=`| `&` |`x & 2 == 3` |`false` |`c & m != c` |
|
||||
/// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
|
||||
/// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
|
||||
/// |`==` or `!=`| `\|` |`x \| 1 == 0`|`false` |`c \| m != c` |
|
||||
/// |`<` or `>=`| `\|` |`x \| 1 < 1` |`false` |`m >= c` |
|
||||
/// |`<=` or `>` | `\|` |`x \| 1 > 0` |`true` |`m > c` |
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If the bits that the comparison cares about are always
|
||||
/// set to zero or one by the bit mask, the comparison is constant `true` or
|
||||
/// `false` (depending on mask, compared value, and operators).
|
||||
///
|
||||
/// So the code is actively misleading, and the only reason someone would write
|
||||
/// this intentionally is to win an underhanded Rust contest or create a
|
||||
/// test-case for this lint.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// if (x & 1 == 2) { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub BAD_BIT_MASK,
|
||||
correctness,
|
||||
"expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bit masks in comparisons which can be removed
|
||||
/// without changing the outcome. The basic structure can be seen in the
|
||||
/// following table:
|
||||
///
|
||||
/// |Comparison| Bit Op |Example |equals |
|
||||
/// |----------|----------|------------|-------|
|
||||
/// |`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`|
|
||||
/// |`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`|
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
|
||||
/// but still a bit misleading, because the bit mask is ineffective.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False negatives: This lint will only match instances
|
||||
/// where we have figured out the math (which is for a power-of-two compared
|
||||
/// value). This means things like `x | 1 >= 7` (which would be better written
|
||||
/// as `x >= 6`) will not be reported (but bit masks like this are fairly
|
||||
/// uncommon).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// if (x | 1 > 3) { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INEFFECTIVE_BIT_MASK,
|
||||
correctness,
|
||||
"expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bit masks that can be replaced by a call
|
||||
/// to `trailing_zeros`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `x.trailing_zeros() > 4` is much clearer than `x & 15
|
||||
/// == 0`
|
||||
///
|
||||
/// ### Known problems
|
||||
/// llvm generates better code for `x & 15 == 0` on x86
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// if x & 0b1111 == 0 { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub VERBOSE_BIT_MASK,
|
||||
pedantic,
|
||||
"expressions where a bit mask is less readable than the corresponding method call"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for double comparisons that could be simplified to a single expression.
|
||||
///
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// # let y = 2;
|
||||
/// if x == y || x < y {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// # let y = 2;
|
||||
/// if x <= y {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DOUBLE_COMPARISONS,
|
||||
complexity,
|
||||
"unnecessary double comparisons that can be simplified"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calculation of subsecond microseconds or milliseconds
|
||||
/// from other `Duration` methods.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's more concise to call `Duration::subsec_micros()` or
|
||||
/// `Duration::subsec_millis()` than to calculate them.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::time::Duration;
|
||||
/// # let duration = Duration::new(5, 0);
|
||||
/// let micros = duration.subsec_nanos() / 1_000;
|
||||
/// let millis = duration.subsec_nanos() / 1_000_000;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::time::Duration;
|
||||
/// # let duration = Duration::new(5, 0);
|
||||
/// let micros = duration.subsec_micros();
|
||||
/// let millis = duration.subsec_millis();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DURATION_SUBSEC,
|
||||
complexity,
|
||||
"checks for calculation of subsecond microseconds or milliseconds"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for equal operands to comparison, logical and
|
||||
/// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
|
||||
/// `||`, `&`, `|`, `^`, `-` and `/`).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is usually just a typo or a copy and paste error.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False negatives: We had some false positives regarding
|
||||
/// calls (notably [racer](https://github.com/phildawes/racer) had one instance
|
||||
/// of `x.pop() && x.pop()`), so we removed matching any function or method
|
||||
/// calls. We may introduce a list of known pure functions in the future.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// if x + 1 == x + 1 {}
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// # let a = 3;
|
||||
/// # let b = 4;
|
||||
/// assert_eq!(a, a);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EQ_OP,
|
||||
correctness,
|
||||
"equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for arguments to `==` which have their address
|
||||
/// taken to satisfy a bound
|
||||
/// and suggests to dereference the other argument instead
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is more idiomatic to dereference the other argument.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// &x == y
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// x == *y
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub OP_REF,
|
||||
style,
|
||||
"taking a reference to satisfy the type constraints on `==`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for erasing operations, e.g., `x * 0`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The whole expression can be replaced by zero.
|
||||
/// This is most likely not the intended outcome and should probably be
|
||||
/// corrected
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = 1;
|
||||
/// 0 / x;
|
||||
/// 0 * x;
|
||||
/// x & 0;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub ERASING_OP,
|
||||
correctness,
|
||||
"using erasing operations, e.g., `x * 0` or `y & 0`"
|
||||
}
|
||||
|
||||
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 unnecessary. 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
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub FLOAT_EQUALITY_WITHOUT_ABS,
|
||||
suspicious,
|
||||
"float equality check without `.abs()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for identity operations, e.g., `x + 0`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This code can be removed without changing the
|
||||
/// meaning. So it just obscures what's going on. Delete it mercilessly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// x / 1 + 0 * 1 - 0 | 0;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub IDENTITY_OP,
|
||||
complexity,
|
||||
"using identity operations, e.g., `x + 0` or `y / 1`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for division of integers
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// When outside of some very specific algorithms,
|
||||
/// integer division is very often a mistake because it discards the
|
||||
/// remainder.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = 3 / 2;
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = 3f32 / 2f32;
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
#[clippy::version = "1.37.0"]
|
||||
pub INTEGER_DIVISION,
|
||||
restriction,
|
||||
"integer division may cause loss of precision"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for comparisons to NaN.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// NaN does not compare meaningfully to anything – not
|
||||
/// even itself – so those comparisons are simply wrong.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1.0;
|
||||
/// if x == f32::NAN { }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = 1.0f32;
|
||||
/// if x.is_nan() { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CMP_NAN,
|
||||
correctness,
|
||||
"comparisons to `NAN`, which will always return false, probably not intended"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for conversions to owned values just for the sake
|
||||
/// of a comparison.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The comparison can operate on a reference, so creating
|
||||
/// an owned value effectively throws it away directly afterwards, which is
|
||||
/// needlessly consuming code and heap space.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = "foo";
|
||||
/// # let y = String::from("foo");
|
||||
/// if x.to_owned() == y {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = "foo";
|
||||
/// # let y = String::from("foo");
|
||||
/// if x == y {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CMP_OWNED,
|
||||
perf,
|
||||
"creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for (in-)equality comparisons on floating-point
|
||||
/// values (apart from zero), except in functions called `*eq*` (which probably
|
||||
/// implement equality for a type involving floats).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Floating point calculations are usually imprecise, so
|
||||
/// asking if two values are *exactly* equal is asking for trouble. For a good
|
||||
/// guide on what to do, see [the floating point
|
||||
/// guide](http://www.floating-point-gui.de/errors/comparison).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = 1.2331f64;
|
||||
/// let y = 1.2332f64;
|
||||
///
|
||||
/// if y == 1.23f64 { }
|
||||
/// if y != x {} // where both are floats
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = 1.2331f64;
|
||||
/// # let y = 1.2332f64;
|
||||
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
|
||||
/// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
|
||||
/// // let error_margin = std::f64::EPSILON;
|
||||
/// if (y - 1.23f64).abs() < error_margin { }
|
||||
/// if (y - x).abs() > error_margin { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub FLOAT_CMP,
|
||||
pedantic,
|
||||
"using `==` or `!=` on float values instead of comparing difference with an epsilon"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for (in-)equality comparisons on floating-point
|
||||
/// value and constant, except in functions called `*eq*` (which probably
|
||||
/// implement equality for a type involving floats).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Floating point calculations are usually imprecise, so
|
||||
/// asking if two values are *exactly* equal is asking for trouble. For a good
|
||||
/// guide on what to do, see [the floating point
|
||||
/// guide](http://www.floating-point-gui.de/errors/comparison).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x: f64 = 1.0;
|
||||
/// const ONE: f64 = 1.00;
|
||||
///
|
||||
/// if x == ONE { } // where both are floats
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x: f64 = 1.0;
|
||||
/// # const ONE: f64 = 1.00;
|
||||
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
|
||||
/// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
|
||||
/// // let error_margin = std::f64::EPSILON;
|
||||
/// if (x - ONE).abs() < error_margin { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub FLOAT_CMP_CONST,
|
||||
restriction,
|
||||
"using `==` or `!=` on float constants instead of comparing difference with an epsilon"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for getting the remainder of a division by one or minus
|
||||
/// one.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The result for a divisor of one can only ever be zero; for
|
||||
/// minus one it can cause panic/overflow (if the left operand is the minimal value of
|
||||
/// the respective integer type) or results in zero. No one will write such code
|
||||
/// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
|
||||
/// contest, it's probably a bad idea. Use something more underhanded.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// let a = x % 1;
|
||||
/// let a = x % -1;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MODULO_ONE,
|
||||
correctness,
|
||||
"taking a number modulo +/-1, which can either panic/overflow or always returns 0"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for modulo arithmetic.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The results of modulo (%) operation might differ
|
||||
/// depending on the language, when negative numbers are involved.
|
||||
/// If you interop with different languages it might be beneficial
|
||||
/// to double check all places that use modulo arithmetic.
|
||||
///
|
||||
/// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = -17 % 3;
|
||||
/// ```
|
||||
#[clippy::version = "1.42.0"]
|
||||
pub MODULO_ARITHMETIC,
|
||||
restriction,
|
||||
"any modulo arithmetic statement"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
|
||||
/// a lazy and.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The bitwise operators do not support short-circuiting, so it may hinder code performance.
|
||||
/// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint evaluates only when the right side is determined to have no side effects. At this time, that
|
||||
/// determination is quite conservative.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let (x,y) = (true, false);
|
||||
/// if x & !y {} // where both x and y are booleans
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let (x,y) = (true, false);
|
||||
/// if x && !y {}
|
||||
/// ```
|
||||
#[clippy::version = "1.54.0"]
|
||||
pub NEEDLESS_BITWISE_BOOL,
|
||||
pedantic,
|
||||
"Boolean expressions that use bitwise rather than lazy operators"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Use `std::ptr::eq` when applicable
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `ptr::eq` can be used to compare `&T` references
|
||||
/// (which coerce to `*const T` implicitly) by their address rather than
|
||||
/// comparing the values they point to.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let a = &[1, 2, 3];
|
||||
/// let b = &[1, 2, 3];
|
||||
///
|
||||
/// assert!(a as *const _ as usize == b as *const _ as usize);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let a = &[1, 2, 3];
|
||||
/// let b = &[1, 2, 3];
|
||||
///
|
||||
/// assert!(std::ptr::eq(a, b));
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub PTR_EQ,
|
||||
style,
|
||||
"use `std::ptr::eq` when comparing raw pointers"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for explicit self-assignments.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Self-assignments are redundant and unlikely to be
|
||||
/// intentional.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If expression contains any deref coercions or
|
||||
/// indexing operations they are assumed not to have any side effects.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct Event {
|
||||
/// x: i32,
|
||||
/// }
|
||||
///
|
||||
/// fn copy_position(a: &mut Event, b: &Event) {
|
||||
/// a.x = a.x;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Should be:
|
||||
/// ```rust
|
||||
/// struct Event {
|
||||
/// x: i32,
|
||||
/// }
|
||||
///
|
||||
/// fn copy_position(a: &mut Event, b: &Event) {
|
||||
/// a.x = b.x;
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub SELF_ASSIGNMENT,
|
||||
correctness,
|
||||
"explicit self-assignment"
|
||||
}
|
||||
|
||||
pub struct Operators {
|
||||
arithmetic_context: numeric_arithmetic::Context,
|
||||
verbose_bit_mask_threshold: u64,
|
||||
}
|
||||
impl_lint_pass!(Operators => [
|
||||
ABSURD_EXTREME_COMPARISONS,
|
||||
INTEGER_ARITHMETIC,
|
||||
FLOAT_ARITHMETIC,
|
||||
ASSIGN_OP_PATTERN,
|
||||
MISREFACTORED_ASSIGN_OP,
|
||||
BAD_BIT_MASK,
|
||||
INEFFECTIVE_BIT_MASK,
|
||||
VERBOSE_BIT_MASK,
|
||||
DOUBLE_COMPARISONS,
|
||||
DURATION_SUBSEC,
|
||||
EQ_OP,
|
||||
OP_REF,
|
||||
ERASING_OP,
|
||||
FLOAT_EQUALITY_WITHOUT_ABS,
|
||||
IDENTITY_OP,
|
||||
INTEGER_DIVISION,
|
||||
CMP_NAN,
|
||||
CMP_OWNED,
|
||||
FLOAT_CMP,
|
||||
FLOAT_CMP_CONST,
|
||||
MODULO_ONE,
|
||||
MODULO_ARITHMETIC,
|
||||
NEEDLESS_BITWISE_BOOL,
|
||||
PTR_EQ,
|
||||
SELF_ASSIGNMENT,
|
||||
]);
|
||||
impl Operators {
|
||||
pub fn new(verbose_bit_mask_threshold: u64) -> Self {
|
||||
Self {
|
||||
arithmetic_context: numeric_arithmetic::Context::default(),
|
||||
verbose_bit_mask_threshold,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'tcx> LateLintPass<'tcx> for Operators {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
eq_op::check_assert(cx, e);
|
||||
match e.kind {
|
||||
ExprKind::Binary(op, lhs, rhs) => {
|
||||
if !e.span.from_expansion() {
|
||||
absurd_extreme_comparisons::check(cx, e, op.node, lhs, rhs);
|
||||
if !(macro_with_not_op(lhs) || macro_with_not_op(rhs)) {
|
||||
eq_op::check(cx, e, op.node, lhs, rhs);
|
||||
op_ref::check(cx, e, op.node, lhs, rhs);
|
||||
}
|
||||
erasing_op::check(cx, e, op.node, lhs, rhs);
|
||||
identity_op::check(cx, e, op.node, lhs, rhs);
|
||||
needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
|
||||
ptr_eq::check(cx, e, op.node, lhs, rhs);
|
||||
}
|
||||
self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
|
||||
bit_mask::check(cx, e, op.node, lhs, rhs);
|
||||
verbose_bit_mask::check(cx, e, op.node, lhs, rhs, self.verbose_bit_mask_threshold);
|
||||
double_comparison::check(cx, op.node, lhs, rhs, e.span);
|
||||
duration_subsec::check(cx, e, op.node, lhs, rhs);
|
||||
float_equality_without_abs::check(cx, e, op.node, lhs, rhs);
|
||||
integer_division::check(cx, e, op.node, lhs, rhs);
|
||||
cmp_nan::check(cx, e, op.node, lhs, rhs);
|
||||
cmp_owned::check(cx, op.node, lhs, rhs);
|
||||
float_cmp::check(cx, e, op.node, lhs, rhs);
|
||||
modulo_one::check(cx, e, op.node, rhs);
|
||||
modulo_arithmetic::check(cx, e, op.node, lhs, rhs);
|
||||
},
|
||||
ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
|
||||
misrefactored_assign_op::check(cx, e, op.node, lhs, rhs);
|
||||
modulo_arithmetic::check(cx, e, op.node, lhs, rhs);
|
||||
},
|
||||
ExprKind::Assign(lhs, rhs, _) => {
|
||||
assign_op_pattern::check(cx, e, lhs, rhs);
|
||||
self_assignment::check(cx, e, lhs, rhs);
|
||||
},
|
||||
ExprKind::Unary(op, arg) => {
|
||||
if op == UnOp::Neg {
|
||||
self.arithmetic_context.check_negate(cx, e, arg);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_post(&mut self, _: &LateContext<'_>, e: &Expr<'_>) {
|
||||
self.arithmetic_context.expr_post(e.hir_id);
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) {
|
||||
self.arithmetic_context.enter_body(cx, b);
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) {
|
||||
self.arithmetic_context.body_post(cx, b);
|
||||
}
|
||||
}
|
||||
|
||||
fn macro_with_not_op(e: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Unary(_, e) = e.kind {
|
||||
e.span.from_expansion()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
@ -2,35 +2,35 @@ use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sext;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_hir::{BinOpKind, Expr};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use std::fmt::Display;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for modulo arithmetic.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The results of modulo (%) operation might differ
|
||||
/// depending on the language, when negative numbers are involved.
|
||||
/// If you interop with different languages it might be beneficial
|
||||
/// to double check all places that use modulo arithmetic.
|
||||
///
|
||||
/// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = -17 % 3;
|
||||
/// ```
|
||||
#[clippy::version = "1.42.0"]
|
||||
pub MODULO_ARITHMETIC,
|
||||
restriction,
|
||||
"any modulo arithmetic statement"
|
||||
}
|
||||
use super::MODULO_ARITHMETIC;
|
||||
|
||||
declare_lint_pass!(ModuloArithmetic => [MODULO_ARITHMETIC]);
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
lhs: &'tcx Expr<'_>,
|
||||
rhs: &'tcx Expr<'_>,
|
||||
) {
|
||||
if op == BinOpKind::Rem {
|
||||
let lhs_operand = analyze_operand(lhs, cx, e);
|
||||
let rhs_operand = analyze_operand(rhs, cx, e);
|
||||
if_chain! {
|
||||
if let Some(lhs_operand) = lhs_operand;
|
||||
if let Some(rhs_operand) = rhs_operand;
|
||||
then {
|
||||
check_const_operands(cx, e, &lhs_operand, &rhs_operand);
|
||||
}
|
||||
else {
|
||||
check_non_const_operands(cx, e, lhs);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct OperandInfo {
|
||||
string_representation: Option<String>,
|
||||
@ -124,27 +124,3 @@ fn check_non_const_operands<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ModuloArithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
match &expr.kind {
|
||||
ExprKind::Binary(op, lhs, rhs) | ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
if op.node == BinOpKind::Rem {
|
||||
let lhs_operand = analyze_operand(lhs, cx, expr);
|
||||
let rhs_operand = analyze_operand(rhs, cx, expr);
|
||||
if_chain! {
|
||||
if let Some(lhs_operand) = lhs_operand;
|
||||
if let Some(rhs_operand) = rhs_operand;
|
||||
then {
|
||||
check_const_operands(cx, expr, &lhs_operand, &rhs_operand);
|
||||
}
|
||||
else {
|
||||
check_non_const_operands(cx, expr, lhs);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
26
clippy_lints/src/operators/modulo_one.rs
Normal file
26
clippy_lints/src/operators/modulo_one.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{is_integer_const, unsext};
|
||||
use rustc_hir::{BinOpKind, Expr};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
||||
use super::MODULO_ONE;
|
||||
|
||||
pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: &Expr<'_>) {
|
||||
if op == BinOpKind::Rem {
|
||||
if is_integer_const(cx, right, 1) {
|
||||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
|
||||
if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
|
||||
if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
|
||||
span_lint(
|
||||
cx,
|
||||
MODULO_ONE,
|
||||
expr.span,
|
||||
"any number modulo -1 will panic/overflow or result in 0",
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
36
clippy_lints/src/operators/needless_bitwise_bool.rs
Normal file
36
clippy_lints/src/operators/needless_bitwise_bool.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::NEEDLESS_BITWISE_BOOL;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
|
||||
let op_str = match op {
|
||||
BinOpKind::BitAnd => "&&",
|
||||
BinOpKind::BitOr => "||",
|
||||
_ => return,
|
||||
};
|
||||
if matches!(
|
||||
rhs.kind,
|
||||
ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..)
|
||||
) && cx.typeck_results().expr_ty(e).is_bool()
|
||||
&& !rhs.can_have_side_effects()
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_BITWISE_BOOL,
|
||||
e.span,
|
||||
"use of bitwise operator instead of lazy operator between booleans",
|
||||
|diag| {
|
||||
if let Some(lhs_snip) = snippet_opt(cx, lhs.span)
|
||||
&& let Some(rhs_snip) = snippet_opt(cx, rhs.span)
|
||||
{
|
||||
let sugg = format!("{} {} {}", lhs_snip, op_str, rhs_snip);
|
||||
diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
127
clippy_lints/src/operators/numeric_arithmetic.rs
Normal file
127
clippy_lints/src/operators/numeric_arithmetic.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use clippy_utils::consts::constant_simple;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
use super::{FLOAT_ARITHMETIC, INTEGER_ARITHMETIC};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Context {
|
||||
expr_id: Option<hir::HirId>,
|
||||
/// This field is used to check whether expressions are constants, such as in enum discriminants
|
||||
/// and consts
|
||||
const_span: Option<Span>,
|
||||
}
|
||||
impl Context {
|
||||
fn skip_expr(&mut self, e: &hir::Expr<'_>) -> bool {
|
||||
self.expr_id.is_some() || self.const_span.map_or(false, |span| span.contains(e.span))
|
||||
}
|
||||
|
||||
pub fn check_binary<'tcx>(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
op: hir::BinOpKind,
|
||||
l: &'tcx hir::Expr<'_>,
|
||||
r: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if self.skip_expr(expr) {
|
||||
return;
|
||||
}
|
||||
match op {
|
||||
hir::BinOpKind::And
|
||||
| hir::BinOpKind::Or
|
||||
| hir::BinOpKind::BitAnd
|
||||
| hir::BinOpKind::BitOr
|
||||
| hir::BinOpKind::BitXor
|
||||
| hir::BinOpKind::Eq
|
||||
| hir::BinOpKind::Lt
|
||||
| hir::BinOpKind::Le
|
||||
| hir::BinOpKind::Ne
|
||||
| hir::BinOpKind::Ge
|
||||
| hir::BinOpKind::Gt => return,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
|
||||
if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
|
||||
match op {
|
||||
hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
|
||||
hir::ExprKind::Lit(_lit) => (),
|
||||
hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
|
||||
if let hir::ExprKind::Lit(lit) = &expr.kind {
|
||||
if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_id = Some(expr.hir_id);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_id = Some(expr.hir_id);
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_id = Some(expr.hir_id);
|
||||
},
|
||||
}
|
||||
} else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
|
||||
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
|
||||
self.expr_id = Some(expr.hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_negate<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
|
||||
if self.skip_expr(expr) {
|
||||
return;
|
||||
}
|
||||
let ty = cx.typeck_results().expr_ty(arg);
|
||||
if constant_simple(cx, cx.typeck_results(), expr).is_none() {
|
||||
if ty.is_integral() {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_id = Some(expr.hir_id);
|
||||
} else if ty.is_floating_point() {
|
||||
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
|
||||
self.expr_id = Some(expr.hir_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expr_post(&mut self, id: hir::HirId) {
|
||||
if Some(id) == self.expr_id {
|
||||
self.expr_id = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||
let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
|
||||
|
||||
match cx.tcx.hir().body_owner_kind(body_owner) {
|
||||
hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
|
||||
let body_span = cx.tcx.def_span(body_owner);
|
||||
|
||||
if let Some(span) = self.const_span {
|
||||
if span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.const_span = Some(body_span);
|
||||
},
|
||||
hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||
let body_owner = cx.tcx.hir().body_owner(body.id());
|
||||
let body_span = cx.tcx.hir().span(body_owner);
|
||||
|
||||
if let Some(span) = self.const_span {
|
||||
if span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.const_span = None;
|
||||
}
|
||||
}
|
218
clippy_lints/src/operators/op_ref.rs
Normal file
218
clippy_lints/src/operators/op_ref.rs
Normal file
@ -0,0 +1,218 @@
|
||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::get_enclosing_block;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
use super::OP_REF;
|
||||
|
||||
#[expect(clippy::similar_names, clippy::too_many_lines)]
|
||||
pub(crate) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
let (trait_id, requires_ref) = match op {
|
||||
BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
|
||||
BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
|
||||
BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
|
||||
BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
|
||||
BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
|
||||
// don't lint short circuiting ops
|
||||
BinOpKind::And | BinOpKind::Or => return,
|
||||
BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
|
||||
BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
|
||||
BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
|
||||
BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
|
||||
BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
|
||||
BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
|
||||
BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
|
||||
(cx.tcx.lang_items().partial_ord_trait(), true)
|
||||
},
|
||||
};
|
||||
if let Some(trait_id) = trait_id {
|
||||
match (&left.kind, &right.kind) {
|
||||
// do not suggest to dereference literals
|
||||
(&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
|
||||
// &foo == &bar
|
||||
(&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
|
||||
let lty = cx.typeck_results().expr_ty(l);
|
||||
let rty = cx.typeck_results().expr_ty(r);
|
||||
let lcpy = is_copy(cx, lty);
|
||||
let rcpy = is_copy(cx, rty);
|
||||
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
|
||||
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|
||||
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
|
||||
{
|
||||
return; // Don't lint
|
||||
}
|
||||
}
|
||||
// either operator autorefs or both args are copyable
|
||||
if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OP_REF,
|
||||
e.span,
|
||||
"needlessly taken reference of both operands",
|
||||
|diag| {
|
||||
let lsnip = snippet(cx, l.span, "...").to_string();
|
||||
let rsnip = snippet(cx, r.span, "...").to_string();
|
||||
multispan_sugg(
|
||||
diag,
|
||||
"use the values directly",
|
||||
vec![(left.span, lsnip), (right.span, rsnip)],
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if lcpy
|
||||
&& !rcpy
|
||||
&& implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OP_REF,
|
||||
e.span,
|
||||
"needlessly taken reference of left operand",
|
||||
|diag| {
|
||||
let lsnip = snippet(cx, l.span, "...").to_string();
|
||||
diag.span_suggestion(
|
||||
left.span,
|
||||
"use the left value directly",
|
||||
lsnip,
|
||||
Applicability::MaybeIncorrect, // FIXME #2597
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if !lcpy
|
||||
&& rcpy
|
||||
&& implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OP_REF,
|
||||
e.span,
|
||||
"needlessly taken reference of right operand",
|
||||
|diag| {
|
||||
let rsnip = snippet(cx, r.span, "...").to_string();
|
||||
diag.span_suggestion(
|
||||
right.span,
|
||||
"use the right value directly",
|
||||
rsnip,
|
||||
Applicability::MaybeIncorrect, // FIXME #2597
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
// &foo == bar
|
||||
(&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => {
|
||||
let lty = cx.typeck_results().expr_ty(l);
|
||||
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
|
||||
let rty = cx.typeck_results().expr_ty(right);
|
||||
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|
||||
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
|
||||
{
|
||||
return; // Don't lint
|
||||
}
|
||||
}
|
||||
let lcpy = is_copy(cx, lty);
|
||||
if (requires_ref || lcpy)
|
||||
&& implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OP_REF,
|
||||
e.span,
|
||||
"needlessly taken reference of left operand",
|
||||
|diag| {
|
||||
let lsnip = snippet(cx, l.span, "...").to_string();
|
||||
diag.span_suggestion(
|
||||
left.span,
|
||||
"use the left value directly",
|
||||
lsnip,
|
||||
Applicability::MaybeIncorrect, // FIXME #2597
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
// foo == &bar
|
||||
(_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
|
||||
let rty = cx.typeck_results().expr_ty(r);
|
||||
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
|
||||
let lty = cx.typeck_results().expr_ty(left);
|
||||
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|
||||
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
|
||||
{
|
||||
return; // Don't lint
|
||||
}
|
||||
}
|
||||
let rcpy = is_copy(cx, rty);
|
||||
if (requires_ref || rcpy)
|
||||
&& implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
|
||||
{
|
||||
span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
|
||||
let rsnip = snippet(cx, r.span, "...").to_string();
|
||||
diag.span_suggestion(
|
||||
right.span,
|
||||
"use the right value directly",
|
||||
rsnip,
|
||||
Applicability::MaybeIncorrect, // FIXME #2597
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn in_impl<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
bin_op: DefId,
|
||||
) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> {
|
||||
if_chain! {
|
||||
if let Some(block) = get_enclosing_block(cx, e.hir_id);
|
||||
if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id());
|
||||
let item = cx.tcx.hir().expect_item(impl_def_id.expect_local());
|
||||
if let ItemKind::Impl(item) = &item.kind;
|
||||
if let Some(of_trait) = &item.of_trait;
|
||||
if let Some(seg) = of_trait.path.segments.last();
|
||||
if let Some(Res::Def(_, trait_id)) = seg.res;
|
||||
if trait_id == bin_op;
|
||||
if let Some(generic_args) = seg.args;
|
||||
if let Some(GenericArg::Type(other_ty)) = generic_args.args.last();
|
||||
|
||||
then {
|
||||
Some((item.self_ty, other_ty))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let ty::Adt(adt_def, _) = middle_ty.kind();
|
||||
if let Some(local_did) = adt_def.did().as_local();
|
||||
let item = cx.tcx.hir().expect_item(local_did);
|
||||
let middle_ty_id = item.def_id.to_def_id();
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
|
||||
if let Res::Def(_, hir_ty_id) = path.res;
|
||||
|
||||
then {
|
||||
hir_ty_id == middle_ty_id
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
65
clippy_lints/src/operators/ptr_eq.rs
Normal file
65
clippy_lints/src/operators/ptr_eq.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::PTR_EQ;
|
||||
|
||||
static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
if BinOpKind::Eq == op {
|
||||
let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
|
||||
(Some(lhs), Some(rhs)) => (lhs, rhs),
|
||||
_ => (left, right),
|
||||
};
|
||||
|
||||
if_chain! {
|
||||
if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
|
||||
if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
|
||||
if let Some(left_snip) = snippet_opt(cx, left_var.span);
|
||||
if let Some(right_snip) = snippet_opt(cx, right_var.span);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PTR_EQ,
|
||||
expr.span,
|
||||
LINT_MSG,
|
||||
"try",
|
||||
format!("std::ptr::eq({}, {})", left_snip, right_snip),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the given expression is a cast to a usize, return the lhs of the cast
|
||||
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
|
||||
fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
|
||||
if let ExprKind::Cast(expr, _) = cast_expr.kind {
|
||||
return Some(expr);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
|
||||
// E.g., `foo as *const _` returns `foo`.
|
||||
fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
|
||||
if let ExprKind::Cast(expr, _) = cast_expr.kind {
|
||||
return Some(expr);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
20
clippy_lints/src/operators/self_assignment.rs
Normal file
20
clippy_lints/src/operators/self_assignment.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::eq_expr_value;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::SELF_ASSIGNMENT;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>) {
|
||||
if eq_expr_value(cx, lhs, rhs) {
|
||||
let lhs = snippet(cx, lhs.span, "<lhs>");
|
||||
let rhs = snippet(cx, rhs.span, "<rhs>");
|
||||
span_lint(
|
||||
cx,
|
||||
SELF_ASSIGNMENT,
|
||||
e.span,
|
||||
&format!("self-assignment of `{}` to `{}`", rhs, lhs),
|
||||
);
|
||||
}
|
||||
}
|
44
clippy_lints/src/operators/verbose_bit_mask.rs
Normal file
44
clippy_lints/src/operators/verbose_bit_mask.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::VERBOSE_BIT_MASK;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
op: BinOpKind,
|
||||
left: &'tcx Expr<'_>,
|
||||
right: &'tcx Expr<'_>,
|
||||
threshold: u64,
|
||||
) {
|
||||
if BinOpKind::Eq == op
|
||||
&& let ExprKind::Binary(op1, left1, right1) = &left.kind
|
||||
&& BinOpKind::BitAnd == op1.node
|
||||
&& let ExprKind::Lit(lit) = &right1.kind
|
||||
&& let LitKind::Int(n, _) = lit.node
|
||||
&& let ExprKind::Lit(lit1) = &right.kind
|
||||
&& let LitKind::Int(0, _) = lit1.node
|
||||
&& n.leading_zeros() == n.count_zeros()
|
||||
&& n > u128::from(threshold)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
VERBOSE_BIT_MASK,
|
||||
e.span,
|
||||
"bit mask could be simplified with a call to `trailing_zeros`",
|
||||
|diag| {
|
||||
let sugg = Sugg::hir(cx, left1, "...").maybe_par();
|
||||
diag.span_suggestion(
|
||||
e.span,
|
||||
"try",
|
||||
format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Use `std::ptr::eq` when applicable
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `ptr::eq` can be used to compare `&T` references
|
||||
/// (which coerce to `*const T` implicitly) by their address rather than
|
||||
/// comparing the values they point to.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let a = &[1, 2, 3];
|
||||
/// let b = &[1, 2, 3];
|
||||
///
|
||||
/// assert!(a as *const _ as usize == b as *const _ as usize);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let a = &[1, 2, 3];
|
||||
/// let b = &[1, 2, 3];
|
||||
///
|
||||
/// assert!(std::ptr::eq(a, b));
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub PTR_EQ,
|
||||
style,
|
||||
"use `std::ptr::eq` when comparing raw pointers"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PtrEq => [PTR_EQ]);
|
||||
|
||||
static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for PtrEq {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(ref op, left, right) = expr.kind {
|
||||
if BinOpKind::Eq == op.node {
|
||||
let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
|
||||
(Some(lhs), Some(rhs)) => (lhs, rhs),
|
||||
_ => (left, right),
|
||||
};
|
||||
|
||||
if_chain! {
|
||||
if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
|
||||
if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
|
||||
if let Some(left_snip) = snippet_opt(cx, left_var.span);
|
||||
if let Some(right_snip) = snippet_opt(cx, right_var.span);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PTR_EQ,
|
||||
expr.span,
|
||||
LINT_MSG,
|
||||
"try",
|
||||
format!("std::ptr::eq({}, {})", left_snip, right_snip),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the given expression is a cast to a usize, return the lhs of the cast
|
||||
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
|
||||
fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
|
||||
if let ExprKind::Cast(expr, _) = cast_expr.kind {
|
||||
return Some(expr);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
|
||||
// E.g., `foo as *const _` returns `foo`.
|
||||
fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
|
||||
if let ExprKind::Cast(expr, _) = cast_expr.kind {
|
||||
return Some(expr);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::eq_expr_value;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for explicit self-assignments.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Self-assignments are redundant and unlikely to be
|
||||
/// intentional.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If expression contains any deref coercions or
|
||||
/// indexing operations they are assumed not to have any side effects.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct Event {
|
||||
/// x: i32,
|
||||
/// }
|
||||
///
|
||||
/// fn copy_position(a: &mut Event, b: &Event) {
|
||||
/// a.x = a.x;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Should be:
|
||||
/// ```rust
|
||||
/// struct Event {
|
||||
/// x: i32,
|
||||
/// }
|
||||
///
|
||||
/// fn copy_position(a: &mut Event, b: &Event) {
|
||||
/// a.x = b.x;
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub SELF_ASSIGNMENT,
|
||||
correctness,
|
||||
"explicit self-assignment"
|
||||
}
|
||||
|
||||
declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for SelfAssignment {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Assign(lhs, rhs, _) = &expr.kind {
|
||||
if eq_expr_value(cx, lhs, rhs) {
|
||||
let lhs = snippet(cx, lhs.span, "<lhs>");
|
||||
let rhs = snippet(cx, rhs.span, "<rhs>");
|
||||
span_lint(
|
||||
cx,
|
||||
SELF_ASSIGNMENT,
|
||||
expr.span,
|
||||
&format!("self-assignment of `{}` to `{}`", rhs, lhs),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#[allow(unused_variables)]
|
||||
#[allow(unused_variables, clippy::eq_op)]
|
||||
#[warn(clippy::zero_divided_by_zero)]
|
||||
fn main() {
|
||||
let nan = 0.0 / 0.0;
|
||||
|
@ -1,11 +1,3 @@
|
||||
error: equal expressions as operands to `/`
|
||||
--> $DIR/zero_div_zero.rs:4:15
|
||||
|
|
||||
LL | let nan = 0.0 / 0.0;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: `#[deny(clippy::eq_op)]` on by default
|
||||
|
||||
error: constant division of `0.0` with `0.0` will always result in NaN
|
||||
--> $DIR/zero_div_zero.rs:4:15
|
||||
|
|
||||
@ -15,12 +7,6 @@ LL | let nan = 0.0 / 0.0;
|
||||
= note: `-D clippy::zero-divided-by-zero` implied by `-D warnings`
|
||||
= help: consider using `f64::NAN` if you would like a constant representing NaN
|
||||
|
||||
error: equal expressions as operands to `/`
|
||||
--> $DIR/zero_div_zero.rs:5:19
|
||||
|
|
||||
LL | let f64_nan = 0.0 / 0.0f64;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: constant division of `0.0` with `0.0` will always result in NaN
|
||||
--> $DIR/zero_div_zero.rs:5:19
|
||||
|
|
||||
@ -29,12 +15,6 @@ LL | let f64_nan = 0.0 / 0.0f64;
|
||||
|
|
||||
= help: consider using `f64::NAN` if you would like a constant representing NaN
|
||||
|
||||
error: equal expressions as operands to `/`
|
||||
--> $DIR/zero_div_zero.rs:6:25
|
||||
|
|
||||
LL | let other_f64_nan = 0.0f64 / 0.0;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: constant division of `0.0` with `0.0` will always result in NaN
|
||||
--> $DIR/zero_div_zero.rs:6:25
|
||||
|
|
||||
@ -43,12 +23,6 @@ LL | let other_f64_nan = 0.0f64 / 0.0;
|
||||
|
|
||||
= help: consider using `f64::NAN` if you would like a constant representing NaN
|
||||
|
||||
error: equal expressions as operands to `/`
|
||||
--> $DIR/zero_div_zero.rs:7:28
|
||||
|
|
||||
LL | let one_more_f64_nan = 0.0f64 / 0.0f64;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: constant division of `0.0` with `0.0` will always result in NaN
|
||||
--> $DIR/zero_div_zero.rs:7:28
|
||||
|
|
||||
@ -57,5 +31,5 @@ LL | let one_more_f64_nan = 0.0f64 / 0.0f64;
|
||||
|
|
||||
= help: consider using `f64::NAN` if you would like a constant representing NaN
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user