mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-19 03:03:21 +00:00
Introduce hir::ExprKind::Let - Take 2
This commit is contained in:
parent
80bff87c6f
commit
b97d4c062b
@ -1,5 +1,6 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call};
|
||||
use if_chain::if_chain;
|
||||
@ -116,8 +117,8 @@ enum AssertKind {
|
||||
/// where `message` is any expression and `c` is a constant bool.
|
||||
fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
|
||||
if_chain! {
|
||||
if let ExprKind::If(cond, then, _) = expr.kind;
|
||||
if let ExprKind::Unary(UnOp::Not, expr) = cond.kind;
|
||||
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
|
||||
if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind;
|
||||
// bind the first argument of the `assert!` macro
|
||||
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
|
||||
// block
|
||||
|
@ -1,4 +1,5 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_block_with_applicability;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{differing_macro_contexts, get_parent_expr};
|
||||
@ -92,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::If(cond, _, _) = &expr.kind {
|
||||
if let Some(higher::If { cond, .. }) = higher::If::hir(expr) {
|
||||
if let ExprKind::Block(block, _) = &cond.kind {
|
||||
if block.rules == BlockCheckMode::DefaultBlock {
|
||||
if block.stmts.is_empty() {
|
||||
|
@ -1,9 +1,9 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
|
||||
use clippy_utils::{higher, is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
|
||||
use rustc_hir::{Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{MultiSpan, Span};
|
||||
@ -49,22 +49,44 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let ExprKind::Match(_expr, arms, _source) = expr.kind {
|
||||
if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
|
||||
for arm in arms {
|
||||
check_arm(arm, wild_arm, cx);
|
||||
if let Some(higher::IfLet {
|
||||
let_pat,
|
||||
if_then,
|
||||
if_else,
|
||||
..
|
||||
}) = higher::IfLet::hir(expr)
|
||||
{
|
||||
check_arm(cx, if_then, None, let_pat, if_else);
|
||||
|
||||
check_if_let(cx, if_then, let_pat);
|
||||
}
|
||||
|
||||
if let ExprKind::Match(_expr, arms, _source) = expr.kind {
|
||||
if let Some(wild_arm) = arms.iter().rfind(|arm| is_wild_like(cx, &arm.pat.kind, &arm.guard)) {
|
||||
for arm in arms {
|
||||
check_arm(cx, arm.body, arm.guard.as_ref(), arm.pat, Some(wild_arm.body));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(first_arm) = arms.get(0) {
|
||||
check_if_let(cx, &first_arm.body, &first_arm.pat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext<'tcx>) {
|
||||
let expr = strip_singleton_blocks(arm.body);
|
||||
fn check_arm<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
outer_block: &'tcx Expr<'tcx>,
|
||||
outer_guard: Option<&Guard<'tcx>>,
|
||||
outer_pat: &'tcx Pat<'tcx>,
|
||||
wild_outer_block: Option<&'tcx Expr<'tcx>>,
|
||||
) {
|
||||
let expr = strip_singleton_blocks(outer_block);
|
||||
if_chain! {
|
||||
if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind;
|
||||
// the outer arm pattern and the inner match
|
||||
if expr_in.span.ctxt() == arm.pat.span.ctxt();
|
||||
if expr_in.span.ctxt() == outer_pat.span.ctxt();
|
||||
// there must be no more than two arms in the inner match for this lint
|
||||
if arms_inner.len() == 2;
|
||||
// no if guards on the inner match
|
||||
@ -73,18 +95,18 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
|
||||
// match <local> { .. }
|
||||
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in));
|
||||
// one of the branches must be "wild-like"
|
||||
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
|
||||
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| is_wild_like(cx, &arm_inner.pat.kind, &arm_inner.guard));
|
||||
let (wild_inner_arm, non_wild_inner_arm) =
|
||||
(&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
|
||||
if !pat_contains_or(non_wild_inner_arm.pat);
|
||||
// the binding must come from the pattern of the containing match arm
|
||||
// ..<local>.. => match <local> { .. }
|
||||
if let Some(binding_span) = find_pat_binding(arm.pat, binding_id);
|
||||
if let Some(binding_span) = find_pat_binding(outer_pat, binding_id);
|
||||
// the "wild-like" branches must be equal
|
||||
if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
|
||||
if wild_outer_block.map(|el| SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, el)).unwrap_or(true);
|
||||
// the binding must not be used in the if guard
|
||||
let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
|
||||
if match arm.guard {
|
||||
if match outer_guard {
|
||||
None => true,
|
||||
Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr),
|
||||
};
|
||||
@ -107,6 +129,31 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
|
||||
}
|
||||
}
|
||||
|
||||
fn check_if_let<'tcx>(cx: &LateContext<'tcx>, outer_expr: &'tcx Expr<'tcx>, outer_pat: &'tcx Pat<'tcx>) {
|
||||
let block_inner = strip_singleton_blocks(outer_expr);
|
||||
if_chain! {
|
||||
if let Some(higher::IfLet { if_then: inner_if_then, let_expr: inner_let_expr, let_pat: inner_let_pat, .. }) = higher::IfLet::hir(block_inner);
|
||||
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_let_expr));
|
||||
if let Some(binding_span) = find_pat_binding(outer_pat, binding_id);
|
||||
let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
|
||||
if !used_visitor.check_expr(inner_if_then);
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
COLLAPSIBLE_MATCH,
|
||||
block_inner.span,
|
||||
"unnecessary nested `if let` or `match`",
|
||||
|diag| {
|
||||
let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_let_pat.span]);
|
||||
help_span.push_span_label(binding_span, "replace this binding".into());
|
||||
help_span.push_span_label(inner_let_pat.span, "with this pattern".into());
|
||||
diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
|
||||
while let ExprKind::Block(block, _) = expr.kind {
|
||||
match (block.stmts, block.expr) {
|
||||
@ -122,13 +169,13 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir>
|
||||
}
|
||||
|
||||
/// A "wild-like" pattern is wild ("_") or `None`.
|
||||
/// For this lint to apply, both the outer and inner match expressions
|
||||
/// For this lint to apply, both the outer and inner patterns
|
||||
/// must have "wild-like" branches that can be combined.
|
||||
fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
if arm.guard.is_some() {
|
||||
fn is_wild_like(cx: &LateContext<'_>, pat_kind: &PatKind<'_>, arm_guard: &Option<Guard<'_>>) -> bool {
|
||||
if arm_guard.is_some() {
|
||||
return false;
|
||||
}
|
||||
match arm.pat.kind {
|
||||
match pat_kind {
|
||||
PatKind::Binding(..) | PatKind::Wild => true,
|
||||
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
_ => false,
|
||||
|
@ -316,9 +316,10 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
|
||||
let mut start_eq = usize::MAX;
|
||||
let mut end_eq = usize::MAX;
|
||||
let mut expr_eq = true;
|
||||
for win in blocks.windows(2) {
|
||||
let l_stmts = win[0].stmts;
|
||||
let r_stmts = win[1].stmts;
|
||||
let mut iter = blocks.windows(2);
|
||||
while let Some(&[win0, win1]) = iter.next() {
|
||||
let l_stmts = win0.stmts;
|
||||
let r_stmts = win1.stmts;
|
||||
|
||||
// `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752.
|
||||
// The comparison therefore needs to be done in a way that builds the correct context.
|
||||
@ -335,22 +336,22 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
|
||||
it1.zip(it2)
|
||||
.fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 })
|
||||
};
|
||||
let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r));
|
||||
let block_expr_eq = both(&win0.expr, &win1.expr, |l, r| evaluator.eq_expr(l, r));
|
||||
|
||||
// IF_SAME_THEN_ELSE
|
||||
if_chain! {
|
||||
if block_expr_eq;
|
||||
if l_stmts.len() == r_stmts.len();
|
||||
if l_stmts.len() == current_start_eq;
|
||||
if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[0].hir_id);
|
||||
if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[1].hir_id);
|
||||
if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win0.hir_id);
|
||||
if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win1.hir_id);
|
||||
then {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
IF_SAME_THEN_ELSE,
|
||||
win[0].span,
|
||||
win0.span,
|
||||
"this `if` has identical blocks",
|
||||
Some(win[1].span),
|
||||
Some(win1.span),
|
||||
"same as this",
|
||||
);
|
||||
|
||||
|
@ -232,6 +232,7 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
|
||||
| ExprKind::If(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::Match(..)
|
||||
| ExprKind::Let(..)
|
||||
| ExprKind::Closure(..)
|
||||
| ExprKind::Block(..)
|
||||
| ExprKind::Assign(..)
|
||||
|
@ -1,3 +1,4 @@
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure_no_visit,
|
||||
diagnostics::span_lint_and_sugg,
|
||||
@ -5,6 +6,7 @@ use clippy_utils::{
|
||||
source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
|
||||
SpanlessEq,
|
||||
};
|
||||
use core::fmt::Write;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
|
||||
@ -13,7 +15,6 @@ use rustc_hir::{
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{Span, SyntaxContext, DUMMY_SP};
|
||||
use std::fmt::Write;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -62,10 +63,11 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
|
||||
impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (cond_expr, then_expr, else_expr) = match expr.kind {
|
||||
ExprKind::If(c, t, e) => (c, t, e),
|
||||
let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) {
|
||||
Some(higher::If { cond, then, r#else }) => (cond, then, r#else),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
|
||||
Some(x) => x,
|
||||
None => return,
|
||||
|
@ -100,7 +100,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
|
||||
if ex.span.ctxt() != expr.span.ctxt() {
|
||||
if decl.inputs.is_empty() {
|
||||
if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) {
|
||||
if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, ex) {
|
||||
// replace `|| vec![]` with `Vec::new`
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -3,6 +3,7 @@ use clippy_utils::consts::{
|
||||
Constant::{Int, F32, F64},
|
||||
};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -545,11 +546,11 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a
|
||||
|
||||
fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::If(cond, body, else_body) = expr.kind;
|
||||
if let ExprKind::Block(block, _) = body.kind;
|
||||
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
|
||||
if let ExprKind::Block(block, _) = then.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(if_body_expr) = block.expr;
|
||||
if let Some(ExprKind::Block(else_block, _)) = else_body.map(|el| &el.kind);
|
||||
if let Some(ExprKind::Block(else_block, _)) = r#else.map(|el| &el.kind);
|
||||
if else_block.stmts.is_empty();
|
||||
if let Some(else_body_expr) = else_block.expr;
|
||||
if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
|
||||
|
@ -1,9 +1,10 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::SpanlessEq;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
@ -42,7 +43,7 @@ declare_clippy_lint! {
|
||||
declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
let mut arm_visit = ArmVisitor {
|
||||
mutex_lock_called: false,
|
||||
found_mutex: None,
|
||||
@ -53,25 +54,23 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
|
||||
found_mutex: None,
|
||||
cx,
|
||||
};
|
||||
if let ExprKind::Match(
|
||||
op,
|
||||
arms,
|
||||
MatchSource::IfLetDesugar {
|
||||
contains_else_clause: true,
|
||||
},
|
||||
) = ex.kind
|
||||
if let Some(higher::IfLet {
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else: Some(if_else),
|
||||
..
|
||||
}) = higher::IfLet::hir(expr)
|
||||
{
|
||||
op_visit.visit_expr(op);
|
||||
op_visit.visit_expr(let_expr);
|
||||
if op_visit.mutex_lock_called {
|
||||
for arm in arms {
|
||||
arm_visit.visit_arm(arm);
|
||||
}
|
||||
arm_visit.visit_expr(if_then);
|
||||
arm_visit.visit_expr(if_else);
|
||||
|
||||
if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
IF_LET_MUTEX,
|
||||
ex.span,
|
||||
expr.span,
|
||||
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
|
||||
None,
|
||||
"move the lock call outside of the `if let ...` expression",
|
||||
|
@ -1,10 +1,11 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::method_chain_args;
|
||||
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::{Expr, ExprKind, MatchSource, PatKind, QPath};
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
@ -44,17 +45,17 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
|
||||
impl<'tcx> LateLintPass<'tcx> for OkIfLet {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! { //begin checking variables
|
||||
if let ExprKind::Match(op, body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let
|
||||
if let ExprKind::MethodCall(_, ok_span, result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
|
||||
if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = body[0].pat.kind; //get operation
|
||||
if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
|
||||
if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr);
|
||||
if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
|
||||
if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = let_pat.kind; //get operation
|
||||
if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type);
|
||||
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
|
||||
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
|
||||
let trimmed_ok = snippet_with_applicability(cx, op.span.until(ok_span), "", &mut applicability);
|
||||
let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
|
||||
let sugg = format!(
|
||||
"if let Ok({}) = {}",
|
||||
some_expr_string,
|
||||
@ -63,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IF_LET_SOME_RESULT,
|
||||
expr.span.with_hi(op.span.hi()),
|
||||
expr.span.with_hi(let_expr.span.hi()),
|
||||
"matching on `Some` with `ok()` is redundant",
|
||||
&format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
|
||||
sugg,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv, msrvs};
|
||||
use clippy_utils::{higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
@ -70,7 +70,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone {
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::If(cond, then, Some(els)) = expr.kind;
|
||||
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr);
|
||||
if let ExprKind::Block(then_block, _) = then.kind;
|
||||
if let Some(then_expr) = then_block.expr;
|
||||
if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
|
||||
|
@ -1,4 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::{in_macro, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
@ -42,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
||||
return;
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::If(cond, then, None) = &expr.kind;
|
||||
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
|
||||
|
||||
// Check if the conditional expression is a binary operation
|
||||
if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind;
|
||||
|
@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Index(array, index) = &expr.kind {
|
||||
let ty = cx.typeck_results().expr_ty(array).peel_refs();
|
||||
if let Some(range) = higher::range(index) {
|
||||
if let Some(range) = higher::Range::hir(index) {
|
||||
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
|
||||
if let ty::Array(_, s) = ty.kind() {
|
||||
let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) {
|
||||
|
@ -172,7 +172,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
||||
Finite
|
||||
}
|
||||
},
|
||||
ExprKind::Struct(..) => higher::range(expr).map_or(false, |r| r.end.is_none()).into(),
|
||||
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
|
||||
_ => Finite,
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
||||
if let hir::StmtKind::Local(local) = stmt.kind;
|
||||
if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
|
||||
if let hir::StmtKind::Expr(if_) = expr.kind;
|
||||
if let hir::ExprKind::If(cond, then, ref else_) = if_.kind;
|
||||
if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind;
|
||||
let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id);
|
||||
if !used_visitor.check_expr(cond);
|
||||
if let hir::ExprKind::Block(then, _) = then.kind;
|
||||
@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
||||
);
|
||||
if has_interior_mutability { return; }
|
||||
|
||||
let (default_multi_stmts, default) = if let Some(else_) = *else_ {
|
||||
let (default_multi_stmts, default) = if let Some(else_) = else_ {
|
||||
if let hir::ExprKind::Block(else_, _) = else_.kind {
|
||||
if let Some(default) = check_assign(cx, canonical_id, else_) {
|
||||
(else_.stmts.len() > 1, default)
|
||||
|
@ -1,11 +1,12 @@
|
||||
use super::utils::make_iterator_snippet;
|
||||
use super::MANUAL_FLATTEN;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionSome, ResultOk};
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind};
|
||||
use rustc_hir::{Expr, ExprKind, Pat, PatKind, StmtKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::source_map::Span;
|
||||
@ -36,14 +37,12 @@ pub(super) fn check<'tcx>(
|
||||
|
||||
if_chain! {
|
||||
if let Some(inner_expr) = inner_expr;
|
||||
if let ExprKind::Match(
|
||||
match_expr, match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false }
|
||||
) = inner_expr.kind;
|
||||
if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(inner_expr);
|
||||
// Ensure match_expr in `if let` statement is the same as the pat from the for-loop
|
||||
if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
|
||||
if path_to_local_id(match_expr, pat_hir_id);
|
||||
if path_to_local_id(let_expr, pat_hir_id);
|
||||
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
|
||||
if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind;
|
||||
if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind;
|
||||
let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
|
||||
let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
|
||||
if some_ctor || ok_ctor;
|
||||
@ -55,7 +54,7 @@ pub(super) fn check<'tcx>(
|
||||
// Prepare the help message
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
|
||||
let copied = match cx.typeck_results().expr_ty(match_expr).kind() {
|
||||
let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
|
||||
ty::Ref(_, inner, _) => match inner.kind() {
|
||||
ty::Ref(..) => ".copied()",
|
||||
_ => ""
|
||||
|
@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(
|
||||
start: Some(start),
|
||||
end: Some(end),
|
||||
limits,
|
||||
}) = higher::range(arg)
|
||||
}) = higher::Range::hir(arg)
|
||||
{
|
||||
// the var must be a single name
|
||||
if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
|
||||
|
@ -551,7 +551,7 @@ declare_lint_pass!(Loops => [
|
||||
impl<'tcx> LateLintPass<'tcx> for Loops {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some((pat, arg, body, span)) = higher::for_loop(expr) {
|
||||
if let Some(higher::ForLoop { pat, arg, body, span }) = higher::ForLoop::hir(expr) {
|
||||
// we don't want to check expanded macros
|
||||
// this check is not at the top of the function
|
||||
// since higher::for_loop expressions are marked as expansions
|
||||
@ -580,8 +580,8 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
|
||||
|
||||
while_let_on_iterator::check(cx, expr);
|
||||
|
||||
if let Some((cond, body)) = higher::while_loop(expr) {
|
||||
while_immutable_condition::check(cx, cond, body);
|
||||
if let Some(higher::While { if_cond, if_then, .. }) = higher::While::hir(&expr) {
|
||||
while_immutable_condition::check(cx, if_cond, if_then);
|
||||
}
|
||||
|
||||
needless_collect::check(expr, cx);
|
||||
|
@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
|
||||
start: Some(start),
|
||||
end: Some(end),
|
||||
..
|
||||
}) = higher::range(arg)
|
||||
}) = higher::Range::hir(arg)
|
||||
{
|
||||
let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
|
||||
if mut_ids[0].is_some() || mut_ids[1].is_some() {
|
||||
|
@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(
|
||||
start: Some(start),
|
||||
ref end,
|
||||
limits,
|
||||
}) = higher::range(arg)
|
||||
}) = higher::Range::hir(arg)
|
||||
{
|
||||
// the var must be a single name
|
||||
if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::utils::make_iterator_snippet;
|
||||
use super::NEVER_LOOP;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::higher::ForLoop;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, LoopSource, Node, Pat, Stmt, StmtKind};
|
||||
@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let LoopSource::ForLoop = source;
|
||||
if let Some((_, Node::Expr(parent_match))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1);
|
||||
if let Some((pat, iterator, _, for_span)) = higher::for_loop(parent_match);
|
||||
if let Some(ForLoop { arg: iterator, pat, span: for_span, .. }) = ForLoop::hir(parent_match);
|
||||
then {
|
||||
// Suggests using an `if let` instead. This is `Unspecified` because the
|
||||
// loop may (probably) contain `break` statements which would be invalid
|
||||
@ -111,6 +111,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
||||
| ExprKind::Unary(_, e)
|
||||
| ExprKind::Cast(e, _)
|
||||
| ExprKind::Type(e, _)
|
||||
| ExprKind::Let(_, e, _)
|
||||
| ExprKind::Field(e, _)
|
||||
| ExprKind::AddrOf(_, _, e)
|
||||
| ExprKind::Struct(_, _, Some(e))
|
||||
@ -128,7 +129,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
||||
// Break can come from the inner loop so remove them.
|
||||
absorb_break(&never_loop_block(b, main_loop_id))
|
||||
},
|
||||
ExprKind::If(e, e2, ref e3) => {
|
||||
ExprKind::If(e, e2, e3) => {
|
||||
let e1 = never_loop_expr(e, main_loop_id);
|
||||
let e2 = never_loop_expr(e2, main_loop_id);
|
||||
let e3 = e3
|
||||
@ -156,7 +157,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
||||
NeverLoopResult::AlwaysBreak
|
||||
}
|
||||
},
|
||||
ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
|
||||
ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
|
||||
combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
|
||||
}),
|
||||
ExprKind::InlineAsm(asm) => asm
|
||||
|
@ -1,8 +1,9 @@
|
||||
use super::WHILE_LET_LOOP;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, ExprKind, MatchSource, StmtKind};
|
||||
use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
||||
@ -11,41 +12,25 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'
|
||||
let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
|
||||
// or extract the first expression (if any) from the block
|
||||
if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) {
|
||||
if let ExprKind::Match(matchexpr, arms, ref source) = inner.kind {
|
||||
// ensure "if let" compatible match structure
|
||||
match *source {
|
||||
MatchSource::Normal | MatchSource::IfLetDesugar { .. } => {
|
||||
if let Some(higher::IfLet {
|
||||
let_pat,
|
||||
let_expr,
|
||||
if_else: Some(if_else),
|
||||
..
|
||||
}) = higher::IfLet::hir(inner)
|
||||
{
|
||||
if is_simple_break_expr(if_else) {
|
||||
could_be_while_let(cx, expr, let_pat, let_expr);
|
||||
}
|
||||
}
|
||||
|
||||
if let ExprKind::Match(ref matchexpr, ref arms, MatchSource::Normal) = inner.kind {
|
||||
if arms.len() == 2
|
||||
&& arms[0].guard.is_none()
|
||||
&& arms[1].guard.is_none()
|
||||
&& is_simple_break_expr(arms[1].body)
|
||||
&& is_simple_break_expr(&arms[1].body)
|
||||
{
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: we used to build a body here instead of using
|
||||
// ellipsis, this was removed because:
|
||||
// 1) it was ugly with big bodies;
|
||||
// 2) it was not indented properly;
|
||||
// 3) it wasn’t very smart (see #675).
|
||||
let mut applicability = Applicability::HasPlaceholders;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
WHILE_LET_LOOP,
|
||||
expr.span,
|
||||
"this loop could be written as a `while let` loop",
|
||||
"try",
|
||||
format!(
|
||||
"while let {} = {} {{ .. }}",
|
||||
snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability),
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
could_be_while_let(cx, expr, &arms[0].pat, matchexpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,15 +39,13 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'
|
||||
/// If a block begins with a statement (possibly a `let` binding) and has an
|
||||
/// expression, return it.
|
||||
fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if block.stmts.is_empty() {
|
||||
return None;
|
||||
if let Some(first_stmt) = block.stmts.get(0) {
|
||||
if let StmtKind::Local(local) = first_stmt.kind {
|
||||
return local.init;
|
||||
}
|
||||
}
|
||||
if let StmtKind::Local(local) = block.stmts[0].kind {
|
||||
local.init //.map(|expr| expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// If a block begins with an expression (with or without semicolon), return it.
|
||||
fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
@ -86,3 +69,34 @@ fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn could_be_while_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
let_pat: &'tcx Pat<'_>,
|
||||
let_expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: we used to build a body here instead of using
|
||||
// ellipsis, this was removed because:
|
||||
// 1) it was ugly with big bodies;
|
||||
// 2) it was not indented properly;
|
||||
// 3) it wasn’t very smart (see #675).
|
||||
let mut applicability = Applicability::HasPlaceholders;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
WHILE_LET_LOOP,
|
||||
expr.span,
|
||||
"this loop could be written as a `while let` loop",
|
||||
"try",
|
||||
format!(
|
||||
"while let {} = {} {{ .. }}",
|
||||
snippet_with_applicability(cx, let_pat.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, let_expr.span, "..", &mut applicability),
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::WHILE_LET_ON_ITERATOR;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{
|
||||
get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
|
||||
@ -7,27 +8,31 @@ use clippy_utils::{
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Mutability, Node, PatKind, QPath, UnOp};
|
||||
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{symbol::sym, Span, Symbol};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! {
|
||||
if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind;
|
||||
if let Some(higher::WhileLet {
|
||||
if_then,
|
||||
let_pat,
|
||||
let_expr,
|
||||
..
|
||||
}) = higher::WhileLet::hir(expr);
|
||||
// check for `Some(..)` pattern
|
||||
if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = arm.pat.kind;
|
||||
if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
|
||||
if let Res::Def(_, pat_did) = pat_path.res;
|
||||
if match_def_path(cx, pat_did, &paths::OPTION_SOME);
|
||||
// check for call to `Iterator::next`
|
||||
if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = scrutinee_expr.kind;
|
||||
if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = let_expr.kind;
|
||||
if method_name.ident.name == sym::next;
|
||||
if is_trait_method(cx, scrutinee_expr, sym::Iterator);
|
||||
if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr);
|
||||
if is_trait_method(cx, let_expr, sym::Iterator);
|
||||
if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr);
|
||||
// get the loop containing the match expression
|
||||
if let Some((_, Node::Expr(loop_expr))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1);
|
||||
if !uses_iter(cx, &iter_expr, arm.body);
|
||||
if !uses_iter(cx, &iter_expr_struct, if_then);
|
||||
then {
|
||||
(scrutinee_expr, iter_expr, some_pat, loop_expr)
|
||||
(let_expr, iter_expr_struct, some_pat, expr)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -81,6 +86,7 @@ struct IterExpr {
|
||||
/// The path being used.
|
||||
path: Res,
|
||||
}
|
||||
|
||||
/// Parses any expression to find out which field of which variable is used. Will return `None` if
|
||||
/// the expression might have side effects.
|
||||
fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
|
||||
@ -285,6 +291,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
|
||||
}
|
||||
impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
|
||||
type Map = ErasedMap<'tcx>;
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
|
||||
use clippy_utils::{
|
||||
@ -9,7 +10,7 @@ use clippy_utils::{
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
@ -43,48 +44,67 @@ declare_lint_pass!(ManualMap => [MANUAL_MAP]);
|
||||
impl LateLintPass<'_> for ManualMap {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Match(
|
||||
scrutinee,
|
||||
[arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }],
|
||||
match_kind,
|
||||
) = expr.kind
|
||||
if let Some(higher::IfLet {
|
||||
let_pat,
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else: Some(if_else),
|
||||
}) = higher::IfLet::hir(expr)
|
||||
{
|
||||
manage_lint(cx, expr, (&let_pat.kind, if_then), (&PatKind::Wild, if_else), let_expr);
|
||||
}
|
||||
|
||||
if let ExprKind::Match(scrutinee, [then @ Arm { guard: None, .. }, r#else @ Arm { guard: None, .. }], _) =
|
||||
expr.kind
|
||||
{
|
||||
manage_lint(
|
||||
cx,
|
||||
expr,
|
||||
(&then.pat.kind, then.body),
|
||||
(&r#else.pat.kind, r#else.body),
|
||||
scrutinee,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn manage_lint<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
then: (&'tcx PatKind<'_>, &'tcx Expr<'_>),
|
||||
r#else: (&'tcx PatKind<'_>, &'tcx Expr<'_>),
|
||||
scrut: &'tcx Expr<'_>,
|
||||
) {
|
||||
if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let (scrutinee_ty, ty_ref_count, ty_mutability) =
|
||||
peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
|
||||
let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrut));
|
||||
if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type)
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let (then_pat, then_expr) = then;
|
||||
let (else_pat, else_expr) = r#else;
|
||||
|
||||
let expr_ctxt = expr.span.ctxt();
|
||||
let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
|
||||
try_parse_pattern(cx, arm1.pat, expr_ctxt),
|
||||
try_parse_pattern(cx, arm2.pat, expr_ctxt),
|
||||
try_parse_pattern(cx, then_pat, expr_ctxt),
|
||||
try_parse_pattern(cx, else_pat, expr_ctxt),
|
||||
) {
|
||||
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count }))
|
||||
if is_none_expr(cx, arm1.body) =>
|
||||
{
|
||||
(arm2.body, pattern, ref_count, true)
|
||||
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => {
|
||||
(else_expr, pattern, ref_count, true)
|
||||
},
|
||||
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count }))
|
||||
if is_none_expr(cx, arm1.body) =>
|
||||
{
|
||||
(arm2.body, pattern, ref_count, false)
|
||||
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => {
|
||||
(else_expr, pattern, ref_count, false)
|
||||
},
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild))
|
||||
if is_none_expr(cx, arm2.body) =>
|
||||
{
|
||||
(arm1.body, pattern, ref_count, true)
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_expr) => {
|
||||
(then_expr, pattern, ref_count, true)
|
||||
},
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None))
|
||||
if is_none_expr(cx, arm2.body) =>
|
||||
{
|
||||
(arm1.body, pattern, ref_count, false)
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_expr) => {
|
||||
(then_expr, pattern, ref_count, false)
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
@ -99,10 +119,7 @@ impl LateLintPass<'_> for ManualMap {
|
||||
None => return,
|
||||
};
|
||||
|
||||
// These two lints will go back and forth with each other.
|
||||
if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
|
||||
&& !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
|
||||
{
|
||||
if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -129,10 +146,9 @@ impl LateLintPass<'_> for ManualMap {
|
||||
|
||||
// Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
|
||||
// it's being passed by value.
|
||||
let scrutinee = peel_hir_expr_refs(scrutinee).0;
|
||||
let scrutinee = peel_hir_expr_refs(scrut).0;
|
||||
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
|
||||
let scrutinee_str =
|
||||
if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
|
||||
let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
|
||||
format!("({})", scrutinee_str)
|
||||
} else {
|
||||
scrutinee_str.into()
|
||||
@ -183,7 +199,7 @@ impl LateLintPass<'_> for ManualMap {
|
||||
expr.span,
|
||||
"manual implementation of `Option::map`",
|
||||
"try this",
|
||||
if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause(cx.tcx, expr) {
|
||||
if is_else_clause(cx.tcx, expr) {
|
||||
format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
|
||||
} else {
|
||||
format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
|
||||
@ -191,8 +207,6 @@ impl LateLintPass<'_> for ManualMap {
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether the expression could be passed as a function, or whether a closure is needed.
|
||||
// Returns the function to be passed to `map` if it exists.
|
||||
@ -221,21 +235,28 @@ enum OptionPat<'a> {
|
||||
|
||||
// Try to parse into a recognized `Option` pattern.
|
||||
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
|
||||
fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
|
||||
fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
|
||||
match pat.kind {
|
||||
fn try_parse_pattern(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat_kind: &'tcx PatKind<'_>,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<OptionPat<'tcx>> {
|
||||
fn f(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat_kind: &'tcx PatKind<'_>,
|
||||
ref_count: usize,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<OptionPat<'tcx>> {
|
||||
match pat_kind {
|
||||
PatKind::Wild => Some(OptionPat::Wild),
|
||||
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
|
||||
PatKind::Ref(ref_pat, _) => f(cx, &ref_pat.kind, ref_count + 1, ctxt),
|
||||
PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
|
||||
PatKind::TupleStruct(ref qpath, [pattern], _)
|
||||
if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
|
||||
{
|
||||
PatKind::TupleStruct(ref qpath, [pattern], _) if is_lang_ctor(cx, qpath, OptionSome) => {
|
||||
Some(OptionPat::Some { pattern, ref_count })
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
f(cx, pat, 0, ctxt)
|
||||
f(cx, pat_kind, 0, ctxt)
|
||||
}
|
||||
|
||||
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
|
||||
|
@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::If(cond, then, _) = &expr.kind;
|
||||
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
|
||||
if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
|
||||
if let ExprKind::Path(target_path) = &target_arg.kind;
|
||||
@ -212,7 +212,7 @@ fn find_stripping<'tcx>(
|
||||
if is_ref_str(self.cx, ex);
|
||||
let unref = peel_ref(ex);
|
||||
if let ExprKind::Index(indexed, index) = &unref.kind;
|
||||
if let Some(higher::Range { start, end, .. }) = higher::range(index);
|
||||
if let Some(higher::Range { start, end, .. }) = higher::Range::hir(index);
|
||||
if let ExprKind::Path(path) = &indexed.kind;
|
||||
if self.cx.qpath_res(path, ex.hir_id) == self.target;
|
||||
then {
|
||||
|
@ -2,6 +2,7 @@ use clippy_utils::consts::{constant, miri_to_const, Constant};
|
||||
use clippy_utils::diagnostics::{
|
||||
multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
|
||||
@ -12,8 +13,10 @@ use clippy_utils::{
|
||||
strip_pat_refs,
|
||||
};
|
||||
use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||
use core::array;
|
||||
use core::iter::{once, ExactSizeIterator};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_ast::ast::{Attribute, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
@ -628,8 +631,11 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
check_match_single_binding(cx, ex, arms, expr);
|
||||
}
|
||||
}
|
||||
if let ExprKind::Match(ex, arms, _) = expr.kind {
|
||||
check_match_ref_pats(cx, ex, arms, expr);
|
||||
if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
|
||||
check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
|
||||
}
|
||||
if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr) {
|
||||
check_match_ref_pats(cx, let_expr, once(let_pat), expr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1179,40 +1185,41 @@ fn is_panic_block(block: &Block<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||
if has_only_ref_pats(arms) {
|
||||
let mut suggs = Vec::with_capacity(arms.len() + 1);
|
||||
let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
|
||||
let span = ex.span.source_callsite();
|
||||
suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
|
||||
(
|
||||
"you don't need to add `&` to both the expression and the patterns",
|
||||
"try",
|
||||
)
|
||||
} else {
|
||||
let span = ex.span.source_callsite();
|
||||
suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
|
||||
(
|
||||
"you don't need to add `&` to all patterns",
|
||||
"instead of prefixing all patterns with `&`, you can dereference the expression",
|
||||
)
|
||||
};
|
||||
fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
|
||||
where
|
||||
'b: 'a,
|
||||
I: Clone + Iterator<Item = &'a Pat<'b>>,
|
||||
{
|
||||
if !has_only_ref_pats(pats.clone()) {
|
||||
return;
|
||||
}
|
||||
|
||||
suggs.extend(arms.iter().filter_map(|a| {
|
||||
if let PatKind::Ref(refp, _) = a.pat.kind {
|
||||
Some((a.pat.span, snippet(cx, refp.span, "..").to_string()))
|
||||
let (first_sugg, msg, title);
|
||||
let span = ex.span.source_callsite();
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
|
||||
first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
|
||||
msg = "try";
|
||||
title = "you don't need to add `&` to both the expression and the patterns";
|
||||
} else {
|
||||
first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
|
||||
msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
|
||||
title = "you don't need to add `&` to all patterns";
|
||||
}
|
||||
|
||||
let remaining_suggs = pats.filter_map(|pat| {
|
||||
if let PatKind::Ref(ref refp, _) = pat.kind {
|
||||
Some((pat.span, snippet(cx, refp.span, "..").to_string()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
|
||||
if !expr.span.from_expansion() {
|
||||
multispan_sugg(diag, msg, suggs);
|
||||
multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
|
||||
@ -1286,46 +1293,99 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
|
||||
|
||||
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
|
||||
fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
|
||||
match match_source {
|
||||
MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
|
||||
MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
|
||||
_ => false,
|
||||
if let Some(higher::IfLet {
|
||||
let_pat,
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else: Some(if_else),
|
||||
}) = higher::IfLet::hir(expr)
|
||||
{
|
||||
return find_matches_sugg(
|
||||
cx,
|
||||
let_expr,
|
||||
array::IntoIter::new([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
|
||||
expr,
|
||||
true,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
|
||||
return find_matches_sugg(
|
||||
cx,
|
||||
scrut,
|
||||
arms.iter().map(|arm| {
|
||||
(
|
||||
cx.tcx.hir().attrs(arm.hir_id),
|
||||
Some(arm.pat),
|
||||
arm.body,
|
||||
arm.guard.as_ref(),
|
||||
)
|
||||
}),
|
||||
expr,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Lint a `match` or `if let` for replacement by `matches!`
|
||||
fn find_matches_sugg<'a, 'b, I>(
|
||||
cx: &LateContext<'_>,
|
||||
ex: &Expr<'_>,
|
||||
mut iter: I,
|
||||
expr: &Expr<'_>,
|
||||
is_if_let: bool,
|
||||
) -> bool
|
||||
where
|
||||
'b: 'a,
|
||||
I: Clone
|
||||
+ DoubleEndedIterator
|
||||
+ ExactSizeIterator
|
||||
+ Iterator<
|
||||
Item = (
|
||||
&'a [Attribute],
|
||||
Option<&'a Pat<'b>>,
|
||||
&'a Expr<'b>,
|
||||
Option<&'a Guard<'b>>,
|
||||
),
|
||||
>,
|
||||
{
|
||||
if_chain! {
|
||||
if iter.len() >= 2;
|
||||
if cx.typeck_results().expr_ty(expr).is_bool();
|
||||
if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
|
||||
let iter_without_last = iter.clone();
|
||||
if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
|
||||
if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
|
||||
if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
|
||||
if b0 != b1;
|
||||
if first_guard.is_none() || iter.len() == 0;
|
||||
if first_attrs.is_empty();
|
||||
if iter
|
||||
.all(|arm| {
|
||||
find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
|
||||
});
|
||||
then {
|
||||
if let Some(ref last_pat) = last_pat_opt {
|
||||
if !is_wild(last_pat) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Lint a `match` or desugared `if let` for replacement by `matches!`
|
||||
fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool {
|
||||
if_chain! {
|
||||
if arms.len() >= 2;
|
||||
if cx.typeck_results().expr_ty(expr).is_bool();
|
||||
if let Some((b1_arm, b0_arms)) = arms.split_last();
|
||||
if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared);
|
||||
if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared);
|
||||
if is_wild(b1_arm.pat);
|
||||
if b0 != b1;
|
||||
let if_guard = &b0_arms[0].guard;
|
||||
if if_guard.is_none() || b0_arms.len() == 1;
|
||||
if cx.tcx.hir().attrs(b0_arms[0].hir_id).is_empty();
|
||||
if b0_arms[1..].iter()
|
||||
.all(|arm| {
|
||||
find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) &&
|
||||
arm.guard.is_none() && cx.tcx.hir().attrs(arm.hir_id).is_empty()
|
||||
});
|
||||
then {
|
||||
// The suggestion may be incorrect, because some arms can have `cfg` attributes
|
||||
// evaluated into `false` and so such arms will be stripped before.
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let pat = {
|
||||
use itertools::Itertools as _;
|
||||
b0_arms.iter()
|
||||
.map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability))
|
||||
iter_without_last
|
||||
.filter_map(|arm| {
|
||||
let pat_span = arm.1?.span;
|
||||
Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
|
||||
})
|
||||
.join(" | ")
|
||||
};
|
||||
let pat_and_guard = if let Some(Guard::If(g)) = if_guard {
|
||||
let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
|
||||
format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
|
||||
} else {
|
||||
pat
|
||||
@ -1342,7 +1402,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
|
||||
cx,
|
||||
MATCH_LIKE_MATCHES_MACRO,
|
||||
expr.span,
|
||||
&format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }),
|
||||
&format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
|
||||
"try this",
|
||||
format!(
|
||||
"{}matches!({}, {})",
|
||||
@ -1360,7 +1420,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
|
||||
}
|
||||
|
||||
/// Extract a `bool` or `{ bool }`
|
||||
fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
|
||||
fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
|
||||
match ex {
|
||||
ExprKind::Lit(Spanned {
|
||||
node: LitKind::Bool(b), ..
|
||||
@ -1372,7 +1432,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
|
||||
..
|
||||
},
|
||||
_,
|
||||
) if desugared => {
|
||||
) if is_if_let => {
|
||||
if let ExprKind::Lit(Spanned {
|
||||
node: LitKind::Bool(b), ..
|
||||
}) = exp.kind
|
||||
@ -1644,19 +1704,26 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotat
|
||||
None
|
||||
}
|
||||
|
||||
fn has_only_ref_pats(arms: &[Arm<'_>]) -> bool {
|
||||
let mapped = arms
|
||||
.iter()
|
||||
.map(|a| {
|
||||
match a.pat.kind {
|
||||
fn has_only_ref_pats<'a, 'b, I>(pats: I) -> bool
|
||||
where
|
||||
'b: 'a,
|
||||
I: Iterator<Item = &'a Pat<'b>>,
|
||||
{
|
||||
let mut at_least_one_is_true = false;
|
||||
for opt in pats.map(|pat| match pat.kind {
|
||||
PatKind::Ref(..) => Some(true), // &-patterns
|
||||
PatKind::Wild => Some(false), // an "anything" wildcard is also fine
|
||||
_ => None, // any other pattern is not fine
|
||||
}) {
|
||||
if let Some(inner) = opt {
|
||||
if inner {
|
||||
at_least_one_is_true = true;
|
||||
}
|
||||
})
|
||||
.collect::<Option<Vec<bool>>>();
|
||||
// look for Some(v) where there's at least one true element
|
||||
mapped.map_or(false, |v| v.iter().any(|el| *el))
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
at_least_one_is_true
|
||||
}
|
||||
|
||||
pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
|
||||
@ -1745,6 +1812,7 @@ where
|
||||
mod redundant_pattern_match {
|
||||
use super::REDUNDANT_PATTERN_MATCHING;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
|
||||
use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
|
||||
@ -1755,22 +1823,27 @@ mod redundant_pattern_match {
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
|
||||
use rustc_hir::{
|
||||
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
|
||||
Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath,
|
||||
Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
|
||||
use rustc_span::sym;
|
||||
|
||||
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
|
||||
match match_source {
|
||||
MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
|
||||
MatchSource::IfLetDesugar { contains_else_clause } => {
|
||||
find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause);
|
||||
},
|
||||
MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false),
|
||||
_ => {},
|
||||
if let Some(higher::IfLet {
|
||||
if_else,
|
||||
let_pat,
|
||||
let_expr,
|
||||
..
|
||||
}) = higher::IfLet::ast(cx, expr)
|
||||
{
|
||||
find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some())
|
||||
}
|
||||
if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
|
||||
find_sugg_for_match(cx, expr, op, arms)
|
||||
}
|
||||
if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
|
||||
find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1924,18 +1997,18 @@ mod redundant_pattern_match {
|
||||
fn find_sugg_for_if_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: &'tcx Expr<'tcx>,
|
||||
arm: &Arm<'_>,
|
||||
let_pat: &Pat<'_>,
|
||||
let_expr: &'tcx Expr<'_>,
|
||||
keyword: &'static str,
|
||||
has_else: bool,
|
||||
) {
|
||||
// also look inside refs
|
||||
let mut kind = &arm.pat.kind;
|
||||
let mut kind = &let_pat.kind;
|
||||
// if we have &None for example, peel it so we can detect "if let None = x"
|
||||
if let PatKind::Ref(inner, _mutability) = kind {
|
||||
kind = &inner.kind;
|
||||
}
|
||||
let op_ty = cx.typeck_results().expr_ty(op);
|
||||
let op_ty = cx.typeck_results().expr_ty(let_expr);
|
||||
// Determine which function should be used, and the type contained by the corresponding
|
||||
// variant.
|
||||
let (good_method, inner_ty) = match kind {
|
||||
@ -1989,38 +2062,38 @@ mod redundant_pattern_match {
|
||||
// scrutinee would be, so they have to be considered as well.
|
||||
// e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
|
||||
// for the duration if body.
|
||||
let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, op);
|
||||
let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
|
||||
|
||||
// check that `while_let_on_iterator` lint does not trigger
|
||||
if_chain! {
|
||||
if keyword == "while";
|
||||
if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
|
||||
if let ExprKind::MethodCall(method_path, _, _, _) = let_expr.kind;
|
||||
if method_path.ident.name == sym::next;
|
||||
if is_trait_method(cx, op, sym::Iterator);
|
||||
if is_trait_method(cx, let_expr, sym::Iterator);
|
||||
then {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let result_expr = match &op.kind {
|
||||
let result_expr = match &let_expr.kind {
|
||||
ExprKind::AddrOf(_, _, borrowed) => borrowed,
|
||||
_ => op,
|
||||
_ => let_expr,
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REDUNDANT_PATTERN_MATCHING,
|
||||
arm.pat.span,
|
||||
let_pat.span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|diag| {
|
||||
// while let ... = ... { ... }
|
||||
// if/while let ... = ... { ... }
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
let expr_span = expr.span;
|
||||
|
||||
// while let ... = ... { ... }
|
||||
// if/while let ... = ... { ... }
|
||||
// ^^^
|
||||
let op_span = result_expr.span.source_callsite();
|
||||
|
||||
// while let ... = ... { ... }
|
||||
// if/while let ... = ... { ... }
|
||||
// ^^^^^^^^^^^^^^^^^^^
|
||||
let span = expr_span.until(op_span.shrink_to_hi());
|
||||
|
||||
|
@ -18,7 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
|
||||
// since it is already covered by `&loops::ITER_NEXT_LOOP`
|
||||
let mut parent_expr_opt = get_parent_expr(cx, expr);
|
||||
while let Some(parent_expr) = parent_expr_opt {
|
||||
if higher::for_loop(parent_expr).is_some() {
|
||||
if higher::ForLoop::hir(parent_expr).is_some() {
|
||||
return;
|
||||
}
|
||||
parent_expr_opt = get_parent_expr(cx, parent_expr);
|
||||
@ -29,7 +29,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind;
|
||||
if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
|
||||
= higher::range(index_expr);
|
||||
= higher::Range::hir(index_expr);
|
||||
if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
|
||||
if let ast::LitKind::Int(start_idx, _) = start_lit.node;
|
||||
then {
|
||||
|
@ -53,7 +53,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((_, arg, body, _)) = higher::for_loop(expr) {
|
||||
if let Some(higher::ForLoop { arg, body, .. }) = higher::ForLoop::hir(expr) {
|
||||
// A `for` loop lowers to:
|
||||
// ```rust
|
||||
// match ::std::iter::Iterator::next(&mut iter) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
//! This lint is **warn** by default
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{is_else_clause, is_expn_of};
|
||||
@ -77,10 +78,15 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind {
|
||||
if let Some(higher::If {
|
||||
cond,
|
||||
then,
|
||||
r#else: Some(r#else),
|
||||
}) = higher::If::hir(e)
|
||||
{
|
||||
let reduce = |ret, not| {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability);
|
||||
let snip = Sugg::hir_with_applicability(cx, cond, "<predicate>", &mut applicability);
|
||||
let mut snip = if not { !snip } else { snip };
|
||||
|
||||
if ret {
|
||||
@ -101,8 +107,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
|
||||
applicability,
|
||||
);
|
||||
};
|
||||
if let ExprKind::Block(then_block, _) = then_block.kind {
|
||||
match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
|
||||
if let ExprKind::Block(then, _) = then.kind {
|
||||
match (fetch_bool_block(then), fetch_bool_expr(r#else)) {
|
||||
(RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
|
||||
span_lint(
|
||||
cx,
|
||||
|
@ -1,7 +1,6 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use rustc_ast::ast::{
|
||||
Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat,
|
||||
PatKind,
|
||||
Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, PatKind,
|
||||
};
|
||||
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
|
@ -1,4 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::usage::contains_return_break_continue_macro;
|
||||
@ -6,7 +7,7 @@ use clippy_utils::{eager_or_lazy, in_macro, is_else_clause, is_lang_ctor};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionSome;
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
|
||||
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
@ -84,20 +85,20 @@ struct OptionIfLetElseOccurence {
|
||||
|
||||
/// Extracts the body of a given arm. If the arm contains only an expression,
|
||||
/// then it returns the expression. Otherwise, it returns the entire block
|
||||
fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
|
||||
fn extract_body_from_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
|
||||
if let ExprKind::Block(
|
||||
Block {
|
||||
stmts: statements,
|
||||
expr: Some(expr),
|
||||
stmts: block_stmts,
|
||||
expr: Some(block_expr),
|
||||
..
|
||||
},
|
||||
_,
|
||||
) = &arm.body.kind
|
||||
) = expr.kind
|
||||
{
|
||||
if let [] = statements {
|
||||
Some(expr)
|
||||
if let [] = block_stmts {
|
||||
Some(block_expr)
|
||||
} else {
|
||||
Some(arm.body)
|
||||
Some(expr)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@ -121,37 +122,33 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
|
||||
/// If this expression is the option if let/else construct we're detecting, then
|
||||
/// this function returns an `OptionIfLetElseOccurence` struct with details if
|
||||
/// this construct is found, or None if this construct is not found.
|
||||
fn detect_option_if_let_else<'tcx>(
|
||||
cx: &'_ LateContext<'tcx>,
|
||||
expr: &'_ Expr<'tcx>,
|
||||
) -> Option<OptionIfLetElseOccurence> {
|
||||
fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
|
||||
if_chain! {
|
||||
if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly
|
||||
if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
|
||||
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr);
|
||||
if !is_else_clause(cx.tcx, expr);
|
||||
if arms.len() == 2;
|
||||
if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
|
||||
if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &arms[0].pat.kind;
|
||||
if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
|
||||
if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
|
||||
if is_lang_ctor(cx, struct_qpath, OptionSome);
|
||||
if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
|
||||
if !contains_return_break_continue_macro(arms[0].body);
|
||||
if !contains_return_break_continue_macro(arms[1].body);
|
||||
if !contains_return_break_continue_macro(if_then);
|
||||
if !contains_return_break_continue_macro(if_else);
|
||||
|
||||
then {
|
||||
let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
|
||||
let some_body = extract_body_from_arm(&arms[0])?;
|
||||
let none_body = extract_body_from_arm(&arms[1])?;
|
||||
let some_body = extract_body_from_expr(if_then)?;
|
||||
let none_body = extract_body_from_expr(if_else)?;
|
||||
let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" };
|
||||
let capture_name = id.name.to_ident_string();
|
||||
let (as_ref, as_mut) = match &cond_expr.kind {
|
||||
let (as_ref, as_mut) = match &let_expr.kind {
|
||||
ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
|
||||
ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
|
||||
_ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
|
||||
};
|
||||
let cond_expr = match &cond_expr.kind {
|
||||
let cond_expr = match let_expr.kind {
|
||||
// Pointer dereferencing happens automatically, so we can omit it in the suggestion
|
||||
ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
|
||||
_ => cond_expr,
|
||||
_ => let_expr,
|
||||
};
|
||||
Some(OptionIfLetElseOccurence {
|
||||
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
|
||||
|
@ -104,10 +104,8 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Match(expr, arms, source) = expr.kind {
|
||||
match source {
|
||||
MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => {
|
||||
if let Some(expr_ty) = cx.typeck_results().node_type_opt(expr.hir_id) {
|
||||
if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = expr.kind {
|
||||
if let Some(expr_ty) = cx.typeck_results().node_type_opt(scrutinee.hir_id) {
|
||||
'pattern_checks: for arm in arms {
|
||||
let pat = &arm.pat;
|
||||
if in_external_macro(cx.sess(), pat.span) {
|
||||
@ -118,8 +116,13 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
if let ExprKind::Let(let_pat, let_expr, _) = expr.kind {
|
||||
if let Some(ref expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) {
|
||||
if in_external_macro(cx.sess(), let_pat.span) {
|
||||
return;
|
||||
}
|
||||
apply_lint(cx, let_pat, expr_ty, DerefPossible::Possible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::is_lang_ctor;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
@ -7,7 +8,7 @@ use clippy_utils::{eq_expr_value, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
|
||||
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
@ -50,10 +51,10 @@ impl QuestionMark {
|
||||
/// If it matches, it will suggest to use the question mark operator instead
|
||||
fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::If(if_expr, body, else_) = &expr.kind;
|
||||
if let ExprKind::MethodCall(segment, _, args, _) = &if_expr.kind;
|
||||
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
|
||||
if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind;
|
||||
if segment.ident.name == sym!(is_none);
|
||||
if Self::expression_returns_none(cx, body);
|
||||
if Self::expression_returns_none(cx, then);
|
||||
if let Some(subject) = args.get(0);
|
||||
if Self::is_option(cx, subject);
|
||||
|
||||
@ -61,9 +62,9 @@ impl QuestionMark {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
|
||||
let mut replacement: Option<String> = None;
|
||||
if let Some(else_) = else_ {
|
||||
if let Some(else_inner) = r#else {
|
||||
if_chain! {
|
||||
if let ExprKind::Block(block, None) = &else_.kind;
|
||||
if let ExprKind::Block(block, None) = &else_inner.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(block_expr) = &block.expr;
|
||||
if eq_expr_value(cx, subject, block_expr);
|
||||
@ -96,25 +97,23 @@ impl QuestionMark {
|
||||
|
||||
fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Match(subject, arms, source) = &expr.kind;
|
||||
if *source == MatchSource::IfLetDesugar { contains_else_clause: true };
|
||||
if Self::is_option(cx, subject);
|
||||
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr);
|
||||
if Self::is_option(cx, let_expr);
|
||||
|
||||
if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind;
|
||||
if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
|
||||
if is_lang_ctor(cx, path1, OptionSome);
|
||||
if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
|
||||
let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
|
||||
|
||||
if let ExprKind::Block(block, None) = &arms[0].body.kind;
|
||||
if let ExprKind::Block(ref block, None) = if_then.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(trailing_expr) = &block.expr;
|
||||
if path_to_local_id(trailing_expr, bind_id);
|
||||
|
||||
if let PatKind::Wild = arms[1].pat.kind;
|
||||
if Self::expression_returns_none(cx, arms[1].body);
|
||||
if Self::expression_returns_none(cx, if_else);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let receiver_str = snippet_with_applicability(cx, subject.span, "..", &mut applicability);
|
||||
let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
|
||||
let replacement = format!(
|
||||
"{}{}?",
|
||||
receiver_str,
|
||||
|
@ -329,7 +329,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
|
||||
if let ExprKind::MethodCall(iter_path, _, iter_args, _) = iter.kind;
|
||||
if iter_path.ident.name == sym::iter;
|
||||
// range expression in `.zip()` call: `0..x.len()`
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
|
||||
if is_integer_const(cx, start, 0);
|
||||
// `.len()` call
|
||||
if let ExprKind::MethodCall(len_path, _, len_args, _) = end.kind;
|
||||
@ -337,7 +337,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
|
||||
// `.iter()` and `.len()` called on same `Path`
|
||||
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
|
||||
if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
|
||||
if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
|
||||
then {
|
||||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
@ -356,7 +356,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
start,
|
||||
end: Some(end),
|
||||
limits: RangeLimits::HalfOpen
|
||||
}) = higher::range(expr);
|
||||
}) = higher::Range::hir(expr);
|
||||
if let Some(y) = y_plus_one(cx, end);
|
||||
then {
|
||||
let span = if expr.span.from_expansion() {
|
||||
@ -401,7 +401,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
// inclusive range minus one: `x..=(y-1)`
|
||||
fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(expr);
|
||||
if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(expr);
|
||||
if let Some(y) = y_minus_one(cx, end);
|
||||
then {
|
||||
span_lint_and_then(
|
||||
@ -438,8 +438,8 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let mut cur_expr = expr;
|
||||
while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
|
||||
match higher::for_loop(parent_expr) {
|
||||
Some((_, args, _, _)) if args.hir_id == expr.hir_id => return true,
|
||||
match higher::ForLoop::hir(parent_expr) {
|
||||
Some(higher::ForLoop { arg, .. }) if arg.hir_id == expr.hir_id => return true,
|
||||
_ => cur_expr = parent_expr,
|
||||
}
|
||||
}
|
||||
@ -455,7 +455,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(expr);
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr);
|
||||
let ty = cx.typeck_results().expr_ty(start);
|
||||
if let ty::Int(_) | ty::Uint(_) = ty.kind();
|
||||
if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);
|
||||
|
@ -725,7 +725,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
|
||||
BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => {
|
||||
visit_op(lhs);
|
||||
visit_op(rhs);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -212,14 +212,6 @@ fn check_final_expr<'tcx>(
|
||||
check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block);
|
||||
}
|
||||
},
|
||||
MatchSource::IfLetDesugar {
|
||||
contains_else_clause: true,
|
||||
} => {
|
||||
if let ExprKind::Block(ifblock, _) = arms[0].body.kind {
|
||||
check_block_return(cx, ifblock);
|
||||
}
|
||||
check_final_expr(cx, arms[1].body, None, RetReplacement::Empty);
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
|
||||
|
@ -588,7 +588,7 @@ fn ident_difference_expr_with_base_location(
|
||||
| (ForLoop(_, _, _, _), ForLoop(_, _, _, _))
|
||||
| (While(_, _, _), While(_, _, _))
|
||||
| (If(_, _, _), If(_, _, _))
|
||||
| (Let(_, _), Let(_, _))
|
||||
| (Let(_, _, _), Let(_, _, _))
|
||||
| (Type(_, _), Type(_, _))
|
||||
| (Cast(_, _), Cast(_, _))
|
||||
| (Lit(_), Lit(_))
|
||||
|
@ -67,7 +67,7 @@ impl EarlyLintPass for UnnestedOrPatterns {
|
||||
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
|
||||
if let ast::ExprKind::Let(pat, _) = &e.kind {
|
||||
if let ast::ExprKind::Let(pat, _, _) = &e.kind {
|
||||
lint_unnested_or_patterns(cx, pat);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated};
|
||||
use if_chain::if_chain;
|
||||
@ -160,11 +161,11 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
|
||||
if in_external_macro(self.cx.tcx.sess, expr.span) {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::If(cond, then, els) = &expr.kind {
|
||||
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) {
|
||||
walk_expr(self, cond);
|
||||
self.visit_branch(cond, then, false);
|
||||
if let Some(els) = els {
|
||||
self.visit_branch(cond, els, true);
|
||||
if let Some(else_inner) = r#else {
|
||||
self.visit_branch(cond, else_inner, true);
|
||||
}
|
||||
} else {
|
||||
// find `unwrap[_err]()` calls:
|
||||
|
@ -208,6 +208,15 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
|
||||
print!(" if let ExprKind::");
|
||||
let current = format!("{}.kind", self.current);
|
||||
match expr.kind {
|
||||
ExprKind::Let(pat, expr, _) => {
|
||||
let let_pat = self.next("pat");
|
||||
let let_expr = self.next("expr");
|
||||
println!(" Let(ref {}, ref {}, _) = {};", let_pat, let_expr, current);
|
||||
self.current = let_expr;
|
||||
self.visit_expr(expr);
|
||||
self.current = let_pat;
|
||||
self.visit_pat(pat);
|
||||
},
|
||||
ExprKind::Box(inner) => {
|
||||
let inner_pat = self.next("inner");
|
||||
println!("Box(ref {}) = {};", inner_pat, current);
|
||||
|
@ -66,28 +66,6 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
|
||||
hir::ImplItemKind::TyAlias(_) => println!("associated type"),
|
||||
}
|
||||
}
|
||||
// fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx
|
||||
// hir::TraitItem) {
|
||||
// if !has_attr(&item.attrs) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx
|
||||
// hir::Variant, _:
|
||||
// &hir::Generics) {
|
||||
// if !has_attr(&var.node.attrs) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx
|
||||
// hir::FieldDef) {
|
||||
// if !has_attr(&field.attrs) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) {
|
||||
@ -127,13 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
|
||||
hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0),
|
||||
}
|
||||
}
|
||||
// fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx
|
||||
// hir::ForeignItem) {
|
||||
// if !has_attr(&item.attrs) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
|
||||
@ -171,6 +142,10 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
|
||||
print_expr(cx, arg, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Let(ref pat, ref expr, _) => {
|
||||
print_pat(cx, pat, indent + 1);
|
||||
print_expr(cx, expr, indent + 1);
|
||||
},
|
||||
hir::ExprKind::MethodCall(path, _, args, _) => {
|
||||
println!("{}MethodCall", ind);
|
||||
println!("{}method name: {}", ind, path.ident.name);
|
||||
|
@ -49,8 +49,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
|
||||
if_chain! {
|
||||
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
|
||||
if let ty::Slice(..) = ty.kind();
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
|
||||
if let Some(vec_args) = higher::vec_macro(cx, addressee);
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind;
|
||||
if let Some(vec_args) = higher::VecArgs::hir(cx, addressee);
|
||||
then {
|
||||
self.check_vec_macro(cx, &vec_args, mutability, expr.span);
|
||||
}
|
||||
@ -58,8 +58,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
|
||||
|
||||
// search for `for _ in vec![…]`
|
||||
if_chain! {
|
||||
if let Some((_, arg, _, _)) = higher::for_loop(expr);
|
||||
if let Some(vec_args) = higher::vec_macro(cx, arg);
|
||||
if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr);
|
||||
if let Some(vec_args) = higher::VecArgs::hir(cx, arg);
|
||||
if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg)));
|
||||
then {
|
||||
// report the error around the `vec!` not inside `<std macros>:`
|
||||
|
@ -158,7 +158,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
|
||||
(Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
|
||||
(Lit(l), Lit(r)) => l.kind == r.kind,
|
||||
(Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
|
||||
(Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re),
|
||||
(Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re),
|
||||
(If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
|
||||
(While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
|
||||
(ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
|
||||
|
@ -60,6 +60,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
|
||||
| ExprKind::MethodCall(..)
|
||||
| ExprKind::Binary(..)
|
||||
| ExprKind::Unary(..)
|
||||
| ExprKind::Let(..)
|
||||
| ExprKind::Cast(..)
|
||||
| ExprKind::Type(..)
|
||||
| ExprKind::DropTemps(..)
|
||||
|
@ -1,5 +1,4 @@
|
||||
//! This module contains functions for retrieve the original AST from lowered
|
||||
//! `hir`.
|
||||
//! This module contains functions that retrieves specifiec elements.
|
||||
|
||||
#![deny(clippy::missing_docs_in_private_items)]
|
||||
|
||||
@ -7,10 +6,149 @@ use crate::{is_expn_of, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{self, LitKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
|
||||
use rustc_hir::{Block, BorrowKind, Expr, ExprKind, LoopSource, Node, Pat, StmtKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{sym, ExpnKind, Span, Symbol};
|
||||
|
||||
/// The essential nodes of a desugared for loop as well as the entire span:
|
||||
/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
|
||||
pub struct ForLoop<'tcx> {
|
||||
pub pat: &'tcx hir::Pat<'tcx>,
|
||||
pub arg: &'tcx hir::Expr<'tcx>,
|
||||
pub body: &'tcx hir::Expr<'tcx>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> ForLoop<'tcx> {
|
||||
#[inline]
|
||||
pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
|
||||
if let Some(first_arm) = arms.get(0);
|
||||
if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
|
||||
if let Some(first_arg) = iterargs.get(0);
|
||||
if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none();
|
||||
if let hir::ExprKind::Loop(ref block, ..) = first_arm.body.kind;
|
||||
if block.expr.is_none();
|
||||
if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
|
||||
if let hir::StmtKind::Local(ref local) = let_stmt.kind;
|
||||
if let hir::StmtKind::Expr(ref body_expr) = body.kind;
|
||||
then {
|
||||
return Some(Self {
|
||||
pat: &*local.pat,
|
||||
arg: first_arg,
|
||||
body: body_expr,
|
||||
span: first_arm.span
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct If<'hir> {
|
||||
pub cond: &'hir Expr<'hir>,
|
||||
pub r#else: Option<&'hir Expr<'hir>>,
|
||||
pub then: &'hir Expr<'hir>,
|
||||
}
|
||||
|
||||
impl<'hir> If<'hir> {
|
||||
#[inline]
|
||||
pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
|
||||
if let ExprKind::If(
|
||||
Expr {
|
||||
kind: ExprKind::DropTemps(cond),
|
||||
..
|
||||
},
|
||||
then,
|
||||
r#else,
|
||||
) = expr.kind
|
||||
{
|
||||
Some(Self { cond, r#else, then })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IfLet<'hir> {
|
||||
pub let_pat: &'hir Pat<'hir>,
|
||||
pub let_expr: &'hir Expr<'hir>,
|
||||
pub if_then: &'hir Expr<'hir>,
|
||||
pub if_else: Option<&'hir Expr<'hir>>,
|
||||
}
|
||||
|
||||
impl<'hir> IfLet<'hir> {
|
||||
#[inline]
|
||||
pub fn ast(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option<Self> {
|
||||
let rslt = Self::hir(expr)?;
|
||||
Self::is_not_within_while_context(cx, expr)?;
|
||||
Some(rslt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
|
||||
if let ExprKind::If(
|
||||
Expr {
|
||||
kind: ExprKind::Let(let_pat, let_expr, _),
|
||||
..
|
||||
},
|
||||
if_then,
|
||||
if_else,
|
||||
) = expr.kind
|
||||
{
|
||||
return Some(Self {
|
||||
let_pat,
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else,
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_not_within_while_context(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option<()> {
|
||||
let hir = cx.tcx.hir();
|
||||
let parent = hir.get_parent_node(expr.hir_id);
|
||||
let parent_parent = hir.get_parent_node(parent);
|
||||
let parent_parent_node = hir.get(parent_parent);
|
||||
if let Node::Expr(Expr {
|
||||
kind: ExprKind::Loop(_, _, LoopSource::While, _),
|
||||
..
|
||||
}) = parent_parent_node
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IfOrIfLet<'hir> {
|
||||
pub cond: &'hir Expr<'hir>,
|
||||
pub r#else: Option<&'hir Expr<'hir>>,
|
||||
pub then: &'hir Expr<'hir>,
|
||||
}
|
||||
|
||||
impl<'hir> IfOrIfLet<'hir> {
|
||||
#[inline]
|
||||
pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
|
||||
if let ExprKind::If(cond, then, r#else) = expr.kind {
|
||||
if let ExprKind::DropTemps(new_cond) = cond.kind {
|
||||
return Some(Self {
|
||||
cond: new_cond,
|
||||
r#else,
|
||||
then,
|
||||
});
|
||||
}
|
||||
if let ExprKind::Let(..) = cond.kind {
|
||||
return Some(Self { cond, r#else, then });
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent a range akin to `ast::ExprKind::Range`.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Range<'a> {
|
||||
@ -22,18 +160,18 @@ pub struct Range<'a> {
|
||||
pub limits: ast::RangeLimits,
|
||||
}
|
||||
|
||||
impl<'a> Range<'a> {
|
||||
/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
|
||||
pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
|
||||
pub fn hir(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
|
||||
/// Finds the field named `name` in the field. Always return `Some` for
|
||||
/// convenience.
|
||||
fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> {
|
||||
let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
|
||||
|
||||
Some(expr)
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Call(path, args)
|
||||
hir::ExprKind::Call(ref path, ref args)
|
||||
if matches!(
|
||||
path.kind,
|
||||
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
|
||||
@ -45,7 +183,7 @@ pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
|
||||
limits: ast::RangeLimits::Closed,
|
||||
})
|
||||
},
|
||||
hir::ExprKind::Struct(path, fields, None) => match path {
|
||||
hir::ExprKind::Struct(ref path, ref fields, None) => match path {
|
||||
hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
|
||||
start: None,
|
||||
end: None,
|
||||
@ -76,73 +214,6 @@ pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a `let` statement is from a `for` loop desugaring.
|
||||
pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
|
||||
// This will detect plain for-loops without an actual variable binding:
|
||||
//
|
||||
// ```
|
||||
// for x in some_vec {
|
||||
// // do stuff
|
||||
// }
|
||||
// ```
|
||||
if_chain! {
|
||||
if let Some(expr) = local.init;
|
||||
if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
|
||||
then {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// This detects a variable binding in for loop to avoid `let_unit_value`
|
||||
// lint (see issue #1964).
|
||||
//
|
||||
// ```
|
||||
// for _ in vec![()] {
|
||||
// // anything
|
||||
// }
|
||||
// ```
|
||||
if let hir::LocalSource::ForLoopDesugar = local.source {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Recover the essential nodes of a desugared for loop as well as the entire span:
|
||||
/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
|
||||
pub fn for_loop<'tcx>(
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
|
||||
if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind;
|
||||
if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none();
|
||||
if let hir::ExprKind::Loop(block, ..) = arms[0].body.kind;
|
||||
if block.expr.is_none();
|
||||
if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
|
||||
if let hir::StmtKind::Local(local) = let_stmt.kind;
|
||||
if let hir::StmtKind::Expr(expr) = body.kind;
|
||||
then {
|
||||
return Some((&*local.pat, &iterargs[0], expr, arms[0].span));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Recover the essential nodes of a desugared while loop:
|
||||
/// `while cond { body }` becomes `(cond, body)`.
|
||||
pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Loop(hir::Block { expr: Some(expr), .. }, _, hir::LoopSource::While, _) = &expr.kind;
|
||||
if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
|
||||
if let hir::ExprKind::DropTemps(cond) = &cond.kind;
|
||||
if let [hir::Arm { body, .. }, ..] = &arms[..];
|
||||
then {
|
||||
return Some((cond, body));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Represent the pre-expansion arguments of a `vec!` invocation.
|
||||
@ -153,11 +224,12 @@ pub enum VecArgs<'a> {
|
||||
Vec(&'a [hir::Expr<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> VecArgs<'a> {
|
||||
/// Returns the arguments of the `vec!` macro if this expression was expanded
|
||||
/// from `vec!`.
|
||||
pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option<VecArgs<'e>> {
|
||||
pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<VecArgs<'a>> {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Call(fun, args) = expr.kind;
|
||||
if let hir::ExprKind::Call(ref fun, ref args) = expr.kind;
|
||||
if let hir::ExprKind::Path(ref qpath) = fun.kind;
|
||||
if is_expn_of(fun.span, "vec").is_some();
|
||||
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
|
||||
@ -169,8 +241,8 @@ pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option<Ve
|
||||
else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
|
||||
// `vec![a, b, c]` case
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Box(boxed) = args[0].kind;
|
||||
if let hir::ExprKind::Array(args) = boxed.kind;
|
||||
if let hir::ExprKind::Box(ref boxed) = args[0].kind;
|
||||
if let hir::ExprKind::Array(ref args) = boxed.kind;
|
||||
then {
|
||||
return Some(VecArgs::Vec(&*args));
|
||||
}
|
||||
@ -189,6 +261,121 @@ pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option<Ve
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct While<'hir> {
|
||||
pub if_cond: &'hir Expr<'hir>,
|
||||
pub if_then: &'hir Expr<'hir>,
|
||||
pub if_else: Option<&'hir Expr<'hir>>,
|
||||
}
|
||||
|
||||
impl<'hir> While<'hir> {
|
||||
#[inline]
|
||||
pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
|
||||
if let ExprKind::Loop(
|
||||
Block {
|
||||
expr:
|
||||
Some(Expr {
|
||||
kind:
|
||||
ExprKind::If(
|
||||
Expr {
|
||||
kind: ExprKind::DropTemps(if_cond),
|
||||
..
|
||||
},
|
||||
if_then,
|
||||
if_else_ref,
|
||||
),
|
||||
..
|
||||
}),
|
||||
..
|
||||
},
|
||||
_,
|
||||
LoopSource::While,
|
||||
_,
|
||||
) = expr.kind
|
||||
{
|
||||
let if_else = *if_else_ref;
|
||||
return Some(Self {
|
||||
if_cond,
|
||||
if_then,
|
||||
if_else,
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WhileLet<'hir> {
|
||||
pub if_expr: &'hir Expr<'hir>,
|
||||
pub let_pat: &'hir Pat<'hir>,
|
||||
pub let_expr: &'hir Expr<'hir>,
|
||||
pub if_then: &'hir Expr<'hir>,
|
||||
pub if_else: Option<&'hir Expr<'hir>>,
|
||||
}
|
||||
|
||||
impl<'hir> WhileLet<'hir> {
|
||||
#[inline]
|
||||
pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
|
||||
if let ExprKind::Loop(
|
||||
Block {
|
||||
expr: Some(if_expr), ..
|
||||
},
|
||||
_,
|
||||
LoopSource::While,
|
||||
_,
|
||||
) = expr.kind
|
||||
{
|
||||
if let Expr {
|
||||
kind:
|
||||
ExprKind::If(
|
||||
Expr {
|
||||
kind: ExprKind::Let(let_pat, let_expr, _),
|
||||
..
|
||||
},
|
||||
if_then,
|
||||
if_else_ref,
|
||||
),
|
||||
..
|
||||
} = if_expr
|
||||
{
|
||||
let if_else = *if_else_ref;
|
||||
return Some(Self {
|
||||
if_expr,
|
||||
let_pat,
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a hir binary operator to the corresponding `ast` type.
|
||||
#[must_use]
|
||||
pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
|
||||
match op {
|
||||
hir::BinOpKind::Eq => ast::BinOpKind::Eq,
|
||||
hir::BinOpKind::Ge => ast::BinOpKind::Ge,
|
||||
hir::BinOpKind::Gt => ast::BinOpKind::Gt,
|
||||
hir::BinOpKind::Le => ast::BinOpKind::Le,
|
||||
hir::BinOpKind::Lt => ast::BinOpKind::Lt,
|
||||
hir::BinOpKind::Ne => ast::BinOpKind::Ne,
|
||||
hir::BinOpKind::Or => ast::BinOpKind::Or,
|
||||
hir::BinOpKind::Add => ast::BinOpKind::Add,
|
||||
hir::BinOpKind::And => ast::BinOpKind::And,
|
||||
hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
|
||||
hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
|
||||
hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
|
||||
hir::BinOpKind::Div => ast::BinOpKind::Div,
|
||||
hir::BinOpKind::Mul => ast::BinOpKind::Mul,
|
||||
hir::BinOpKind::Rem => ast::BinOpKind::Rem,
|
||||
hir::BinOpKind::Shl => ast::BinOpKind::Shl,
|
||||
hir::BinOpKind::Shr => ast::BinOpKind::Shr,
|
||||
hir::BinOpKind::Sub => ast::BinOpKind::Sub,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract args from an assert-like macro.
|
||||
/// Currently working with:
|
||||
@ -218,8 +405,8 @@ pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx
|
||||
if let StmtKind::Semi(matchexpr) = block.stmts.get(0)?.kind {
|
||||
// macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
|
||||
if_chain! {
|
||||
if let ExprKind::If(clause, _, _) = matchexpr.kind;
|
||||
if let ExprKind::Unary(UnOp::Not, condition) = clause.kind;
|
||||
if let Some(If { cond, .. }) = If::hir(matchexpr);
|
||||
if let ExprKind::Unary(UnOp::Not, condition) = cond.kind;
|
||||
then {
|
||||
return Some(vec![condition]);
|
||||
}
|
||||
@ -345,3 +532,35 @@ impl FormatArgsExpn<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a `let` statement is from a `for` loop desugaring.
|
||||
pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
|
||||
// This will detect plain for-loops without an actual variable binding:
|
||||
//
|
||||
// ```
|
||||
// for x in some_vec {
|
||||
// // do stuff
|
||||
// }
|
||||
// ```
|
||||
if_chain! {
|
||||
if let Some(ref expr) = local.init;
|
||||
if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
|
||||
then {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// This detects a variable binding in for loop to avoid `let_unit_value`
|
||||
// lint (see issue #1964).
|
||||
//
|
||||
// ```
|
||||
// for _ in vec![()] {
|
||||
// // anything
|
||||
// }
|
||||
// ```
|
||||
if let hir::LocalSource::ForLoopDesugar = local.source {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
@ -232,6 +232,9 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
(&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
|
||||
self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r))
|
||||
},
|
||||
(&ExprKind::Let(ref lp, ref le, _), &ExprKind::Let(ref rp, ref re, _)) => {
|
||||
self.eq_pat(lp, rp) && self.eq_expr(le, re)
|
||||
},
|
||||
(&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
|
||||
(&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
|
||||
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
|
||||
@ -665,6 +668,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Let(ref pat, ref expr, _) => {
|
||||
self.hash_expr(expr);
|
||||
self.hash_pat(pat);
|
||||
},
|
||||
ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {},
|
||||
ExprKind::Lit(ref l) => {
|
||||
l.node.hash(&mut self.s);
|
||||
|
@ -961,17 +961,6 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
|
||||
let map = tcx.hir();
|
||||
let mut iter = map.parent_iter(expr.hir_id);
|
||||
match iter.next() {
|
||||
Some((arm_id, Node::Arm(..))) => matches!(
|
||||
iter.next(),
|
||||
Some((
|
||||
_,
|
||||
Node::Expr(Expr {
|
||||
kind: ExprKind::Match(_, [_, else_arm], MatchSource::IfLetDesugar { .. }),
|
||||
..
|
||||
})
|
||||
))
|
||||
if else_arm.hir_id == arm_id
|
||||
),
|
||||
Some((
|
||||
_,
|
||||
Node::Expr(Expr {
|
||||
@ -1370,15 +1359,15 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
|
||||
let mut conds = Vec::new();
|
||||
let mut blocks: Vec<&Block<'_>> = Vec::new();
|
||||
|
||||
while let ExprKind::If(cond, then_expr, ref else_expr) = expr.kind {
|
||||
conds.push(cond);
|
||||
if let ExprKind::Block(block, _) = then_expr.kind {
|
||||
while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
|
||||
conds.push(&*cond);
|
||||
if let ExprKind::Block(ref block, _) = then.kind {
|
||||
blocks.push(block);
|
||||
} else {
|
||||
panic!("ExprKind::If node is not an ExprKind::Block");
|
||||
}
|
||||
|
||||
if let Some(else_expr) = *else_expr {
|
||||
if let Some(ref else_expr) = r#else {
|
||||
expr = else_expr;
|
||||
} else {
|
||||
break;
|
||||
|
@ -116,7 +116,7 @@ impl<'a> Sugg<'a> {
|
||||
/// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
|
||||
/// function variants of `Sugg`, since these use different snippet functions.
|
||||
fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
|
||||
if let Some(range) = higher::range(expr) {
|
||||
if let Some(range) = higher::Range::hir(expr) {
|
||||
let op = match range.limits {
|
||||
ast::RangeLimits::HalfOpen => AssocOp::DotDot,
|
||||
ast::RangeLimits::Closed => AssocOp::DotDotEq,
|
||||
@ -128,6 +128,7 @@ impl<'a> Sugg<'a> {
|
||||
hir::ExprKind::AddrOf(..)
|
||||
| hir::ExprKind::Box(..)
|
||||
| hir::ExprKind::If(..)
|
||||
| hir::ExprKind::Let(..)
|
||||
| hir::ExprKind::Closure(..)
|
||||
| hir::ExprKind::Unary(..)
|
||||
| hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
|
||||
|
@ -12,7 +12,8 @@ if_chain! {
|
||||
if let ExprKind::Lit(ref lit1) = right.kind;
|
||||
if let LitKind::Int(2, _) = lit1.node;
|
||||
if block.expr.is_none();
|
||||
if let ExprKind::Lit(ref lit2) = cond.kind;
|
||||
if let ExprKind::DropTemps(ref expr) = cond.kind;
|
||||
if let ExprKind::Lit(ref lit2) = expr.kind;
|
||||
if let LitKind::Bool(true) = lit2.node;
|
||||
if let ExprKind::Block(ref block1) = then.kind;
|
||||
if block1.stmts.len() == 1;
|
||||
|
@ -35,7 +35,7 @@ LL | Ok(val) => match val {
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: unnecessary nested match
|
||||
error: unnecessary nested `if let` or `match`
|
||||
--> $DIR/collapsible_match.rs:25:9
|
||||
|
|
||||
LL | / if let Some(n) = val {
|
||||
@ -51,7 +51,7 @@ LL | if let Ok(val) = res_opt {
|
||||
LL | if let Some(n) = val {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: unnecessary nested match
|
||||
error: unnecessary nested `if let` or `match`
|
||||
--> $DIR/collapsible_match.rs:32:9
|
||||
|
|
||||
LL | / if let Some(n) = val {
|
||||
@ -87,7 +87,7 @@ LL | match val {
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: unnecessary nested match
|
||||
error: unnecessary nested `if let` or `match`
|
||||
--> $DIR/collapsible_match.rs:52:13
|
||||
|
|
||||
LL | / if let Some(n) = val {
|
||||
@ -121,7 +121,7 @@ LL | match val {
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: unnecessary nested match
|
||||
error: unnecessary nested `if let` or `match`
|
||||
--> $DIR/collapsible_match.rs:72:13
|
||||
|
|
||||
LL | / if let Some(n) = val {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#![feature(lang_items, start, libc)]
|
||||
#![no_std]
|
||||
#![allow(clippy::if_same_then_else)]
|
||||
#![allow(clippy::redundant_pattern_matching)]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![allow(clippy::blocks_in_if_conditions)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
/// Issue: https://github.com/rust-lang/rust-clippy/issues/2596
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![allow(clippy::blocks_in_if_conditions)]
|
||||
|
||||
fn fn_val(i: i32) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:21:11
|
||||
--> $DIR/infinite_loop.rs:23:11
|
||||
|
|
||||
LL | while y < 10 {
|
||||
| ^^^^^^
|
||||
@ -8,7 +8,7 @@ LL | while y < 10 {
|
||||
= note: this may lead to an infinite or to a never running loop
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:26:11
|
||||
--> $DIR/infinite_loop.rs:28:11
|
||||
|
|
||||
LL | while y < 10 && x < 3 {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 {
|
||||
= note: this may lead to an infinite or to a never running loop
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:33:11
|
||||
--> $DIR/infinite_loop.rs:35:11
|
||||
|
|
||||
LL | while !cond {
|
||||
| ^^^^^
|
||||
@ -24,7 +24,7 @@ LL | while !cond {
|
||||
= note: this may lead to an infinite or to a never running loop
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:77:11
|
||||
--> $DIR/infinite_loop.rs:79:11
|
||||
|
|
||||
LL | while i < 3 {
|
||||
| ^^^^^
|
||||
@ -32,7 +32,7 @@ LL | while i < 3 {
|
||||
= note: this may lead to an infinite or to a never running loop
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:82:11
|
||||
--> $DIR/infinite_loop.rs:84:11
|
||||
|
|
||||
LL | while i < 3 && j > 0 {
|
||||
| ^^^^^^^^^^^^^^
|
||||
@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 {
|
||||
= note: this may lead to an infinite or to a never running loop
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:86:11
|
||||
--> $DIR/infinite_loop.rs:88:11
|
||||
|
|
||||
LL | while i < 3 {
|
||||
| ^^^^^
|
||||
@ -48,7 +48,7 @@ LL | while i < 3 {
|
||||
= note: this may lead to an infinite or to a never running loop
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:101:11
|
||||
--> $DIR/infinite_loop.rs:103:11
|
||||
|
|
||||
LL | while i < 3 {
|
||||
| ^^^^^
|
||||
@ -56,7 +56,7 @@ LL | while i < 3 {
|
||||
= note: this may lead to an infinite or to a never running loop
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:106:11
|
||||
--> $DIR/infinite_loop.rs:108:11
|
||||
|
|
||||
LL | while i < 3 {
|
||||
| ^^^^^
|
||||
@ -64,7 +64,7 @@ LL | while i < 3 {
|
||||
= note: this may lead to an infinite or to a never running loop
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:172:15
|
||||
--> $DIR/infinite_loop.rs:174:15
|
||||
|
|
||||
LL | while self.count < n {
|
||||
| ^^^^^^^^^^^^^^
|
||||
@ -72,7 +72,7 @@ LL | while self.count < n {
|
||||
= note: this may lead to an infinite or to a never running loop
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:180:11
|
||||
--> $DIR/infinite_loop.rs:182:11
|
||||
|
|
||||
LL | while y < 10 {
|
||||
| ^^^^^^
|
||||
@ -82,7 +82,7 @@ LL | while y < 10 {
|
||||
= help: rewrite it as `if cond { loop { } }`
|
||||
|
||||
error: variables in the condition are not mutated in the loop body
|
||||
--> $DIR/infinite_loop.rs:187:11
|
||||
--> $DIR/infinite_loop.rs:189:11
|
||||
|
|
||||
LL | while y < 10 {
|
||||
| ^^^^^^
|
||||
|
@ -2,6 +2,7 @@
|
||||
#![feature(half_open_range_patterns)]
|
||||
#![warn(clippy::match_overlapping_arm)]
|
||||
#![allow(clippy::redundant_pattern_matching)]
|
||||
#![allow(clippy::if_same_then_else)]
|
||||
|
||||
/// Tests for match_overlapping_arm
|
||||
|
||||
|
@ -1,60 +1,60 @@
|
||||
error: some ranges overlap
|
||||
--> $DIR/match_overlapping_arm.rs:12:9
|
||||
--> $DIR/match_overlapping_arm.rs:13:9
|
||||
|
|
||||
LL | 0..=10 => println!("0 ... 10"),
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: `-D clippy::match-overlapping-arm` implied by `-D warnings`
|
||||
note: overlaps with this
|
||||
--> $DIR/match_overlapping_arm.rs:13:9
|
||||
--> $DIR/match_overlapping_arm.rs:14:9
|
||||
|
|
||||
LL | 0..=11 => println!("0 ... 11"),
|
||||
| ^^^^^^
|
||||
|
||||
error: some ranges overlap
|
||||
--> $DIR/match_overlapping_arm.rs:18:9
|
||||
--> $DIR/match_overlapping_arm.rs:19:9
|
||||
|
|
||||
LL | 0..=5 => println!("0 ... 5"),
|
||||
| ^^^^^
|
||||
|
|
||||
note: overlaps with this
|
||||
--> $DIR/match_overlapping_arm.rs:20:9
|
||||
--> $DIR/match_overlapping_arm.rs:21:9
|
||||
|
|
||||
LL | FOO..=11 => println!("0 ... 11"),
|
||||
| ^^^^^^^^
|
||||
|
||||
error: some ranges overlap
|
||||
--> $DIR/match_overlapping_arm.rs:55:9
|
||||
--> $DIR/match_overlapping_arm.rs:56:9
|
||||
|
|
||||
LL | 0..11 => println!("0 .. 11"),
|
||||
| ^^^^^
|
||||
|
|
||||
note: overlaps with this
|
||||
--> $DIR/match_overlapping_arm.rs:56:9
|
||||
--> $DIR/match_overlapping_arm.rs:57:9
|
||||
|
|
||||
LL | 0..=11 => println!("0 ... 11"),
|
||||
| ^^^^^^
|
||||
|
||||
error: some ranges overlap
|
||||
--> $DIR/match_overlapping_arm.rs:80:9
|
||||
--> $DIR/match_overlapping_arm.rs:81:9
|
||||
|
|
||||
LL | 0..=10 => println!("0 ... 10"),
|
||||
| ^^^^^^
|
||||
|
|
||||
note: overlaps with this
|
||||
--> $DIR/match_overlapping_arm.rs:79:9
|
||||
--> $DIR/match_overlapping_arm.rs:80:9
|
||||
|
|
||||
LL | 5..14 => println!("5 .. 14"),
|
||||
| ^^^^^
|
||||
|
||||
error: some ranges overlap
|
||||
--> $DIR/match_overlapping_arm.rs:85:9
|
||||
--> $DIR/match_overlapping_arm.rs:86:9
|
||||
|
|
||||
LL | 0..7 => println!("0 .. 7"),
|
||||
| ^^^^
|
||||
|
|
||||
note: overlaps with this
|
||||
--> $DIR/match_overlapping_arm.rs:86:9
|
||||
--> $DIR/match_overlapping_arm.rs:87:9
|
||||
|
|
||||
LL | 0..=10 => println!("0 ... 10"),
|
||||
| ^^^^^^
|
||||
|
Loading…
Reference in New Issue
Block a user