diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 4bc27fd48..a41166036 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -14,7 +14,6 @@ cargo_metadata = "0.15.3" clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } -if_chain = "1.0" itertools = "0.10.1" quine-mc_cluskey = "0.2" regex-syntax = "0.7" diff --git a/clippy_lints/src/allow_attributes.rs b/clippy_lints/src/allow_attributes.rs index e3f4cf79d..98299e1e4 100644 --- a/clippy_lints/src/allow_attributes.rs +++ b/clippy_lints/src/allow_attributes.rs @@ -52,24 +52,22 @@ declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]); impl LateLintPass<'_> for AllowAttribute { // Separate each crate's features. fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) { - if_chain! { - if !in_external_macro(cx.sess(), attr.span); - if cx.tcx.features().lint_reasons; - if let AttrStyle::Outer = attr.style; - if let Some(ident) = attr.ident(); - if ident.name == rustc_span::symbol::sym::allow; - if !is_from_proc_macro(cx, &attr); - then { - span_lint_and_sugg( - cx, - ALLOW_ATTRIBUTES, - ident.span, - "#[allow] attribute found", - "replace it with", - "expect".into(), - Applicability::MachineApplicable, - ); - } + if !in_external_macro(cx.sess(), attr.span) + && cx.tcx.features().lint_reasons + && let AttrStyle::Outer = attr.style + && let Some(ident) = attr.ident() + && ident.name == rustc_span::symbol::sym::allow + && !is_from_proc_macro(cx, &attr) + { + span_lint_and_sugg( + cx, + ALLOW_ATTRIBUTES, + ident.span, + "#[allow] attribute found", + "replace it with", + "expect".into(), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 64bfa8d90..240ff8597 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -5,7 +5,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug use clippy_utils::is_from_proc_macro; use clippy_utils::macros::{is_panic, macro_backtrace}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use if_chain::if_chain; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::TokenTree; use rustc_ast::{ @@ -471,13 +470,11 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { return; } for item in items { - if_chain! { - if let NestedMetaItem::MetaItem(mi) = &item; - if let MetaItemKind::NameValue(lit) = &mi.kind; - if mi.has_name(sym::since); - then { - check_semver(cx, item.span(), lit); - } + if let NestedMetaItem::MetaItem(mi) = &item + && let MetaItemKind::NameValue(lit) = &mi.kind + && mi.has_name(sym::since) + { + check_semver(cx, item.span(), lit); } } } @@ -580,15 +577,13 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { /// Returns the lint name if it is clippy lint. fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { - if_chain! { - if let Some(meta_item) = lint.meta_item(); - if meta_item.path.segments.len() > 1; - if let tool_name = meta_item.path.segments[0].ident; - if tool_name.name == sym::clippy; - then { - let lint_name = meta_item.path.segments.last().unwrap().ident.name; - return Some(lint_name); - } + if let Some(meta_item) = lint.meta_item() + && meta_item.path.segments.len() > 1 + && let tool_name = meta_item.path.segments[0].ident + && tool_name.name == sym::clippy + { + let lint_name = meta_item.path.segments.last().unwrap().ident.name; + return Some(lint_name); } None } @@ -857,18 +852,17 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It } fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { - if_chain! { - if msrv.meets(msrvs::TOOL_ATTRIBUTES); + if msrv.meets(msrvs::TOOL_ATTRIBUTES) // check cfg_attr - if attr.has_name(sym::cfg_attr); - if let Some(items) = attr.meta_item_list(); - if items.len() == 2; + && attr.has_name(sym::cfg_attr) + && let Some(items) = attr.meta_item_list() + && items.len() == 2 // check for `rustfmt` - if let Some(feature_item) = items[0].meta_item(); - if feature_item.has_name(sym::rustfmt); + && let Some(feature_item) = items[0].meta_item() + && feature_item.has_name(sym::rustfmt) // check for `rustfmt_skip` and `rustfmt::skip` - if let Some(skip_item) = &items[1].meta_item(); - if skip_item.has_name(sym!(rustfmt_skip)) + && let Some(skip_item) = &items[1].meta_item() + && (skip_item.has_name(sym!(rustfmt_skip)) || skip_item .path .segments @@ -876,21 +870,20 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr .expect("empty path in attribute") .ident .name - == sym::skip; + == sym::skip) // Only lint outer attributes, because custom inner attributes are unstable // Tracking issue: https://github.com/rust-lang/rust/issues/54726 - if attr.style == AttrStyle::Outer; - then { - span_lint_and_sugg( - cx, - DEPRECATED_CFG_ATTR, - attr.span, - "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes", - "use", - "#[rustfmt::skip]".to_string(), - Applicability::MachineApplicable, - ); - } + && attr.style == AttrStyle::Outer + { + span_lint_and_sugg( + cx, + DEPRECATED_CFG_ATTR, + attr.span, + "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes", + "use", + "#[rustfmt::skip]".to_string(), + Applicability::MachineApplicable, + ); } } @@ -990,12 +983,10 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { mismatched.extend(find_mismatched_target_os(list)); }, MetaItemKind::Word => { - if_chain! { - if let Some(ident) = meta.ident(); - if let Some(os) = find_os(ident.name.as_str()); - then { - mismatched.push((os, ident.span)); - } + if let Some(ident) = meta.ident() + && let Some(os) = find_os(ident.name.as_str()) + { + mismatched.push((os, ident.span)); } }, MetaItemKind::NameValue(..) => {}, @@ -1006,30 +997,28 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { mismatched } - if_chain! { - if attr.has_name(sym::cfg); - if let Some(list) = attr.meta_item_list(); - let mismatched = find_mismatched_target_os(&list); - if !mismatched.is_empty(); - then { - let mess = "operating system used in target family position"; + if attr.has_name(sym::cfg) + && let Some(list) = attr.meta_item_list() + && let mismatched = find_mismatched_target_os(&list) + && !mismatched.is_empty() + { + let mess = "operating system used in target family position"; - span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { - // Avoid showing the unix suggestion multiple times in case - // we have more than one mismatch for unix-like systems - let mut unix_suggested = false; + span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { + // Avoid showing the unix suggestion multiple times in case + // we have more than one mismatch for unix-like systems + let mut unix_suggested = false; - for (os, span) in mismatched { - let sugg = format!("target_os = \"{os}\""); - diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); + for (os, span) in mismatched { + let sugg = format!("target_os = \"{os}\""); + diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); - if !unix_suggested && is_unix(os) { - diag.help("did you mean `unix`?"); - unix_suggested = true; - } + if !unix_suggested && is_unix(os) { + diag.help("did you mean `unix`?"); + unix_suggested = true; } - }); - } + } + }); } } diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 04bf541a5..28bd3fc70 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -4,7 +4,6 @@ use clippy_utils::ty::implements_trait; use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{get_parent_expr, higher}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -114,15 +113,13 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions { let _: Option = for_each_expr(cond, |e| { if let ExprKind::Closure(closure) = e.kind { // do not lint if the closure is called using an iterator (see #1141) - if_chain! { - if let Some(parent) = get_parent_expr(cx, e); - if let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind; - let caller = cx.typeck_results().expr_ty(self_arg); - if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(cx, caller, iter_id, &[]); - then { - return ControlFlow::Continue(Descend::No); - } + if let Some(parent) = get_parent_expr(cx, e) + && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind + && let caller = cx.typeck_results().expr_ty(self_arg) + && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && implements_trait(cx, caller, iter_id, &[]) + { + return ControlFlow::Continue(Descend::No); } let body = cx.tcx.hir().body(closure.body); diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 37ce65676..66fd64c1c 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::eq_expr_value; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; @@ -152,17 +151,15 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { return Ok(Bool::Term(n as u8)); } - if_chain! { - if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind; - if implements_ord(self.cx, e_lhs); - if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind; - if negate(e_binop.node) == Some(expr_binop.node); - if eq_expr_value(self.cx, e_lhs, expr_lhs); - if eq_expr_value(self.cx, e_rhs, expr_rhs); - then { - #[expect(clippy::cast_possible_truncation)] - return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); - } + if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind + && implements_ord(self.cx, e_lhs) + && let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind + && negate(e_binop.node) == Some(expr_binop.node) + && eq_expr_value(self.cx, e_lhs, expr_lhs) + && eq_expr_value(self.cx, e_rhs, expr_rhs) + { + #[expect(clippy::cast_possible_truncation)] + return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); } } let n = self.terminals.len(); diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index 739ce8f67..789cd3b6c 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -49,69 +49,62 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { - if_chain! { - if !e.span.from_expansion(); - if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind; - if !addrof_target.span.from_expansion(); - if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind; - if !deref_target.span.from_expansion(); - if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) ); - let ref_ty = cx.typeck_results().expr_ty(deref_target); - if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind(); - if !is_from_proc_macro(cx, e); - then{ - - if let Some(parent_expr) = get_parent_expr(cx, e){ - if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) && - !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) { - return; - } - - // modification to `&mut &*x` is different from `&mut x` - if matches!(deref_target.kind, ExprKind::Path(..) - | ExprKind::Field(..) - | ExprKind::Index(..) - | ExprKind::Unary(UnOp::Deref, ..)) - && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) { - return; - } + if !e.span.from_expansion() + && let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind + && !addrof_target.span.from_expansion() + && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind + && !deref_target.span.from_expansion() + && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..)) + && let ref_ty = cx.typeck_results().expr_ty(deref_target) + && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind() + && !is_from_proc_macro(cx, e) + { + if let Some(parent_expr) = get_parent_expr(cx, e) { + if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) + && !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) + { + return; } - span_lint_and_then( - cx, - BORROW_DEREF_REF, - e.span, - "deref on an immutable reference", - |diag| { - diag.span_suggestion( - e.span, - "if you would like to reborrow, try removing `&*`", - snippet_opt(cx, deref_target.span).unwrap(), - Applicability::MachineApplicable - ); - - // has deref trait -> give 2 help - // doesn't have deref trait -> give 1 help - if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){ - if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { - return; - } - } - - diag.span_suggestion( - e.span, - "if you would like to deref, try using `&**`", - format!( - "&**{}", - &snippet_opt(cx, deref_target.span).unwrap(), - ), - Applicability::MaybeIncorrect - ); - - } - ); - + // modification to `&mut &*x` is different from `&mut x` + if matches!( + deref_target.kind, + ExprKind::Path(..) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, ..) + ) && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) + { + return; + } } + + span_lint_and_then( + cx, + BORROW_DEREF_REF, + e.span, + "deref on an immutable reference", + |diag| { + diag.span_suggestion( + e.span, + "if you would like to reborrow, try removing `&*`", + snippet_opt(cx, deref_target.span).unwrap(), + Applicability::MachineApplicable, + ); + + // has deref trait -> give 2 help + // doesn't have deref trait -> give 1 help + if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() { + if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { + return; + } + } + + diag.span_suggestion( + e.span, + "if you would like to deref, try using `&**`", + format!("&**{}", &snippet_opt(cx, deref_target.span).unwrap()), + Applicability::MaybeIncorrect, + ); + }, + ); } } } diff --git a/clippy_lints/src/cargo/multiple_crate_versions.rs b/clippy_lints/src/cargo/multiple_crate_versions.rs index f9b17d45e..473646d00 100644 --- a/clippy_lints/src/cargo/multiple_crate_versions.rs +++ b/clippy_lints/src/cargo/multiple_crate_versions.rs @@ -2,7 +2,6 @@ use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId}; use clippy_utils::diagnostics::span_lint; -use if_chain::if_chain; use itertools::Itertools; use rustc_hir::def_id::LOCAL_CRATE; use rustc_lint::LateContext; @@ -15,31 +14,33 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { let mut packages = metadata.packages.clone(); packages.sort_by(|a, b| a.name.cmp(&b.name)); - if_chain! { - if let Some(resolve) = &metadata.resolve; - if let Some(local_id) = packages - .iter() - .find_map(|p| if p.name == local_name.as_str() { Some(&p.id) } else { None }); - then { - for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { - let group: Vec<&Package> = group.collect(); + if let Some(resolve) = &metadata.resolve + && let Some(local_id) = packages.iter().find_map(|p| { + if p.name == local_name.as_str() { + Some(&p.id) + } else { + None + } + }) + { + for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { + let group: Vec<&Package> = group.collect(); - if group.len() <= 1 { - continue; - } + if group.len() <= 1 { + continue; + } - if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) { - let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect(); - versions.sort(); - let versions = versions.iter().join(", "); + if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) { + let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect(); + versions.sort(); + let versions = versions.iter().join(", "); - span_lint( - cx, - MULTIPLE_CRATE_VERSIONS, - DUMMY_SP, - &format!("multiple versions for dependency `{name}`: {versions}"), - ); - } + span_lint( + cx, + MULTIPLE_CRATE_VERSIONS, + DUMMY_SP, + &format!("multiple versions for dependency `{name}`: {versions}"), + ); } } } diff --git a/clippy_lints/src/cargo/wildcard_dependencies.rs b/clippy_lints/src/cargo/wildcard_dependencies.rs index 7fa6acbf5..3b7546dfd 100644 --- a/clippy_lints/src/cargo/wildcard_dependencies.rs +++ b/clippy_lints/src/cargo/wildcard_dependencies.rs @@ -1,6 +1,5 @@ use cargo_metadata::Metadata; use clippy_utils::diagnostics::span_lint; -use if_chain::if_chain; use rustc_lint::LateContext; use rustc_span::source_map::DUMMY_SP; @@ -9,19 +8,17 @@ use super::WILDCARD_DEPENDENCIES; pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { for dep in &metadata.packages[0].dependencies { // VersionReq::any() does not work - if_chain! { - if let Ok(wildcard_ver) = semver::VersionReq::parse("*"); - if let Some(ref source) = dep.source; - if !source.starts_with("git"); - if dep.req == wildcard_ver; - then { - span_lint( - cx, - WILDCARD_DEPENDENCIES, - DUMMY_SP, - &format!("wildcard dependency for `{}`", dep.name), - ); - } + if let Ok(wildcard_ver) = semver::VersionReq::parse("*") + && let Some(ref source) = dep.source + && !source.starts_with("git") + && dep.req == wildcard_ver + { + span_lint( + cx, + WILDCARD_DEPENDENCIES, + DUMMY_SP, + &format!("wildcard dependency for `{}`", dep.name), + ); } } } diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index a83dfd94d..bd12ee406 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -1,7 +1,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{method_chain_args, sext}; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -28,13 +27,11 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast // Don't lint for positive constants. let const_val = constant(cx, cx.typeck_results(), cast_op); - if_chain! { - if let Some(Constant::Int(n)) = const_val; - if let ty::Int(ity) = *cast_from.kind(); - if sext(cx.tcx, n, ity) >= 0; - then { - return false; - } + if let Some(Constant::Int(n)) = const_val + && let ty::Int(ity) = *cast_from.kind() + && sext(cx.tcx, n, ity) >= 0 + { + return false; } // Don't lint for the result of methods that always return non-negative values. @@ -42,13 +39,11 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast let mut method_name = path.ident.name.as_str(); let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; - if_chain! { - if method_name == "unwrap"; - if let Some(arglist) = method_chain_args(cast_op, &["unwrap"]); - if let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind; - then { - method_name = inner_path.ident.name.as_str(); - } + if method_name == "unwrap" + && let Some(arglist) = method_chain_args(cast_op, &["unwrap"]) + && let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind + { + method_name = inner_path.ident.name.as_str(); } if allowed_methods.iter().any(|&name| method_name == name) { diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index d14104029..2a9f7fec1 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,7 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source; -use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; @@ -69,26 +68,24 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let map = cx.tcx.hir(); - if_chain! { - if let Some(parent_id) = map.opt_parent_id(expr.hir_id); - if let Some(parent) = map.find(parent_id); - then { - let expr = match parent { - Node::Block(block) => { - if let Some(parent_expr) = block.expr { - parent_expr - } else { - return false; - } - }, - Node::Expr(expr) => expr, - _ => return false, - }; + if let Some(parent_id) = map.opt_parent_id(expr.hir_id) + && let Some(parent) = map.find(parent_id) + { + let expr = match parent { + Node::Block(block) => { + if let Some(parent_expr) = block.expr { + parent_expr + } else { + return false; + } + }, + Node::Expr(expr) => expr, + _ => return false, + }; - matches!(expr.kind, ExprKind::Cast(..)) - } else { - false - } + matches!(expr.kind, ExprKind::Cast(..)) + } else { + false } } diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index badadf2c9..3db1e3e6d 100644 --- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,7 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; @@ -25,34 +24,32 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option { } pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) { - if_chain! { - if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS); - if let ty::RawPtr(ptrty) = cast_to.kind(); - if let ty::Slice(_) = ptrty.ty.kind(); - if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind; - if let ExprKind::Path(ref qpath) = fun.kind; - if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); - if let Some(rpk) = raw_parts_kind(cx, fun_def_id); - let ctxt = expr.span.ctxt(); - if cast_expr.span.ctxt() == ctxt; - then { - let func = match rpk { - RawPartsKind::Immutable => "from_raw_parts", - RawPartsKind::Mutable => "from_raw_parts_mut" - }; - let span = expr.span; - let mut applicability = Applicability::MachineApplicable; - let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; - let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; - span_lint_and_sugg( - cx, - CAST_SLICE_FROM_RAW_PARTS, - span, - &format!("casting the result of `{func}` to {cast_to}"), - "replace with", - format!("core::ptr::slice_{func}({ptr}, {len})"), - applicability - ); - } + if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) + && let ty::RawPtr(ptrty) = cast_to.kind() + && let ty::Slice(_) = ptrty.ty.kind() + && let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind + && let ExprKind::Path(ref qpath) = fun.kind + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let Some(rpk) = raw_parts_kind(cx, fun_def_id) + && let ctxt = expr.span.ctxt() + && cast_expr.span.ctxt() == ctxt + { + let func = match rpk { + RawPartsKind::Immutable => "from_raw_parts", + RawPartsKind::Mutable => "from_raw_parts_mut", + }; + let span = expr.span; + let mut applicability = Applicability::MachineApplicable; + let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; + let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; + span_lint_and_sugg( + cx, + CAST_SLICE_FROM_RAW_PARTS, + span, + &format!("casting the result of `{func}` to {cast_to}"), + "replace with", + format!("core::ptr::slice_{func}({ptr}, {len})"), + applicability, + ); } } diff --git a/clippy_lints/src/casts/char_lit_as_u8.rs b/clippy_lints/src/casts/char_lit_as_u8.rs index 82e07c98a..a7d3868f7 100644 --- a/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/clippy_lints/src/casts/char_lit_as_u8.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -10,32 +9,31 @@ use rustc_middle::ty::{self, UintTy}; use super::CHAR_LIT_AS_U8; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Cast(e, _) = &expr.kind; - if let ExprKind::Lit(l) = &e.kind; - if let LitKind::Char(c) = l.node; - if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind(); - then { - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); + if let ExprKind::Cast(e, _) = &expr.kind + && let ExprKind::Lit(l) = &e.kind + && let LitKind::Char(c) = l.node + && ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind() + { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); - span_lint_and_then( - cx, - CHAR_LIT_AS_U8, - expr.span, - "casting a character literal to `u8` truncates", - |diag| { - diag.note("`char` is four bytes wide, but `u8` is a single byte"); + span_lint_and_then( + cx, + CHAR_LIT_AS_U8, + expr.span, + "casting a character literal to `u8` truncates", + |diag| { + diag.note("`char` is four bytes wide, but `u8` is a single byte"); - if c.is_ascii() { - diag.span_suggestion( - expr.span, - "use a byte literal instead", - format!("b{snippet}"), - applicability, - ); - } - }); - } + if c.is_ascii() { + diag.span_suggestion( + expr.span, + "use a byte literal instead", + format!("b{snippet}"), + applicability, + ); + } + }, + ); } } diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 0172e9336..ff069860a 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,7 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -17,29 +16,35 @@ pub(super) fn check<'tcx>( cast_to: Ty<'tcx>, msrv: &Msrv, ) { - if_chain! { - if msrv.meets(msrvs::POINTER_CAST_CONSTNESS); - if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, ty: from_ty }) = cast_from.kind(); - if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, ty: to_ty }) = cast_to.kind(); - if matches!((from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)); - if from_ty == to_ty; - then { - let sugg = Sugg::hir(cx, cast_expr, "_"); - let constness = match *to_mutbl { - Mutability::Not => "const", - Mutability::Mut => "mut", - }; + if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) + && let ty::RawPtr(TypeAndMut { + mutbl: from_mutbl, + ty: from_ty, + }) = cast_from.kind() + && let ty::RawPtr(TypeAndMut { + mutbl: to_mutbl, + ty: to_ty, + }) = cast_to.kind() + && matches!( + (from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not) + ) + && from_ty == to_ty + { + let sugg = Sugg::hir(cx, cast_expr, "_"); + let constness = match *to_mutbl { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; - span_lint_and_sugg( - cx, - PTR_CAST_CONSTNESS, - expr.span, - "`as` casting between raw pointers while changing only its constness", - &format!("try `pointer::cast_{constness}`, a safer alternative"), - format!("{}.cast_{constness}()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); - } + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "`as` casting between raw pointers while changing only its constness", + &format!("try `pointer::cast_{constness}`, a safer alternative"), + format!("{}.cast_{constness}()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 61bfce07e..849920bb7 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -3,7 +3,6 @@ use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet_opt; use clippy_utils::visitors::{for_each_expr, Visitable}; use clippy_utils::{get_parent_expr, get_parent_node, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; -use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -25,40 +24,40 @@ pub(super) fn check<'tcx>( ) -> bool { let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); - if_chain! { - if let ty::RawPtr(..) = cast_from.kind(); + if let ty::RawPtr(..) = cast_from.kind() // check both mutability and type are the same - if cast_from.kind() == cast_to.kind(); - if let ExprKind::Cast(_, cast_to_hir) = expr.kind; + && cast_from.kind() == cast_to.kind() + && let ExprKind::Cast(_, cast_to_hir) = expr.kind // Ignore casts to e.g. type aliases and infer types // - p as pointer_alias // - p as _ - if let TyKind::Ptr(to_pointee) = cast_to_hir.kind; - then { - match to_pointee.ty.kind { - // Ignore casts to pointers that are aliases or cfg dependant, e.g. - // - p as *const std::ffi::c_char (alias) - // - p as *const std::os::raw::c_char (cfg dependant) - TyKind::Path(qpath) => { - if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) { - return false; - } - }, - // Ignore `p as *const _` - TyKind::Infer => return false, - _ => {}, - } - - span_lint_and_sugg( - cx, - UNNECESSARY_CAST, - expr.span, - &format!("casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"), - "try", - cast_str.clone(), - Applicability::MaybeIncorrect, - ); + && let TyKind::Ptr(to_pointee) = cast_to_hir.kind + { + match to_pointee.ty.kind { + // Ignore casts to pointers that are aliases or cfg dependant, e.g. + // - p as *const std::ffi::c_char (alias) + // - p as *const std::os::raw::c_char (cfg dependant) + TyKind::Path(qpath) => { + if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) { + return false; + } + }, + // Ignore `p as *const _` + TyKind::Infer => return false, + _ => {}, } + + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!( + "casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)" + ), + "try", + cast_str.clone(), + Applicability::MaybeIncorrect, + ); } // skip cast of local that is a type alias @@ -86,14 +85,12 @@ pub(super) fn check<'tcx>( } // skip cast to non-primitive type - if_chain! { - if let ExprKind::Cast(_, cast_to) = expr.kind; - if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind; - if let Res::PrimTy(_) = path.res; - then {} - else { - return false; - } + if let ExprKind::Cast(_, cast_to) = expr.kind + && let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind + && let Res::PrimTy(_) = path.res + { + } else { + return false; } // skip cast of fn call that returns type alias @@ -106,18 +103,19 @@ pub(super) fn check<'tcx>( if let Some(lit) = get_numeric_literal(cast_expr) { let literal_str = &cast_str; - if_chain! { - if let LitKind::Int(n, _) = lit.node; - if let Some(src) = snippet_opt(cx, cast_expr.span); - if cast_to.is_floating_point(); - if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node); - let from_nbits = 128 - n.leading_zeros(); - let to_nbits = fp_ty_mantissa_nbits(cast_to); - if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); - then { - lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); - return true - } + if let LitKind::Int(n, _) = lit.node + && let Some(src) = snippet_opt(cx, cast_expr.span) + && cast_to.is_floating_point() + && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) + && let from_nbits = 128 - n.leading_zeros() + && let to_nbits = fp_ty_mantissa_nbits(cast_to) + && from_nbits != 0 + && to_nbits != 0 + && from_nbits <= to_nbits + && num_lit.is_decimal() + { + lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + return true; } match lit.node { diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index d31c2268a..69fa0821e 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -4,7 +4,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -55,20 +54,17 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { return; } - let result = if_chain! { - if !in_constant(cx, item.hir_id); - if !in_external_macro(cx.sess(), item.span); - if let ExprKind::Binary(op, left, right) = &item.kind; - - then { - match op.node { - BinOpKind::Ge | BinOpKind::Le => single_check(item), - BinOpKind::And => double_check(cx, left, right), - _ => None, - } - } else { - None + let result = if !in_constant(cx, item.hir_id) + && !in_external_macro(cx.sess(), item.span) + && let ExprKind::Binary(op, left, right) = &item.kind + { + match op.node { + BinOpKind::Ge | BinOpKind::Le => single_check(item), + BinOpKind::And => double_check(cx, left, right), + _ => None, } + } else { + None }; if let Some(cv) = result { @@ -193,16 +189,13 @@ impl ConversionType { /// Check for `expr <= (to_type::MAX as from_type)` fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { - if_chain! { - if let ExprKind::Binary(ref op, left, right) = &expr.kind; - if let Some((candidate, check)) = normalize_le_ge(op, left, right); - if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX"); - - then { - Conversion::try_new(candidate, from, to) - } else { - None - } + if let ExprKind::Binary(ref op, left, right) = &expr.kind + && let Some((candidate, check)) = normalize_le_ge(op, left, right) + && let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX") + { + Conversion::try_new(candidate, from, to) + } else { + None } } @@ -243,33 +236,27 @@ fn get_types_from_cast<'a>( ) -> Option<(&'a str, &'a str)> { // `to_type::max_value() as from_type` // or `to_type::MAX as from_type` - let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { + let call_from_cast: Option<(&Expr<'_>, &str)> = if let ExprKind::Cast(limit, from_type) = &expr.kind // to_type::max_value(), from_type - if let ExprKind::Cast(limit, from_type) = &expr.kind; - if let TyKind::Path(ref from_type_path) = &from_type.kind; - if let Some(from_sym) = int_ty_to_sym(from_type_path); - - then { - Some((limit, from_sym)) - } else { - None - } + && let TyKind::Path(ref from_type_path) = &from_type.kind + && let Some(from_sym) = int_ty_to_sym(from_type_path) + { + Some((limit, from_sym)) + } else { + None }; // `from_type::from(to_type::max_value())` let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { - if_chain! { + if let ExprKind::Call(from_func, [limit]) = &expr.kind // `from_type::from, to_type::max_value()` - if let ExprKind::Call(from_func, [limit]) = &expr.kind; // `from_type::from` - if let ExprKind::Path(ref path) = &from_func.kind; - if let Some(from_sym) = get_implementing_type(path, INTS, "from"); - - then { - Some((limit, from_sym)) - } else { - None - } + && let ExprKind::Path(ref path) = &from_func.kind + && let Some(from_sym) = get_implementing_type(path, INTS, "from") + { + Some((limit, from_sym)) + } else { + None } }); @@ -298,31 +285,27 @@ fn get_types_from_cast<'a>( /// Gets the type which implements the called function fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> { - if_chain! { - if let QPath::TypeRelative(ty, path) = &path; - if path.ident.name.as_str() == function; - if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind; - if let [int] = tp.segments; - then { - let name = int.ident.name.as_str(); - candidates.iter().find(|c| &name == *c).copied() - } else { - None - } + if let QPath::TypeRelative(ty, path) = &path + && path.ident.name.as_str() == function + && let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind + && let [int] = tp.segments + { + let name = int.ident.name.as_str(); + candidates.iter().find(|c| &name == *c).copied() + } else { + None } } /// Gets the type as a string, if it is a supported integer fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { - if_chain! { - if let QPath::Resolved(_, path) = *path; - if let [ty] = path.segments; - then { - let name = ty.ident.name.as_str(); - INTS.iter().find(|c| &name == *c).copied() - } else { - None - } + if let QPath::Resolved(_, path) = *path + && let [ty] = path.segments + { + let name = ty.ident.name.as_str(); + INTS.iter().find(|c| &name == *c).copied() + } else { + None } } diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index d21ef195d..e5aaf88ab 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -15,7 +15,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -121,49 +120,55 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { } fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { - if_chain! { - if let ast::ExprKind::Block(ref block, _) = else_.kind; - if !block_starts_with_comment(cx, block); - if let Some(else_) = expr_block(block); - if else_.attrs.is_empty(); - if !else_.span.from_expansion(); - if let ast::ExprKind::If(..) = else_.kind; - then { - // Prevent "elseif" - // Check that the "else" is followed by whitespace - let up_to_else = then_span.between(block.span); - let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false }; + if let ast::ExprKind::Block(ref block, _) = else_.kind + && !block_starts_with_comment(cx, block) + && let Some(else_) = expr_block(block) + && else_.attrs.is_empty() + && !else_.span.from_expansion() + && let ast::ExprKind::If(..) = else_.kind + { + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let up_to_else = then_span.between(block.span); + let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { + !c.is_whitespace() + } else { + false + }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - COLLAPSIBLE_ELSE_IF, - block.span, - "this `else { if .. }` block can be collapsed", - "collapse nested if block", - format!( - "{}{}", - if requires_space { " " } else { "" }, - snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) - ), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COLLAPSIBLE_ELSE_IF, + block.span, + "this `else { if .. }` block can be collapsed", + "collapse nested if block", + format!( + "{}{}", + if requires_space { " " } else { "" }, + snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) + ), + applicability, + ); } } fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) { - if_chain! { - if !block_starts_with_comment(cx, then); - if let Some(inner) = expr_block(then); - if inner.attrs.is_empty(); - if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind; + if !block_starts_with_comment(cx, then) + && let Some(inner) = expr_block(then) + && inner.attrs.is_empty() + && let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind // Prevent triggering on `if c { if let a = b { .. } }`. - if !matches!(check_inner.kind, ast::ExprKind::Let(..)); - let ctxt = expr.span.ctxt(); - if inner.span.ctxt() == ctxt; - then { - span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| { + && !matches!(check_inner.kind, ast::ExprKind::Let(..)) + && let ctxt = expr.span.ctxt() + && inner.span.ctxt() == ctxt + { + span_lint_and_then( + cx, + COLLAPSIBLE_IF, + expr.span, + "this `if` statement can be collapsed", + |diag| { let mut app = Applicability::MachineApplicable; let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app); let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app); @@ -177,8 +182,8 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & ), app, // snippet ); - }); - } + }, + ); } } diff --git a/clippy_lints/src/copy_iterator.rs b/clippy_lints/src/copy_iterator.rs index 5d04ad011..db850edd6 100644 --- a/clippy_lints/src/copy_iterator.rs +++ b/clippy_lints/src/copy_iterator.rs @@ -5,8 +5,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use if_chain::if_chain; - declare_clippy_lint! { /// ### What it does /// Checks for types that implement `Copy` as well as @@ -38,25 +36,23 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]); impl<'tcx> LateLintPass<'tcx> for CopyIterator { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if_chain! { - if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), - .. - }) = item.kind; - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - if is_copy(cx, ty); - if let Some(trait_id) = trait_ref.trait_def_id(); - if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id); - then { - span_lint_and_note( - cx, - COPY_ITERATOR, - item.span, - "you are implementing `Iterator` on a `Copy` type", - None, - "consider implementing `IntoIterator` instead", - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + .. + }) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && is_copy(cx, ty) + && let Some(trait_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Iterator, trait_id) + { + span_lint_and_note( + cx, + COPY_ITERATOR, + item.span, + "you are implementing `Iterator` on a `Copy` type", + None, + "consider implementing `IntoIterator` instead", + ); } } } diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index a2005638d..637d5aae1 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -53,35 +53,31 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if_chain! { - if item.attrs.iter().any(is_macro_export); - if let ItemKind::MacroDef(macro_def) = &item.kind; - let tts = macro_def.body.tokens.clone(); - if let Some(span) = contains_unhygienic_crate_reference(&tts); - then { - span_lint_and_sugg( - cx, - CRATE_IN_MACRO_DEF, - span, - "`crate` references the macro call's crate", - "to reference the macro definition's crate, use", - String::from("$crate"), - Applicability::MachineApplicable, - ); - } + if item.attrs.iter().any(is_macro_export) + && let ItemKind::MacroDef(macro_def) = &item.kind + && let tts = macro_def.body.tokens.clone() + && let Some(span) = contains_unhygienic_crate_reference(&tts) + { + span_lint_and_sugg( + cx, + CRATE_IN_MACRO_DEF, + span, + "`crate` references the macro call's crate", + "to reference the macro definition's crate, use", + String::from("$crate"), + Applicability::MachineApplicable, + ); } } } fn is_macro_export(attr: &Attribute) -> bool { - if_chain! { - if let AttrKind::Normal(normal) = &attr.kind; - if let [segment] = normal.item.path.segments.as_slice(); - then { - segment.ident.name == sym::macro_export - } else { - false - } + if let AttrKind::Normal(normal) = &attr.kind + && let [segment] = normal.item.path.segments.as_slice() + { + segment.ident.name == sym::macro_export + } else { + false } } @@ -89,14 +85,12 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { let mut prev_is_dollar = false; let mut cursor = tts.trees(); while let Some(curr) = cursor.next() { - if_chain! { - if !prev_is_dollar; - if let Some(span) = is_crate_keyword(curr); - if let Some(next) = cursor.look_ahead(0); - if is_token(next, &TokenKind::ModSep); - then { - return Some(span); - } + if !prev_is_dollar + && let Some(span) = is_crate_keyword(curr) + && let Some(next) = cursor.look_ahead(0) + && is_token(next, &TokenKind::ModSep) + { + return Some(span); } if let TokenTree::Delimited(_, _, tts) = &curr { let span = contains_unhygienic_crate_reference(tts); @@ -110,10 +104,18 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { } fn is_crate_keyword(tt: &TokenTree) -> Option { - if_chain! { - if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }, _) = tt; - if symbol.as_str() == "crate"; - then { Some(*span) } else { None } + if let TokenTree::Token( + Token { + kind: TokenKind::Ident(symbol, _), + span, + }, + _, + ) = tt + && symbol.as_str() == "crate" + { + Some(*span) + } else { + None } } diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 2bca695c4..97b736dfd 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -33,22 +32,20 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]); impl LateLintPass<'_> for CreateDir { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, [arg, ..]) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id); - then { - span_lint_and_sugg( - cx, - CREATE_DIR, - expr.span, - "calling `std::fs::create_dir` where there may be a better way", - "consider calling `std::fs::create_dir_all` instead", - format!("create_dir_all({})", snippet(cx, arg.span, "..")), - Applicability::MaybeIncorrect, - ) - } + if let ExprKind::Call(func, [arg, ..]) = expr.kind + && let ExprKind::Path(ref path) = func.kind + && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id) + { + span_lint_and_sugg( + cx, + CREATE_DIR, + expr.span, + "calling `std::fs::create_dir` where there may be a better way", + "consider calling `std::fs::create_dir_all` instead", + format!("create_dir_all({})", snippet(cx, arg.span, "..")), + Applicability::MaybeIncorrect, + ); } } } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 1a646ba38..04dcccede 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -10,8 +10,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ #[cfg(feature = "internal")] crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO, #[cfg(feature = "internal")] - crate::utils::internal_lints::if_chain_style::IF_CHAIN_STYLE_INFO, - #[cfg(feature = "internal")] crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO, diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index c74b2b883..b325449c5 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_drop, is_copy}; use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -81,33 +80,31 @@ impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); + if !expr.span.from_expansion() // Avoid cases already linted by `field_reassign_with_default` - if !self.reassigned_linted.contains(&expr.span); - if let ExprKind::Call(path, ..) = expr.kind; - if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); - if let ExprKind::Path(ref qpath) = path.kind; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::default_fn, def_id); - if !is_update_syntax_base(cx, expr); + && !self.reassigned_linted.contains(&expr.span) + && let ExprKind::Call(path, ..) = expr.kind + && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::default_fn, def_id) + && !is_update_syntax_base(cx, expr) // Detect and ignore ::default() because these calls do explicitly name the type. - if let QPath::Resolved(None, _path) = qpath; - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind(); - if !is_from_proc_macro(cx, expr); - then { - let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did()))); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{replacement}` is more clear than this expression"), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } + && let QPath::Resolved(None, _path) = qpath + && let expr_ty = cx.typeck_results().expr_ty(expr) + && let ty::Adt(def, ..) = expr_ty.kind() + && !is_from_proc_macro(cx, expr) + { + let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did()))); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{replacement}` is more clear than this expression"), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); } } @@ -124,38 +121,36 @@ impl<'tcx> LateLintPass<'tcx> for Default { // find all binding statements like `let mut _ = T::default()` where `T::default()` is the // `default` method of the `Default` trait, and store statement index in current block being // checked and the name of the bound variable - let (local, variant, binding_name, binding_type, span) = if_chain! { + let (local, variant, binding_name, binding_type, span) = if let StmtKind::Local(local) = stmt.kind // only take `let ...` statements - if let StmtKind::Local(local) = stmt.kind; - if let Some(expr) = local.init; - if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); - if !expr.span.from_expansion(); + && let Some(expr) = local.init + && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + && !expr.span.from_expansion() // only take bindings to identifiers - if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; + && let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind // only when assigning `... = Default::default()` - if is_expr_default(expr, cx); - let binding_type = cx.typeck_results().node_type(binding_id); - if let Some(adt) = binding_type.ty_adt_def(); - if adt.is_struct(); - let variant = adt.non_enum_variant(); - if adt.did().is_local() || !variant.is_field_list_non_exhaustive(); - let module_did = cx.tcx.parent_module(stmt.hir_id); - if variant + && is_expr_default(expr, cx) + && let binding_type = cx.typeck_results().node_type(binding_id) + && let Some(adt) = binding_type.ty_adt_def() + && adt.is_struct() + && let variant = adt.non_enum_variant() + && (adt.did().is_local() || !variant.is_field_list_non_exhaustive()) + && let module_did = cx.tcx.parent_module(stmt.hir_id) + && variant .fields .iter() - .all(|field| field.vis.is_accessible_from(module_did, cx.tcx)); - let all_fields_are_copy = variant + .all(|field| field.vis.is_accessible_from(module_did, cx.tcx)) + && let all_fields_are_copy = variant .fields .iter() .all(|field| { is_copy(cx, cx.tcx.type_of(field.did).instantiate_identity()) - }); - if !has_drop(cx, binding_type) || all_fields_are_copy; - then { - (local, variant, ident.name, binding_type, expr.span) - } else { - continue; - } + }) + && (!has_drop(cx, binding_type) || all_fields_are_copy) + { + (local, variant, ident.name, binding_type, expr.span) + } else { + continue; }; let init_ctxt = local.span.ctxt(); @@ -216,21 +211,19 @@ impl<'tcx> LateLintPass<'tcx> for Default { .join(", "); // give correct suggestion if generics are involved (see #6944) - let binding_type = if_chain! { - if let ty::Adt(adt_def, args) = binding_type.kind(); - if !args.is_empty(); - then { - let adt_def_ty_name = cx.tcx.item_name(adt_def.did()); - let generic_args = args.iter().collect::>(); - let tys_str = generic_args - .iter() - .map(ToString::to_string) - .collect::>() - .join(", "); - format!("{adt_def_ty_name}::<{}>", &tys_str) - } else { - binding_type.to_string() - } + let binding_type = if let ty::Adt(adt_def, args) = binding_type.kind() + && !args.is_empty() + { + let adt_def_ty_name = cx.tcx.item_name(adt_def.did()); + let generic_args = args.iter().collect::>(); + let tys_str = generic_args + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "); + format!("{adt_def_ty_name}::<{}>", &tys_str) + } else { + binding_type.to_string() }; let sugg = if ext_with_default { @@ -260,48 +253,42 @@ impl<'tcx> LateLintPass<'tcx> for Default { /// Checks if the given expression is the `default` method belonging to the `Default` trait. fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { - if_chain! { - if let ExprKind::Call(fn_expr, _) = &expr.kind; - if let ExprKind::Path(qpath) = &fn_expr.kind; - if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); - then { - // right hand side of assignment is `Default::default` - cx.tcx.is_diagnostic_item(sym::default_fn, def_id) - } else { - false - } + if let ExprKind::Call(fn_expr, _) = &expr.kind + && let ExprKind::Path(qpath) = &fn_expr.kind + && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id) + { + // right hand side of assignment is `Default::default` + cx.tcx.is_diagnostic_item(sym::default_fn, def_id) + } else { + false } } /// Returns the reassigned field and the assigning expression (right-hand side of assign). fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { - if_chain! { + if let StmtKind::Semi(later_expr) = this.kind // only take assignments - if let StmtKind::Semi(later_expr) = this.kind; - if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind; + && let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind // only take assignments to fields where the left-hand side field is a field of // the same binding as the previous statement - if let ExprKind::Field(binding, field_ident) = assign_lhs.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind; - if let Some(second_binding_name) = path.segments.last(); - if second_binding_name.ident.name == binding_name; - then { - Some((field_ident, assign_rhs)) - } else { - None - } + && let ExprKind::Field(binding, field_ident) = assign_lhs.kind + && let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind + && let Some(second_binding_name) = path.segments.last() + && second_binding_name.ident.name == binding_name + { + Some((field_ident, assign_rhs)) + } else { + None } } /// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }` fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let ExprKind::Struct(_, _, Some(base)) = parent.kind; - then { - base.hir_id == expr.hir_id - } else { - false - } + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::Struct(_, _, Some(base)) = parent.kind + { + base.hir_id == expr.hir_id + } else { + false } } diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index bf070432e..b90d01b76 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -56,32 +56,30 @@ fn is_alias(ty: hir::Ty<'_>) -> bool { impl LateLintPass<'_> for DefaultConstructedUnitStructs { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if_chain!( + if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind // make sure we have a call to `Default::default` - if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind; - if let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind; + && let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind // make sure this isn't a type alias: // `::Assoc` cannot be used as a constructor - if !is_alias(*base); - if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); - if cx.tcx.is_diagnostic_item(sym::default_fn, def_id); + && !is_alias(*base) + && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id) + && cx.tcx.is_diagnostic_item(sym::default_fn, def_id) // make sure we have a struct with no fields (unit struct) - if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind(); - if def.is_struct(); - if let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant(); - if !var.is_field_list_non_exhaustive(); - if !expr.span.from_expansion() && !qpath.span().from_expansion(); - then { - span_lint_and_sugg( - cx, - DEFAULT_CONSTRUCTED_UNIT_STRUCTS, - expr.span.with_lo(qpath.qself_span().hi()), - "use of `default` to create a unit struct", - "remove this call to `default`", - String::new(), - Applicability::MachineApplicable, - ) - } - ); + && let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind() + && def.is_struct() + && let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant() + && !var.is_field_list_non_exhaustive() + && !expr.span.from_expansion() && !qpath.span().from_expansion() + { + span_lint_and_sugg( + cx, + DEFAULT_CONSTRUCTED_UNIT_STRUCTS, + expr.span.with_lo(qpath.qself_span().hi()), + "use of `default` to create a unit struct", + "remove this call to `default`", + String::new(), + Applicability::MachineApplicable, + ); + }; } } diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index b296ea20f..fb2970395 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::{get_parent_node, numeric_literal}; -use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor}; @@ -82,40 +81,40 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { /// Check whether a passed literal has potential to cause fallback or not. fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { - if_chain! { - if !in_external_macro(self.cx.sess(), lit.span); - if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))); - if matches!(lit.node, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - then { - let (suffix, is_float) = match lit_ty.kind() { - ty::Int(IntTy::I32) => ("i32", false), - ty::Float(FloatTy::F64) => ("f64", true), - // Default numeric fallback never results in other types. - _ => return, - }; + if !in_external_macro(self.cx.sess(), lit.span) + && matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))) + && matches!( + lit.node, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) + ) + { + let (suffix, is_float) = match lit_ty.kind() { + ty::Int(IntTy::I32) => ("i32", false), + ty::Float(FloatTy::F64) => ("f64", true), + // Default numeric fallback never results in other types. + _ => return, + }; - let src = if let Some(src) = snippet_opt(self.cx, lit.span) { - src - } else { - match lit.node { - LitKind::Int(src, _) => format!("{src}"), - LitKind::Float(src, _) => format!("{src}"), - _ => return, - } - }; - let sugg = numeric_literal::format(&src, Some(suffix), is_float); - span_lint_hir_and_then( - self.cx, - DEFAULT_NUMERIC_FALLBACK, - emit_hir_id, - lit.span, - "default numeric fallback might occur", - |diag| { - diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect); - } - ); + let src = if let Some(src) = snippet_opt(self.cx, lit.span) { + src + } else { + match lit.node { + LitKind::Int(src, _) => format!("{src}"), + LitKind::Float(src, _) => format!("{src}"), + _ => return, } + }; + let sugg = numeric_literal::format(&src, Some(suffix), is_float); + span_lint_hir_and_then( + self.cx, + DEFAULT_NUMERIC_FALLBACK, + emit_hir_id, + lit.span, + "default numeric fallback might occur", + |diag| { + diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect); + }, + ); } } } @@ -149,36 +148,33 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { ExprKind::Struct(_, fields, base) => { let ty = self.cx.typeck_results().expr_ty(expr); - if_chain! { - if let Some(adt_def) = ty.ty_adt_def(); - if adt_def.is_struct(); - if let Some(variant) = adt_def.variants().iter().next(); - then { - let fields_def = &variant.fields; + if let Some(adt_def) = ty.ty_adt_def() + && adt_def.is_struct() + && let Some(variant) = adt_def.variants().iter().next() + { + let fields_def = &variant.fields; - // Push field type then visit each field expr. - for field in *fields { - let bound = - fields_def - .iter() - .find_map(|f_def| { - if f_def.ident(self.cx.tcx) == field.ident - { Some(self.cx.tcx.type_of(f_def.did).instantiate_identity()) } - else { None } - }); - self.ty_bounds.push(bound.into()); - self.visit_expr(field.expr); - self.ty_bounds.pop(); - } - - // Visit base with no bound. - if let Some(base) = base { - self.ty_bounds.push(ExplicitTyBound(false)); - self.visit_expr(base); - self.ty_bounds.pop(); - } - return; + // Push field type then visit each field expr. + for field in *fields { + let bound = fields_def.iter().find_map(|f_def| { + if f_def.ident(self.cx.tcx) == field.ident { + Some(self.cx.tcx.type_of(f_def.did).instantiate_identity()) + } else { + None + } + }); + self.ty_bounds.push(bound.into()); + self.visit_expr(field.expr); + self.ty_bounds.pop(); } + + // Visit base with no bound. + if let Some(base) = base { + self.ty_bounds.push(ExplicitTyBound(false)); + self.visit_expr(base); + self.ty_bounds.pop(); + } + return; } }, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 6c109a51f..34dd43cfe 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -597,26 +597,24 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { return; } - if_chain! { - if !pat.span.from_expansion(); - if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind(); + if !pat.span.from_expansion() + && let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind() // only lint immutable refs, because borrowed `&mut T` cannot be moved out - if let ty::Ref(_, _, Mutability::Not) = *tam.kind(); - then { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0; - self.current_body = self.current_body.or(cx.enclosing_body); - self.ref_locals.insert( - id, - Some(RefPat { - always_deref: true, - spans: vec![pat.span], - app, - replacements: vec![(pat.span, snip.into())], - hir_id: pat.hir_id, - }), - ); - } + && let ty::Ref(_, _, Mutability::Not) = *tam.kind() + { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0; + self.current_body = self.current_body.or(cx.enclosing_body); + self.ref_locals.insert( + id, + Some(RefPat { + always_deref: true, + spans: vec![pat.span], + app, + replacements: vec![(pat.span, snip.into())], + hir_id: pat.hir_id, + }), + ); } } } diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index a450becc6..9db56fa8a 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -148,83 +148,65 @@ fn check_struct<'tcx>( } fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) { - if_chain! { - if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind; - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res; - if let variant_id = cx.tcx.parent(id); - if let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id); - if variant_def.fields.is_empty(); - if !variant_def.is_field_list_non_exhaustive(); - - then { - let enum_span = cx.tcx.def_span(adt_def.did()); - let indent_enum = indent_of(cx, enum_span).unwrap_or(0); - let variant_span = cx.tcx.def_span(variant_def.def_id); - let indent_variant = indent_of(cx, variant_span).unwrap_or(0); - span_lint_and_then( - cx, - DERIVABLE_IMPLS, + if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && let variant_id = cx.tcx.parent(id) + && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) + && variant_def.fields.is_empty() + && !variant_def.is_field_list_non_exhaustive() + { + let enum_span = cx.tcx.def_span(adt_def.did()); + let indent_enum = indent_of(cx, enum_span).unwrap_or(0); + let variant_span = cx.tcx.def_span(variant_def.def_id); + let indent_variant = indent_of(cx, variant_span).unwrap_or(0); + span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| { + diag.span_suggestion_hidden( item.span, - "this `impl` can be derived", - |diag| { - diag.span_suggestion_hidden( - item.span, - "remove the manual implementation...", - String::new(), - Applicability::MachineApplicable - ); - diag.span_suggestion( - enum_span.shrink_to_lo(), - "...and instead derive it...", - format!( - "#[derive(Default)]\n{indent}", - indent = " ".repeat(indent_enum), - ), - Applicability::MachineApplicable - ); - diag.span_suggestion( - variant_span.shrink_to_lo(), - "...and mark the default variant", - format!( - "#[default]\n{indent}", - indent = " ".repeat(indent_variant), - ), - Applicability::MachineApplicable - ); - } + "remove the manual implementation...", + String::new(), + Applicability::MachineApplicable, ); - } + diag.span_suggestion( + enum_span.shrink_to_lo(), + "...and instead derive it...", + format!("#[derive(Default)]\n{indent}", indent = " ".repeat(indent_enum),), + Applicability::MachineApplicable, + ); + diag.span_suggestion( + variant_span.shrink_to_lo(), + "...and mark the default variant", + format!("#[default]\n{indent}", indent = " ".repeat(indent_variant),), + Applicability::MachineApplicable, + ); + }); } } impl<'tcx> LateLintPass<'tcx> for DerivableImpls { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if_chain! { - if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), - items: [child], - self_ty, - .. - }) = item.kind; - if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived); - if !item.span.from_expansion(); - if let Some(def_id) = trait_ref.trait_def_id(); - if cx.tcx.is_diagnostic_item(sym::Default, def_id); - if let impl_item_hir = child.id.hir_id(); - if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); - if let ImplItemKind::Fn(_, b) = &impl_item.kind; - if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); - if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind(); - if let attrs = cx.tcx.hir().attrs(item.hir_id()); - if !attrs.iter().any(|attr| attr.doc_str().is_some()); - if cx.tcx.hir().attrs(impl_item_hir).is_empty(); - - then { - if adt_def.is_struct() { - check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); - } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { - check_enum(cx, item, func_expr, adt_def); - } + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + self_ty, + .. + }) = item.kind + && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !item.span.from_expansion() + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Default, def_id) + && let impl_item_hir = child.id.hir_id() + && let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir) + && let ImplItemKind::Fn(_, b) = &impl_item.kind + && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b) + && let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() + && let attrs = cx.tcx.hir().attrs(item.hir_id()) + && !attrs.iter().any(|attr| attr.doc_str().is_some()) + && cx.tcx.hir().attrs(impl_item_hir).is_empty() + { + if adt_def.is_struct() { + check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); + } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { + check_enum(cx, item, func_expr, adt_def); } } } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 6aaa9e39b..9807823ad 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; @@ -233,42 +232,37 @@ fn check_hash_peq<'tcx>( ty: Ty<'tcx>, hash_is_automatically_derived: bool, ) { - if_chain! { - if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait(); - if let Some(def_id) = trait_ref.trait_def_id(); - if cx.tcx.is_diagnostic_item(sym::Hash, def_id); - then { - // Look for the PartialEq implementations for `ty` - cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); + if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Hash, def_id) + { + // Look for the PartialEq implementations for `ty` + cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { + let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); - if !hash_is_automatically_derived || peq_is_automatically_derived { - return; - } + if !hash_is_automatically_derived || peq_is_automatically_derived { + return; + } - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - // Only care about `impl PartialEq for Foo` - // For `impl PartialEq for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( - cx, - DERIVED_HASH_WITH_MANUAL_EQ, - span, - "you are deriving `Hash` but have implemented `PartialEq` explicitly", - |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); - diag.span_note( - cx.tcx.hir().span(hir_id), - "`PartialEq` implemented here" - ); - } + // Only care about `impl PartialEq for Foo` + // For `impl PartialEq for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + span_lint_and_then( + cx, + DERIVED_HASH_WITH_MANUAL_EQ, + span, + "you are deriving `Hash` but have implemented `PartialEq` explicitly", + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir().span(hir_id), "`PartialEq` implemented here"); } - ); - } - }); - } + }, + ); + } + }); } } @@ -280,49 +274,38 @@ fn check_ord_partial_ord<'tcx>( ty: Ty<'tcx>, ord_is_automatically_derived: bool, ) { - if_chain! { - if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord); - if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); - if let Some(def_id) = &trait_ref.trait_def_id(); - if *def_id == ord_trait_def_id; - then { - // Look for the PartialOrd implementations for `ty` - cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); + if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() + && let Some(def_id) = &trait_ref.trait_def_id() + && *def_id == ord_trait_def_id + { + // Look for the PartialOrd implementations for `ty` + cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { + let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); - if partial_ord_is_automatically_derived == ord_is_automatically_derived { - return; - } + if partial_ord_is_automatically_derived == ord_is_automatically_derived { + return; + } - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - // Only care about `impl PartialOrd for Foo` - // For `impl PartialOrd for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - let mess = if partial_ord_is_automatically_derived { - "you are implementing `Ord` explicitly but have derived `PartialOrd`" - } else { - "you are deriving `Ord` but have implemented `PartialOrd` explicitly" - }; + // Only care about `impl PartialOrd for Foo` + // For `impl PartialOrd for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + let mess = if partial_ord_is_automatically_derived { + "you are implementing `Ord` explicitly but have derived `PartialOrd`" + } else { + "you are deriving `Ord` but have implemented `PartialOrd` explicitly" + }; - span_lint_and_then( - cx, - DERIVE_ORD_XOR_PARTIAL_ORD, - span, - mess, - |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); - diag.span_note( - cx.tcx.hir().span(hir_id), - "`PartialOrd` implemented here" - ); - } - } - ); - } - }); - } + span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir().span(hir_id), "`PartialOrd` implemented here"); + } + }); + } + }); } } @@ -395,27 +378,27 @@ fn check_unsafe_derive_deserialize<'tcx>( visitor.has_unsafe } - if_chain! { - if let Some(trait_def_id) = trait_ref.trait_def_id(); - if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE); - if let ty::Adt(def, _) = ty.kind(); - if let Some(local_def_id) = def.did().as_local(); - let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); - if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id); - if cx.tcx.inherent_impls(def.did()) + if let Some(trait_def_id) = trait_ref.trait_def_id() + && match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE) + && let ty::Adt(def, _) = ty.kind() + && let Some(local_def_id) = def.did().as_local() + && let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id) + && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) + && cx + .tcx + .inherent_impls(def.did()) .iter() .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local())) - .any(|imp| has_unsafe(cx, imp)); - then { - span_lint_and_help( - cx, - UNSAFE_DERIVE_DESERIALIZE, - item.span, - "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", - None, - "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html" - ); - } + .any(|imp| has_unsafe(cx, imp)) + { + span_lint_and_help( + cx, + UNSAFE_DERIVE_DESERIALIZE, + item.span, + "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", + None, + "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", + ); } } @@ -432,12 +415,10 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { return; } - if_chain! { - if let Some(header) = kind.header(); - if header.unsafety == Unsafety::Unsafe; - then { - self.has_unsafe = true; - } + if let Some(header) = kind.header() + && header.unsafety == Unsafety::Unsafe + { + self.has_unsafe = true; } walk_fn(self, kind, decl, body_id, id); @@ -464,30 +445,28 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - if_chain! { - if let ty::Adt(adt, args) = ty.kind(); - if cx.tcx.visibility(adt.did()).is_public(); - if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); - if let Some(def_id) = trait_ref.trait_def_id(); - if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); - let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id); - if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]); + if let ty::Adt(adt, args) = ty.kind() + && cx.tcx.visibility(adt.did()).is_public() + && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) + && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) + && !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]) // If all of our fields implement `Eq`, we can implement `Eq` too - if adt + && adt .all_fields() .map(|f| f.ty(cx.tcx, args)) - .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])); - then { - span_lint_and_sugg( - cx, - DERIVE_PARTIAL_EQ_WITHOUT_EQ, - span.ctxt().outer_expn_data().call_site, - "you are deriving `PartialEq` and can implement `Eq`", - "consider deriving `Eq` as well", - "PartialEq, Eq".to_string(), - Applicability::MachineApplicable, - ) - } + .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])) + { + span_lint_and_sugg( + cx, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + span.ctxt().outer_expn_data().call_site, + "you are deriving `PartialEq` and can implement `Eq`", + "consider deriving `Eq` as well", + "PartialEq, Eq".to_string(), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index d4a698521..587360545 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -4,7 +4,6 @@ use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; -use if_chain::if_chain; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; @@ -428,25 +427,21 @@ fn lint_for_missing_headers( span, "docs for function returning `Result` missing `# Errors` section", ); - } else { - if_chain! { - if let Some(body_id) = body_id; - if let Some(future) = cx.tcx.lang_items().future_trait(); - let typeck = cx.tcx.typeck_body(body_id); - let body = cx.tcx.hir().body(body_id); - let ret_ty = typeck.expr_ty(body.value); - if implements_trait(cx, ret_ty, future, &[]); - if let ty::Coroutine(_, subs, _) = ret_ty.kind(); - if is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result); - then { - span_lint( - cx, - MISSING_ERRORS_DOC, - span, - "docs for function returning `Result` missing `# Errors` section", - ); - } - } + } else if let Some(body_id) = body_id + && let Some(future) = cx.tcx.lang_items().future_trait() + && let typeck = cx.tcx.typeck_body(body_id) + && let body = cx.tcx.hir().body(body_id) + && let ret_ty = typeck.expr_ty(body.value) + && implements_trait(cx, ret_ty, future, &[]) + && let ty::Coroutine(_, subs, _) = ret_ty.kind() + && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) + { + span_lint( + cx, + MISSING_ERRORS_DOC, + span, + "docs for function returning `Result` missing `# Errors` section", + ); } } } diff --git a/clippy_lints/src/empty_drop.rs b/clippy_lints/src/empty_drop.rs index 5fcdca7cf..17be95780 100644 --- a/clippy_lints/src/empty_drop.rs +++ b/clippy_lints/src/empty_drop.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::peel_blocks; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; @@ -36,31 +35,30 @@ declare_lint_pass!(EmptyDrop => [EMPTY_DROP]); impl LateLintPass<'_> for EmptyDrop { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if_chain! { - if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), - items: [child], - .. - }) = item.kind; - if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait(); - if let impl_item_hir = child.id.hir_id(); - if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); - if let ImplItemKind::Fn(_, b) = &impl_item.kind; - if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); - let func_expr = peel_blocks(func_expr); - if let ExprKind::Block(block, _) = func_expr.kind; - if block.stmts.is_empty() && block.expr.is_none(); - then { - span_lint_and_sugg( - cx, - EMPTY_DROP, - item.span, - "empty drop implementation", - "try removing this impl", - String::new(), - Applicability::MaybeIncorrect - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + .. + }) = item.kind + && trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait() + && let impl_item_hir = child.id.hir_id() + && let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir) + && let ImplItemKind::Fn(_, b) = &impl_item.kind + && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b) + && let func_expr = peel_blocks(func_expr) + && let ExprKind::Block(block, _) = func_expr.kind + && block.stmts.is_empty() + && block.expr.is_none() + { + span_lint_and_sugg( + cx, + EMPTY_DROP, + item.span, + "empty drop implementation", + "try removing this impl", + String::new(), + Applicability::MaybeIncorrect, + ); } } } diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs index affd08221..6f5a0cb88 100644 --- a/clippy_lints/src/endian_bytes.rs +++ b/clippy_lints/src/endian_bytes.rs @@ -114,27 +114,23 @@ impl LateLintPass<'_> for EndianBytes { return; } - if_chain! { - if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind; - if args.is_empty(); - let ty = cx.typeck_results().expr_ty(receiver); - if ty.is_primitive_ty(); - if maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty); - then { - return; - } + if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind + && args.is_empty() + && let ty = cx.typeck_results().expr_ty(receiver) + && ty.is_primitive_ty() + && maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty) + { + return; } - if_chain! { - if let ExprKind::Call(function, ..) = expr.kind; - if let ExprKind::Path(qpath) = function.kind; - if let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id(); - if let Some(function_name) = cx.get_def_path(def_id).last(); - let ty = cx.typeck_results().expr_ty(expr); - if ty.is_primitive_ty(); - then { - maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty); - } + if let ExprKind::Call(function, ..) = expr.kind + && let ExprKind::Path(qpath) = function.kind + && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id() + && let Some(function_name) = cx.get_def_path(def_id).last() + && let ty = cx.typeck_results().expr_ty(expr) + && ty.is_primitive_ty() + { + maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty); } } } diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index f976cfd3f..b7e62e082 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -71,40 +70,31 @@ declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]); impl LateLintPass<'_> for ExhaustiveItems { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if_chain! { - if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind; - if cx.effective_visibilities.is_exported(item.owner_id.def_id); - let attrs = cx.tcx.hir().attrs(item.hir_id()); - if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); - then { - let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { - if v.fields().iter().any(|f| { - !cx.tcx.visibility(f.def_id).is_public() - }) { - // skip structs with private fields - return; - } - (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") - } else { - (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") - }; - let suggestion_span = item.span.shrink_to_lo(); - let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); - span_lint_and_then( - cx, - lint, - item.span, - msg, - |diag| { - let sugg = format!("#[non_exhaustive]\n{indent}"); - diag.span_suggestion(suggestion_span, - "try adding #[non_exhaustive]", - sugg, - Applicability::MaybeIncorrect); - } + if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind + && cx.effective_visibilities.is_exported(item.owner_id.def_id) + && let attrs = cx.tcx.hir().attrs(item.hir_id()) + && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) + { + let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { + if v.fields().iter().any(|f| !cx.tcx.visibility(f.def_id).is_public()) { + // skip structs with private fields + return; + } + (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") + } else { + (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") + }; + let suggestion_span = item.span.shrink_to_lo(); + let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); + span_lint_and_then(cx, lint, item.span, msg, |diag| { + let sugg = format!("#[non_exhaustive]\n{indent}"); + diag.span_suggestion( + suggestion_span, + "try adding #[non_exhaustive]", + sugg, + Applicability::MaybeIncorrect, ); - - } + }); } } } diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index e14b1c556..07d025f68 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_entrypoint_fn; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -42,19 +41,17 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(path_expr, _args) = e.kind; - if let ExprKind::Path(ref path) = path_expr.kind; - if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::process_exit, def_id); - let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id; - if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent); + if let ExprKind::Call(path_expr, _args) = e.kind + && let ExprKind::Path(ref path) = path_expr.kind + && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) + && let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id + && let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent) // If the next item up is a function we check if it is an entry point // and only then emit a linter warning - if !is_entrypoint_fn(cx, parent.to_def_id()); - then { - span_lint(cx, EXIT, e.span, "usage of `process::exit`"); - } + && !is_entrypoint_fn(cx, parent.to_def_id()) + { + span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } } } diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 4b5bcb06a..08cb2114a 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{find_format_args, format_args_inputs_span}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_expn_of, path_def_id}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; @@ -101,30 +100,28 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { /// If `kind` is a block that looks like `{ let result = $expr; result }` then /// returns $expr. Otherwise returns `kind`. fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> { - if_chain! { - if let ExprKind::Block(block, _label @ None) = kind; - if let Block { + if let ExprKind::Block(block, _label @ None) = kind + && let Block { stmts: [Stmt { kind: StmtKind::Local(local), .. }], expr: Some(expr_end_of_block), rules: BlockCheckMode::DefaultBlock, .. - } = block; + } = block // Find id of the local that expr_end_of_block resolves to - if let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind; - if let Res::Local(expr_res) = expr_path.res; - if let Some(Node::Pat(res_pat)) = cx.tcx.hir().find(expr_res); + && let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind + && let Res::Local(expr_res) = expr_path.res + && let Some(Node::Pat(res_pat)) = cx.tcx.hir().find(expr_res) // Find id of the local we found in the block - if let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind; + && let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind // If those two are the same hir id - if res_pat.hir_id == local_hir_id; + && res_pat.hir_id == local_hir_id - if let Some(init) = local.init; - then { - return &init.kind; - } + && let Some(init) = local.init + { + return &init.kind; } kind } diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index efb69476b..753f75d83 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::method_chain_args; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -53,13 +52,13 @@ declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]); impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { // check for `impl From for ..` - if_chain! { - if let hir::ItemKind::Impl(impl_) = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); - if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id); - then { - lint_impl_body(cx, item.span, impl_.items); - } + if let hir::ItemKind::Impl(impl_) = &item.kind + && let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) + && cx + .tcx + .is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id) + { + lint_impl_body(cx, item.span, impl_.items); } } } @@ -98,34 +97,33 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl } for impl_item in impl_items { - if_chain! { - if impl_item.ident.name == sym::from; - if let ImplItemKind::Fn(_, body_id) = - cx.tcx.hir().impl_item(impl_item.id).kind; - then { - // check the body for `begin_panic` or `unwrap` - let body = cx.tcx.hir().body(body_id); - let mut fpu = FindPanicUnwrap { - lcx: cx, - typeck_results: cx.tcx.typeck(impl_item.id.owner_id.def_id), - result: Vec::new(), - }; - fpu.visit_expr(body.value); + if impl_item.ident.name == sym::from + && let ImplItemKind::Fn(_, body_id) = cx.tcx.hir().impl_item(impl_item.id).kind + { + // check the body for `begin_panic` or `unwrap` + let body = cx.tcx.hir().body(body_id); + let mut fpu = FindPanicUnwrap { + lcx: cx, + typeck_results: cx.tcx.typeck(impl_item.id.owner_id.def_id), + result: Vec::new(), + }; + fpu.visit_expr(body.value); - // if we've found one, lint - if !fpu.result.is_empty() { - span_lint_and_then( - cx, - FALLIBLE_IMPL_FROM, - impl_span, - "consider implementing `TryFrom` instead", - move |diag| { - diag.help( - "`From` is intended for infallible conversions only. \ - Use `TryFrom` if there's a possibility for the conversion to fail"); - diag.span_note(fpu.result, "potential failure(s)"); - }); - } + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + FALLIBLE_IMPL_FROM, + impl_span, + "consider implementing `TryFrom` instead", + move |diag| { + diag.help( + "`From` is intended for infallible conversions only. \ + Use `TryFrom` if there's a possibility for the conversion to fail", + ); + diag.span_note(fpu.result, "potential failure(s)"); + }, + ); } } } diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 506a11917..663c33e8c 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal; -use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -64,73 +63,70 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { let ty = cx.typeck_results().expr_ty(expr); - if_chain! { - if let ty::Float(fty) = *ty.kind(); - if let hir::ExprKind::Lit(lit) = expr.kind; - if let LitKind::Float(sym, lit_float_ty) = lit.node; - then { - let sym_str = sym.as_str(); - let formatter = FloatFormat::new(sym_str); - // Try to bail out if the float is for sure fine. - // If its within the 2 decimal digits of being out of precision we - // check if the parsed representation is the same as the string - // since we'll need the truncated string anyway. - let digits = count_digits(sym_str); - let max = max_digits(fty); - let type_suffix = match lit_float_ty { - LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"), - LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"), - LitFloatType::Unsuffixed => None - }; - let (is_whole, is_inf, mut float_str) = match fty { - FloatTy::F32 => { - let value = sym_str.parse::().unwrap(); + if let ty::Float(fty) = *ty.kind() + && let hir::ExprKind::Lit(lit) = expr.kind + && let LitKind::Float(sym, lit_float_ty) = lit.node + { + let sym_str = sym.as_str(); + let formatter = FloatFormat::new(sym_str); + // Try to bail out if the float is for sure fine. + // If its within the 2 decimal digits of being out of precision we + // check if the parsed representation is the same as the string + // since we'll need the truncated string anyway. + let digits = count_digits(sym_str); + let max = max_digits(fty); + let type_suffix = match lit_float_ty { + LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"), + LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"), + LitFloatType::Unsuffixed => None, + }; + let (is_whole, is_inf, mut float_str) = match fty { + FloatTy::F32 => { + let value = sym_str.parse::().unwrap(); - (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) - }, - FloatTy::F64 => { - let value = sym_str.parse::().unwrap(); + (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) + }, + FloatTy::F64 => { + let value = sym_str.parse::().unwrap(); + (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) + }, + }; - (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) - }, - }; + if is_inf { + return; + } - if is_inf { - return; - } - - if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') { - // Normalize the literal by stripping the fractional portion - if sym_str.split('.').next().unwrap() != float_str { - // If the type suffix is missing the suggestion would be - // incorrectly interpreted as an integer so adding a `.0` - // suffix to prevent that. - if type_suffix.is_none() { - float_str.push_str(".0"); - } - - span_lint_and_sugg( - cx, - LOSSY_FLOAT_LITERAL, - expr.span, - "literal cannot be represented as the underlying type without loss of precision", - "consider changing the type or replacing it with", - numeric_literal::format(&float_str, type_suffix, true), - Applicability::MachineApplicable, - ); + if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') { + // Normalize the literal by stripping the fractional portion + if sym_str.split('.').next().unwrap() != float_str { + // If the type suffix is missing the suggestion would be + // incorrectly interpreted as an integer so adding a `.0` + // suffix to prevent that. + if type_suffix.is_none() { + float_str.push_str(".0"); } - } else if digits > max as usize && float_str.len() < sym_str.len() { + span_lint_and_sugg( cx, - EXCESSIVE_PRECISION, + LOSSY_FLOAT_LITERAL, expr.span, - "float has excessive precision", - "consider changing the type or truncating it to", + "literal cannot be represented as the underlying type without loss of precision", + "consider changing the type or replacing it with", numeric_literal::format(&float_str, type_suffix, true), Applicability::MachineApplicable, ); } + } else if digits > max as usize && float_str.len() < sym_str.len() { + span_lint_and_sugg( + cx, + EXCESSIVE_PRECISION, + expr.span, + "float has excessive precision", + "consider changing the type or truncating it to", + numeric_literal::format(&float_str, type_suffix, true), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 09a9d9924..d52287347 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -133,30 +132,25 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su expr = inner_expr; } - if_chain! { + if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind() // if the expression is a float literal and it is unsuffixed then // add a suffix so the suggestion is valid and unambiguous - if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind(); - if let ExprKind::Lit(lit) = &expr.kind; - if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; - then { - let op = format!( - "{suggestion}{}{}", - // Check for float literals without numbers following the decimal - // separator such as `2.` and adds a trailing zero - if sym.as_str().ends_with('.') { - "0" - } else { - "" - }, - float_ty.name_str() - ).into(); + && let ExprKind::Lit(lit) = &expr.kind + && let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node + { + let op = format!( + "{suggestion}{}{}", + // Check for float literals without numbers following the decimal + // separator such as `2.` and adds a trailing zero + if sym.as_str().ends_with('.') { "0" } else { "" }, + float_ty.name_str() + ) + .into(); - suggestion = match suggestion { - Sugg::MaybeParen(_) => Sugg::MaybeParen(op), - _ => Sugg::NonParen(op) - }; - } + suggestion = match suggestion { + Sugg::MaybeParen(_) => Sugg::MaybeParen(op), + _ => Sugg::NonParen(op), + }; } suggestion.maybe_par() @@ -359,35 +353,59 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { ) = receiver.kind { // check if expression of the form x * x + y * y - if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind; - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind; - if eq_expr_value(cx, lmul_lhs, lmul_rhs); - if eq_expr_value(cx, rmul_lhs, rmul_rhs); - then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, "..").maybe_par(), Sugg::hir(cx, rmul_lhs, ".."))); - } + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Mul, .. + }, + lmul_lhs, + lmul_rhs, + ) = add_lhs.kind + && let ExprKind::Binary( + Spanned { + node: BinOpKind::Mul, .. + }, + rmul_lhs, + rmul_rhs, + ) = add_rhs.kind + && eq_expr_value(cx, lmul_lhs, lmul_rhs) + && eq_expr_value(cx, rmul_lhs, rmul_rhs) + { + return Some(format!( + "{}.hypot({})", + Sugg::hir(cx, lmul_lhs, "..").maybe_par(), + Sugg::hir(cx, rmul_lhs, "..") + )); } // check if expression of the form x.powi(2) + y.powi(2) - if_chain! { - if let ExprKind::MethodCall( - PathSegment { ident: lmethod_name, .. }, - largs_0, [largs_1, ..], - _ - ) = &add_lhs.kind; - if let ExprKind::MethodCall( - PathSegment { ident: rmethod_name, .. }, - rargs_0, [rargs_1, ..], - _ - ) = &add_rhs.kind; - if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; - if let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1); - if let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1); - if Int(2) == lvalue && Int(2) == rvalue; - then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, ".."))); - } + if let ExprKind::MethodCall( + PathSegment { + ident: lmethod_name, .. + }, + largs_0, + [largs_1, ..], + _, + ) = &add_lhs.kind + && let ExprKind::MethodCall( + PathSegment { + ident: rmethod_name, .. + }, + rargs_0, + [rargs_1, ..], + _, + ) = &add_rhs.kind + && lmethod_name.as_str() == "powi" + && rmethod_name.as_str() == "powi" + && let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1) + && let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1) + && Int(2) == lvalue + && Int(2) == rvalue + { + return Some(format!( + "{}.hypot({})", + Sugg::hir(cx, largs_0, "..").maybe_par(), + Sugg::hir(cx, rargs_0, "..") + )); } } @@ -411,39 +429,44 @@ fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { // TODO: Lint expressions of the form `x.exp() - y` where y > 1 // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind; - if cx.typeck_results().expr_ty(lhs).is_floating_point(); - if let Some(value) = constant(cx, cx.typeck_results(), rhs); - if F32(1.0) == value || F64(1.0) == value; - if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind; - if cx.typeck_results().expr_ty(self_arg).is_floating_point(); - if path.ident.name.as_str() == "exp"; - then { - span_lint_and_sugg( - cx, - IMPRECISE_FLOPS, - expr.span, - "(e.pow(x) - 1) can be computed more accurately", - "consider using", - format!( - "{}.exp_m1()", - Sugg::hir(cx, self_arg, "..").maybe_par() - ), - Applicability::MachineApplicable, - ); - } + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = expr.kind + && cx.typeck_results().expr_ty(lhs).is_floating_point() + && let Some(value) = constant(cx, cx.typeck_results(), rhs) + && (F32(1.0) == value || F64(1.0) == value) + && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind + && cx.typeck_results().expr_ty(self_arg).is_floating_point() + && path.ident.name.as_str() == "exp" + { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "(e.pow(x) - 1) can be computed more accurately", + "consider using", + format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_par()), + Applicability::MachineApplicable, + ); } } fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { - if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind; - if cx.typeck_results().expr_ty(lhs).is_floating_point(); - if cx.typeck_results().expr_ty(rhs).is_floating_point(); - then { - return Some((lhs, rhs)); - } + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Mul, .. + }, + lhs, + rhs, + ) = &expr.kind + && cx.typeck_results().expr_ty(lhs).is_floating_point() + && cx.typeck_results().expr_ty(rhs).is_floating_point() + { + return Some((lhs, rhs)); } None @@ -553,60 +576,72 @@ 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 Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr); - let if_body_expr = peel_blocks(then); - let else_body_expr = peel_blocks(r#else); - if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr); - then { - let positive_abs_sugg = ( - "manual implementation of `abs` method", - format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), - ); - let negative_abs_sugg = ( - "manual implementation of negation of `abs` method", - format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), - ); - let sugg = if is_testing_positive(cx, cond, body) { - if if_expr_positive { - positive_abs_sugg - } else { - negative_abs_sugg - } - } else if is_testing_negative(cx, cond, body) { - if if_expr_positive { - negative_abs_sugg - } else { - positive_abs_sugg - } + if let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(expr) + && let if_body_expr = peel_blocks(then) + && let else_body_expr = peel_blocks(r#else) + && let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr) + { + let positive_abs_sugg = ( + "manual implementation of `abs` method", + format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + ); + let negative_abs_sugg = ( + "manual implementation of negation of `abs` method", + format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + ); + let sugg = if is_testing_positive(cx, cond, body) { + if if_expr_positive { + positive_abs_sugg } else { - return; - }; - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - sugg.0, - "try", - sugg.1, - Applicability::MachineApplicable, - ); - } + negative_abs_sugg + } + } else if is_testing_negative(cx, cond, body) { + if if_expr_positive { + negative_abs_sugg + } else { + positive_abs_sugg + } + } else { + return; + }; + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + sugg.0, + "try", + sugg.1, + Applicability::MachineApplicable, + ); } } fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind; - if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind; - then { - return method_name_a.as_str() == method_name_b.as_str() && - args_a.len() == args_b.len() && - ( - ["ln", "log2", "log10"].contains(&method_name_a.as_str()) || - method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0]) - ); - } + if let ExprKind::MethodCall( + PathSegment { + ident: method_name_a, .. + }, + _, + args_a, + _, + ) = expr_a.kind + && let ExprKind::MethodCall( + PathSegment { + ident: method_name_b, .. + }, + _, + args_b, + _, + ) = expr_b.kind + { + return method_name_a.as_str() == method_name_b.as_str() + && args_a.len() == args_b.len() + && (["ln", "log2", "log10"].contains(&method_name_a.as_str()) + || method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])); } false @@ -614,103 +649,98 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_> fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { // check if expression of the form x.logN() / y.logN() - if_chain! { - if let ExprKind::Binary( - Spanned { - node: BinOpKind::Div, .. - }, - lhs, - rhs, - ) = &expr.kind; - if are_same_base_logs(cx, lhs, rhs); - if let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind; - if let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind; - then { - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "log base can be expressed more clearly", - "consider using", - format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),), - Applicability::MachineApplicable, - ); - } + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + lhs, + rhs, + ) = &expr.kind + && are_same_base_logs(cx, lhs, rhs) + && let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind + && let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind + { + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "log base can be expressed more clearly", + "consider using", + format!( + "{}.log({})", + Sugg::hir(cx, largs_self, "..").maybe_par(), + Sugg::hir(cx, rargs_self, ".."), + ), + Applicability::MachineApplicable, + ); } } fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Binary( - Spanned { - node: BinOpKind::Div, .. - }, - div_lhs, - div_rhs, - ) = &expr.kind; - if let ExprKind::Binary( + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + div_lhs, + div_rhs, + ) = &expr.kind + && let ExprKind::Binary( Spanned { node: BinOpKind::Mul, .. }, mul_lhs, mul_rhs, - ) = &div_lhs.kind; - if let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs); - if let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs); - then { - // TODO: also check for constant values near PI/180 or 180/PI - if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && - (F32(180_f32) == lvalue || F64(180_f64) == lvalue) + ) = &div_lhs.kind + && let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs) + && let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs) + { + // TODO: also check for constant values near PI/180 or 180/PI + if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) + && (F32(180_f32) == lvalue || F64(180_f64) == lvalue) + { + let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); + if let ExprKind::Lit(literal) = mul_lhs.kind + && let ast::LitKind::Float(ref value, float_type) = literal.node + && float_type == ast::LitFloatType::Unsuffixed { - let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); - if_chain! { - if let ExprKind::Lit(literal) = mul_lhs.kind; - if let ast::LitKind::Float(ref value, float_type) = literal.node; - if float_type == ast::LitFloatType::Unsuffixed; - then { - if value.as_str().ends_with('.') { - proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); - } else { - proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); - } - } + if value.as_str().ends_with('.') { + proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); + } else { + proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); } - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "conversion to degrees can be done more accurately", - "consider using", - proposal, - Applicability::MachineApplicable, - ); - } else if - (F32(180_f32) == rvalue || F64(180_f64) == rvalue) && - (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) - { - let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); - if_chain! { - if let ExprKind::Lit(literal) = mul_lhs.kind; - if let ast::LitKind::Float(ref value, float_type) = literal.node; - if float_type == ast::LitFloatType::Unsuffixed; - then { - if value.as_str().ends_with('.') { - proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); - } else { - proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); - } - } - } - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "conversion to radians can be done more accurately", - "consider using", - proposal, - Applicability::MachineApplicable, - ); } + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "conversion to degrees can be done more accurately", + "consider using", + proposal, + Applicability::MachineApplicable, + ); + } else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue) + && (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) + { + let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); + if let ExprKind::Lit(literal) = mul_lhs.kind + && let ast::LitKind::Float(ref value, float_type) = literal.node + && float_type == ast::LitFloatType::Unsuffixed + { + if value.as_str().ends_with('.') { + proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); + } else { + proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); + } + } + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "conversion to radians can be done more accurately", + "consider using", + proposal, + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 3c1f2d9d5..c9868255d 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -8,7 +8,6 @@ use clippy_utils::macros::{ }; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; -use if_chain::if_chain; use itertools::Itertools; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, @@ -404,49 +403,43 @@ fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symb } fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { - if_chain! { - if !value.span.from_expansion(); - if let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind; - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id); - if is_diag_trait_item(cx, method_def_id, sym::ToString); - let receiver_ty = cx.typeck_results().expr_ty(receiver); - if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display); - let (n_needed_derefs, target) = - count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()); - if implements_trait(cx, target, display_trait_id, &[]); - if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait(); - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); - if n_needed_derefs == 0 && !needs_ref { - span_lint_and_sugg( - cx, - TO_STRING_IN_FORMAT_ARGS, - to_string_span.with_lo(receiver.span.hi()), - &format!( - "`to_string` applied to a type that implements `Display` in `{name}!` args" - ), - "remove this", - String::new(), - Applicability::MachineApplicable, - ); - } else { - span_lint_and_sugg( - cx, - TO_STRING_IN_FORMAT_ARGS, - value.span, - &format!( - "`to_string` applied to a type that implements `Display` in `{name}!` args" - ), - "use this", - format!( - "{}{:*>n_needed_derefs$}{receiver_snippet}", - if needs_ref { "&" } else { "" }, - "" - ), - Applicability::MachineApplicable, - ); - } + if !value.span.from_expansion() + && let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) + && is_diag_trait_item(cx, method_def_id, sym::ToString) + && let receiver_ty = cx.typeck_results().expr_ty(receiver) + && let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display) + && let (n_needed_derefs, target) = + count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) + && implements_trait(cx, target, display_trait_id, &[]) + && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); + if n_needed_derefs == 0 && !needs_ref { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + to_string_span.with_lo(receiver.span.hi()), + &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), + "remove this", + String::new(), + Applicability::MachineApplicable, + ); + } else { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + value.span, + &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), + "use this", + format!( + "{}{:*>n_needed_derefs$}{receiver_snippet}", + if needs_ref { "&" } else { "" }, + "" + ), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 08ee7032c..ec87629a3 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node}; use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; -use if_chain::if_chain; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; @@ -141,27 +140,25 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl { } fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { + if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind // Get the hir_id of the object we are calling the method on - if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind; // Is the method to_string() ? - if path.ident.name == sym::to_string; + && path.ident.name == sym::to_string // Is the method a part of the ToString trait? (i.e. not to_string() implemented // separately) - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diag_trait_item(cx, expr_def_id, sym::ToString); + && let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && is_diag_trait_item(cx, expr_def_id, sym::ToString) // Is the method is called on self - if let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind; - if let [segment] = path.segments; - if segment.ident.name == kw::SelfLower; - then { - span_lint( - cx, - RECURSIVE_FORMAT_IMPL, - expr.span, - "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion", - ); - } + && let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind + && let [segment] = path.segments + && segment.ident.name == kw::SelfLower + { + span_lint( + cx, + RECURSIVE_FORMAT_IMPL, + expr.span, + "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion", + ); } } @@ -215,55 +212,53 @@ fn check_format_arg_self(cx: &LateContext<'_>, span: Span, arg: &Expr<'_>, impl_ } fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) { - if_chain! { - if let Some(macro_call) = root_macro_call_first_node(cx, expr); - if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id); - then { - let replacement = match name { - sym::print_macro | sym::eprint_macro => "write", - sym::println_macro | sym::eprintln_macro => "writeln", - _ => return, - }; + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + let replacement = match name { + sym::print_macro | sym::eprint_macro => "write", + sym::println_macro | sym::eprintln_macro => "writeln", + _ => return, + }; - let name = name.as_str().strip_suffix("_macro").unwrap(); + let name = name.as_str().strip_suffix("_macro").unwrap(); - span_lint_and_sugg( - cx, - PRINT_IN_FORMAT_IMPL, - macro_call.span, - &format!("use of `{name}!` in `{}` impl", impl_trait.name), - "replace with", - if let Some(formatter_name) = impl_trait.formatter_name { - format!("{replacement}!({formatter_name}, ..)") - } else { - format!("{replacement}!(..)") - }, - Applicability::HasPlaceholders, - ); - } + span_lint_and_sugg( + cx, + PRINT_IN_FORMAT_IMPL, + macro_call.span, + &format!("use of `{name}!` in `{}` impl", impl_trait.name), + "replace with", + if let Some(formatter_name) = impl_trait.formatter_name { + format!("{replacement}!({formatter_name}, ..)") + } else { + format!("{replacement}!(..)") + }, + Applicability::HasPlaceholders, + ); } } fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option { - if_chain! { - if impl_item.ident.name == sym::fmt; - if let ImplItemKind::Fn(_, body_id) = impl_item.kind; - if let Some(Impl { of_trait: Some(trait_ref),..}) = get_parent_as_impl(cx.tcx, impl_item.hir_id()); - if let Some(did) = trait_ref.trait_def_id(); - if let Some(name) = cx.tcx.get_diagnostic_name(did); - if matches!(name, sym::Debug | sym::Display); - then { - let body = cx.tcx.hir().body(body_id); - let formatter_name = body.params.get(1) - .and_then(|param| param.pat.simple_ident()) - .map(|ident| ident.name); + if impl_item.ident.name == sym::fmt + && let ImplItemKind::Fn(_, body_id) = impl_item.kind + && let Some(Impl { + of_trait: Some(trait_ref), + .. + }) = get_parent_as_impl(cx.tcx, impl_item.hir_id()) + && let Some(did) = trait_ref.trait_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(did) + && matches!(name, sym::Debug | sym::Display) + { + let body = cx.tcx.hir().body(body_id); + let formatter_name = body + .params + .get(1) + .and_then(|param| param.pat.simple_ident()) + .map(|ident| ident.name); - Some(FormatTraitNames { - name, - formatter_name, - }) - } else { - None - } + Some(FormatTraitNames { name, formatter_name }) + } else { + None } } diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 10ddc3bda..a1db7b63b 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; use clippy_utils::is_span_if; use clippy_utils::source::snippet_opt; -use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -168,93 +167,84 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { /// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint. fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { - if_chain! { - if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind; - if !lhs.span.from_expansion() && !rhs.span.from_expansion(); + if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind + && !lhs.span.from_expansion() && !rhs.span.from_expansion() // span between BinOp LHS and RHS - let binop_span = lhs.span.between(rhs.span); + && let binop_span = lhs.span.between(rhs.span) // if RHS is an UnOp - if let ExprKind::Unary(op, ref un_rhs) = rhs.kind; + && let ExprKind::Unary(op, ref un_rhs) = rhs.kind // from UnOp operator to UnOp operand - let unop_operand_span = rhs.span.until(un_rhs.span); - if let Some(binop_snippet) = snippet_opt(cx, binop_span); - if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span); - let binop_str = BinOpKind::to_string(&binop.node); + && let unop_operand_span = rhs.span.until(un_rhs.span) + && let Some(binop_snippet) = snippet_opt(cx, binop_span) + && let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span) + && let binop_str = BinOpKind::to_string(&binop.node) // no space after BinOp operator and space after UnOp operator - if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' '); - then { - let unop_str = UnOp::to_string(op); - let eqop_span = lhs.span.between(un_rhs.span); - span_lint_and_help( - cx, - SUSPICIOUS_UNARY_OP_FORMATTING, - eqop_span, - &format!( - "by not having a space between `{binop_str}` and `{unop_str}` it looks like \ - `{binop_str}{unop_str}` is a single operator" - ), - None, - &format!( - "put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`" - ), - ); - } + && binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ') + { + let unop_str = UnOp::to_string(op); + let eqop_span = lhs.span.between(un_rhs.span); + span_lint_and_help( + cx, + SUSPICIOUS_UNARY_OP_FORMATTING, + eqop_span, + &format!( + "by not having a space between `{binop_str}` and `{unop_str}` it looks like \ + `{binop_str}{unop_str}` is a single operator" + ), + None, + &format!("put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"), + ); } } /// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`. fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { - if_chain! { - if let ExprKind::If(_, then, Some(else_)) = &expr.kind; - if is_block(else_) || is_if(else_); - if !then.span.from_expansion() && !else_.span.from_expansion(); - if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::If(_, then, Some(else_)) = &expr.kind + && (is_block(else_) || is_if(else_)) + && !then.span.from_expansion() && !else_.span.from_expansion() + && !in_external_macro(cx.sess(), expr.span) // workaround for rust-lang/rust#43081 - if expr.span.lo().0 != 0 && expr.span.hi().0 != 0; + && expr.span.lo().0 != 0 && expr.span.hi().0 != 0 // this will be a span from the closing ‘}’ of the “then” block (excluding) to // the “if” of the “else if” block (excluding) - let else_span = then.span.between(else_.span); + && let else_span = then.span.between(else_.span) // the snippet should look like " else \n " with maybe comments anywhere // it’s bad when there is a ‘\n’ after the “else” - if let Some(else_snippet) = snippet_opt(cx, else_span); - if let Some((pre_else, post_else)) = else_snippet.split_once("else"); - if let Some((_, post_else_post_eol)) = post_else.split_once('\n'); - - then { - // Allow allman style braces `} \n else \n {` - if_chain! { - if is_block(else_); - if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n'); - // Exactly one eol before and after the else - if !pre_else_post_eol.contains('\n'); - if !post_else_post_eol.contains('\n'); - then { - return; - } - } - - // Don't warn if the only thing inside post_else_post_eol is a comment block. - let trimmed_post_else_post_eol = post_else_post_eol.trim(); - if trimmed_post_else_post_eol.starts_with("/*") && trimmed_post_else_post_eol.ends_with("*/") { - return - } - - let else_desc = if is_if(else_) { "if" } else { "{..}" }; - span_lint_and_note( - cx, - SUSPICIOUS_ELSE_FORMATTING, - else_span, - &format!("this is an `else {else_desc}` but the formatting might hide it"), - None, - &format!( - "to remove this lint, remove the `else` or remove the new line between \ - `else` and `{else_desc}`", - ), - ); + && let Some(else_snippet) = snippet_opt(cx, else_span) + && let Some((pre_else, post_else)) = else_snippet.split_once("else") + && let Some((_, post_else_post_eol)) = post_else.split_once('\n') + { + // Allow allman style braces `} \n else \n {` + if is_block(else_) + && let Some((_, pre_else_post_eol)) = pre_else.split_once('\n') + // Exactly one eol before and after the else + && !pre_else_post_eol.contains('\n') + && !post_else_post_eol.contains('\n') + { + return; } + + // Don't warn if the only thing inside post_else_post_eol is a comment block. + let trimmed_post_else_post_eol = post_else_post_eol.trim(); + if trimmed_post_else_post_eol.starts_with("/*") && trimmed_post_else_post_eol.ends_with("*/") { + return; + } + + let else_desc = if is_if(else_) { "if" } else { "{..}" }; + span_lint_and_note( + cx, + SUSPICIOUS_ELSE_FORMATTING, + else_span, + &format!("this is an `else {else_desc}` but the formatting might hide it"), + None, + &format!( + "to remove this lint, remove the `else` or remove the new line between \ + `else` and `{else_desc}`", + ), + ); } } @@ -272,61 +262,56 @@ fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize { fn check_array(cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::Array(ref array) = expr.kind { for element in array { - if_chain! { - if let ExprKind::Binary(ref op, ref lhs, _) = element.kind; - if has_unary_equivalent(op.node) && lhs.span.eq_ctxt(op.span); - let space_span = lhs.span.between(op.span); - if let Some(space_snippet) = snippet_opt(cx, space_span); - let lint_span = lhs.span.with_lo(lhs.span.hi()); - if space_snippet.contains('\n'); - if indentation(cx, op.span) <= indentation(cx, lhs.span); - then { - span_lint_and_note( - cx, - POSSIBLE_MISSING_COMMA, - lint_span, - "possibly missing a comma here", - None, - "to remove this lint, add a comma or write the expr in a single line", - ); - } + if let ExprKind::Binary(ref op, ref lhs, _) = element.kind + && has_unary_equivalent(op.node) + && lhs.span.eq_ctxt(op.span) + && let space_span = lhs.span.between(op.span) + && let Some(space_snippet) = snippet_opt(cx, space_span) + && let lint_span = lhs.span.with_lo(lhs.span.hi()) + && space_snippet.contains('\n') + && indentation(cx, op.span) <= indentation(cx, lhs.span) + { + span_lint_and_note( + cx, + POSSIBLE_MISSING_COMMA, + lint_span, + "possibly missing a comma here", + None, + "to remove this lint, add a comma or write the expr in a single line", + ); } } } } fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { - if_chain! { - if !first.span.from_expansion() && !second.span.from_expansion(); - if matches!(first.kind, ExprKind::If(..)); - if is_block(second) || is_if(second); + if !first.span.from_expansion() && !second.span.from_expansion() + && matches!(first.kind, ExprKind::If(..)) + && (is_block(second) || is_if(second)) // Proc-macros can give weird spans. Make sure this is actually an `if`. - if is_span_if(cx, first.span); + && is_span_if(cx, first.span) // If there is a line break between the two expressions, don't lint. // If there is a non-whitespace character, this span came from a proc-macro. - let else_span = first.span.between(second.span); - if let Some(else_snippet) = snippet_opt(cx, else_span); - if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace()); - then { - let (looks_like, next_thing) = if is_if(second) { - ("an `else if`", "the second `if`") - } else { - ("an `else {..}`", "the next block") - }; + && let else_span = first.span.between(second.span) + && let Some(else_snippet) = snippet_opt(cx, else_span) + && !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace()) + { + let (looks_like, next_thing) = if is_if(second) { + ("an `else if`", "the second `if`") + } else { + ("an `else {..}`", "the next block") + }; - span_lint_and_note( - cx, - SUSPICIOUS_ELSE_FORMATTING, - else_span, - &format!("this looks like {looks_like} but the `else` is missing"), - None, - &format!( - "to remove this lint, add the missing `else` or add a new line before {next_thing}", - ), - ); - } + span_lint_and_note( + cx, + SUSPICIOUS_ELSE_FORMATTING, + else_span, + &format!("this looks like {looks_like} but the `else` is missing"), + None, + &format!("to remove this lint, add the missing `else` or add a new line before {next_thing}",), + ); } } diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 74a60b6a0..18d11ccc0 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -46,52 +45,41 @@ declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { - if_chain! { - if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind; - if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind; + if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind + && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind // check if the first part of the path is some integer primitive - if let TyKind::Path(ty_qpath) = &ty.kind; - let ty_res = cx.qpath_res(ty_qpath, ty.hir_id); - if let def::Res::PrimTy(prim_ty) = ty_res; - if matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)); + && let TyKind::Path(ty_qpath) = &ty.kind + && let ty_res = cx.qpath_res(ty_qpath, ty.hir_id) + && let def::Res::PrimTy(prim_ty) = ty_res + && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)) // check if the second part of the path indeed calls the associated // function `from_str_radix` - if pathseg.ident.name.as_str() == "from_str_radix"; + && pathseg.ident.name.as_str() == "from_str_radix" // check if the second argument is a primitive `10` - if is_integer_literal(radix, 10); + && is_integer_literal(radix, 10) + { + let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { + let ty = cx.typeck_results().expr_ty(expr); + if is_ty_stringish(cx, ty) { expr } else { &src } + } else { + &src + }; - then { - let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { - let ty = cx.typeck_results().expr_ty(expr); - if is_ty_stringish(cx, ty) { - expr - } else { - &src - } - } else { - &src - }; + let sugg = + Sugg::hir_with_applicability(cx, expr, "", &mut Applicability::MachineApplicable).maybe_par(); - let sugg = Sugg::hir_with_applicability( - cx, - expr, - "", - &mut Applicability::MachineApplicable - ).maybe_par(); - - span_lint_and_sugg( - cx, - FROM_STR_RADIX_10, - exp.span, - "this call to `from_str_radix` can be replaced with a call to `str::parse`", - "try", - format!("{sugg}.parse::<{}>()", prim_ty.name_str()), - Applicability::MaybeIncorrect - ); - } + span_lint_and_sugg( + cx, + FROM_STR_RADIX_10, + exp.span, + "this call to `from_str_radix` can be replaced with a call to `str::parse`", + "try", + format!("{sugg}.parse::<{}>()", prim_ty.name_str()), + Applicability::MaybeIncorrect, + ); } } } diff --git a/clippy_lints/src/functions/impl_trait_in_params.rs b/clippy_lints/src/functions/impl_trait_in_params.rs index ee66c841e..4afff8a24 100644 --- a/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/clippy_lints/src/functions/impl_trait_in_params.rs @@ -52,54 +52,48 @@ fn report( } pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) { - if_chain! { - if let FnKind::ItemFn(ident, generics, _) = kind; - if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); - if !is_in_test_function(cx.tcx, hir_id); - then { - for param in generics.params { - if param.is_impl_trait() { - report(cx, param, ident, generics, body.params[0].span); - }; - } + if let FnKind::ItemFn(ident, generics, _) = kind + && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() + && !is_in_test_function(cx.tcx, hir_id) + { + for param in generics.params { + if param.is_impl_trait() { + report(cx, param, ident, generics, body.params[0].span); + }; } } } pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { - if_chain! { - if let ImplItemKind::Fn(_, body_id) = impl_item.kind; - if let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id()); - if let hir::ItemKind::Impl(impl_) = item.kind; - if let hir::Impl { of_trait, .. } = *impl_; - if of_trait.is_none(); - let body = cx.tcx.hir().body(body_id); - if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); - if !is_in_test_function(cx.tcx, impl_item.hir_id()); - then { - for param in impl_item.generics.params { - if param.is_impl_trait() { - report(cx, param, &impl_item.ident, impl_item.generics, body.params[0].span); - } + if let ImplItemKind::Fn(_, body_id) = impl_item.kind + && let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id()) + && let hir::ItemKind::Impl(impl_) = item.kind + && let hir::Impl { of_trait, .. } = *impl_ + && of_trait.is_none() + && let body = cx.tcx.hir().body(body_id) + && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() + && !is_in_test_function(cx.tcx, impl_item.hir_id()) + { + for param in impl_item.generics.params { + if param.is_impl_trait() { + report(cx, param, &impl_item.ident, impl_item.generics, body.params[0].span); } } } } pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) { - if_chain! { - if !avoid_breaking_exported_api; - if let TraitItemKind::Fn(_, _) = trait_item.kind; - if let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id()); + if !avoid_breaking_exported_api + && let TraitItemKind::Fn(_, _) = trait_item.kind + && let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id()) // ^^ (Will always be a trait) - if !item.vis_span.is_empty(); // Is public - if !is_in_test_function(cx.tcx, trait_item.hir_id()); - then { - for param in trait_item.generics.params { - if param.is_impl_trait() { - let sp = trait_item.ident.span.with_hi(trait_item.ident.span.hi() + BytePos(1)); - report(cx, param, &trait_item.ident, trait_item.generics, sp.shrink_to_hi()); - } + && !item.vis_span.is_empty() // Is public + && !is_in_test_function(cx.tcx, trait_item.hir_id()) + { + for param in trait_item.generics.params { + if param.is_impl_trait() { + let sp = trait_item.ident.span.with_hi(trait_item.ident.span.hi() + BytePos(1)); + report(cx, param, &trait_item.ident, trait_item.generics, sp.shrink_to_hi()); } } } diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index 18f7368da..bf96c0d62 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -43,15 +43,13 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: // Body must be &(mut) .name // self_data is not necessarily self, to also lint sub-getters, etc… - let block_expr = if_chain! { - if let ExprKind::Block(block,_) = body.value.kind; - if block.stmts.is_empty(); - if let Some(block_expr) = block.expr; - then { - block_expr - } else { - return; - } + let block_expr = if let ExprKind::Block(block, _) = body.value.kind + && block.stmts.is_empty() + && let Some(block_expr) = block.expr + { + block_expr + } else { + return; }; let expr_span = block_expr.span; @@ -61,14 +59,12 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: } else { block_expr }; - let (self_data, used_ident) = if_chain! { - if let ExprKind::Field(self_data, ident) = expr.kind; - if ident.name.as_str() != name; - then { - (self_data, ident) - } else { - return; - } + let (self_data, used_ident) = if let ExprKind::Field(self_data, ident) = expr.kind + && ident.name.as_str() != name + { + (self_data, ident) + } else { + return; }; let mut used_field = None; diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 485235514..47db107d6 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -86,59 +86,60 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S } fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { - if_chain! { - if let Adt(adt, subst) = err_ty.kind(); - if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local(); - if let Some(hir::Node::Item(item)) = cx - .tcx - .hir() - .find_by_def_id(local_def_id); - if let hir::ItemKind::Enum(ref def, _) = item.kind; - then { - let variants_size = AdtVariantInfo::new(cx, *adt, subst); - if let Some((first_variant, variants)) = variants_size.split_first() - && first_variant.size >= large_err_threshold - { - span_lint_and_then( - cx, - RESULT_LARGE_ERR, - hir_ty_span, - "the `Err`-variant returned from this function is very large", - |diag| { - diag.span_label( - def.variants[first_variant.ind].span, - format!("the largest variant contains at least {} bytes", variants_size[0].size), - ); + if let Adt(adt, subst) = err_ty.kind() + && let Some(local_def_id) = err_ty + .ty_adt_def() + .expect("already checked this is adt") + .did() + .as_local() + && let Some(hir::Node::Item(item)) = cx.tcx.hir().find_by_def_id(local_def_id) + && let hir::ItemKind::Enum(ref def, _) = item.kind + { + let variants_size = AdtVariantInfo::new(cx, *adt, subst); + if let Some((first_variant, variants)) = variants_size.split_first() + && first_variant.size >= large_err_threshold + { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag| { + diag.span_label( + def.variants[first_variant.ind].span, + format!("the largest variant contains at least {} bytes", variants_size[0].size), + ); - for variant in variants { - if variant.size >= large_err_threshold { - let variant_def = &def.variants[variant.ind]; - diag.span_label( - variant_def.span, - format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size), - ); - } + for variant in variants { + if variant.size >= large_err_threshold { + let variant_def = &def.variants[variant.ind]; + diag.span_label( + variant_def.span, + format!( + "the variant `{}` contains at least {} bytes", + variant_def.ident, variant.size + ), + ); } - - diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); } - ); - } + + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); } - else { - let ty_size = approx_ty_size(cx, err_ty); - if ty_size >= large_err_threshold { - span_lint_and_then( - cx, - RESULT_LARGE_ERR, - hir_ty_span, - "the `Err`-variant returned from this function is very large", - |diag: &mut Diagnostic| { - diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); - diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); - }, - ); - } + } else { + let ty_size = approx_ty_size(cx, err_ty); + if ty_size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag: &mut Diagnostic| { + diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); } } } diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index e614a8f69..644b9cdae 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, SpanlessEq}; -use if_chain::if_chain; use rustc_errors::Diagnostic; use rustc_hir::intravisit::{self as visit, Visitor}; use rustc_hir::{Expr, ExprKind}; @@ -127,15 +126,13 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { } fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; - if path.ident.as_str() == "lock"; - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Mutex); - then { - Some(self_arg) - } else { - None - } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind + && path.ident.as_str() == "lock" + && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() + && is_type_diagnostic_item(cx, ty, sym::Mutex) + { + Some(self_arg) + } else { + None } } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index a1fcce18f..4545f6e42 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -13,8 +13,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; -use if_chain::if_chain; - use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; @@ -337,42 +335,38 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't } fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(fun, args) = e.kind; - if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind; - if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; - if let Some(ty_did) = ty_path.res.opt_def_id(); - then { - if self.target.ty() != self.maybe_typeck_results.unwrap().expr_ty(e) { - return; - } + if let ExprKind::Call(fun, args) = e.kind + && let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind + && let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind + && let Some(ty_did) = ty_path.res.opt_def_id() + { + if self.target.ty() != self.maybe_typeck_results.unwrap().expr_ty(e) { + return; + } - if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { - if method.ident.name == sym::new { - self.suggestions - .insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { - self.suggestions.insert( - e.span, - format!( - "HashMap::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), - ); - } - } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { - if method.ident.name == sym::new { - self.suggestions - .insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { - self.suggestions.insert( - e.span, - format!( - "HashSet::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), - ); - } + if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { + if method.ident.name == sym::new { + self.suggestions.insert(e.span, "HashMap::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashMap::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); + } + } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { + if method.ident.name == sym::new { + self.suggestions.insert(e.span, "HashSet::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashSet::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); } } } diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index 24f62490f..f2fac9a29 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -2,7 +2,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; -use if_chain::if_chain; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; @@ -40,42 +39,58 @@ declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]); impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let ExprKind::If(cond, then, None) = expr.kind; - if let ExprKind::DropTemps(expr1) = cond.kind; - if let Some((c, op_node, l)) = get_const(cx, expr1); - if let BinOpKind::Ne | BinOpKind::Lt = op_node; - if let ExprKind::Block(block, None) = then.kind; - if let Block { + if let ExprKind::If(cond, then, None) = expr.kind + && let ExprKind::DropTemps(expr1) = cond.kind + && let Some((c, op_node, l)) = get_const(cx, expr1) + && let BinOpKind::Ne | BinOpKind::Lt = op_node + && let ExprKind::Block(block, None) = then.kind + && let Block { stmts: - [Stmt - { kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), .. }], - expr: None, ..} | - Block { stmts: [], expr: Some(ex), ..} = block; - if let ExprKind::AssignOp(op1, target, value) = ex.kind; - let ty = cx.typeck_results().expr_ty(target); - if Some(c) == get_int_max(ty); - let ctxt = expr.span.ctxt(); - if ex.span.ctxt() == ctxt; - if expr1.span.ctxt() == ctxt; - if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target); - if BinOpKind::Add == op1.node; - if let ExprKind::Lit(lit) = value.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if block.expr.is_none(); - then { - let mut app = Applicability::MachineApplicable; - let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0; - let sugg = if let Some(parent) = get_parent_expr(cx, expr) - && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind - && else_.hir_id == expr.hir_id - { - format!("{{{code} = {code}.saturating_add(1); }}") - } else { - format!("{code} = {code}.saturating_add(1);") - }; - span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app); + [ + Stmt { + kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), + .. + }, + ], + expr: None, + .. } + | Block { + stmts: [], + expr: Some(ex), + .. + } = block + && let ExprKind::AssignOp(op1, target, value) = ex.kind + && let ty = cx.typeck_results().expr_ty(target) + && Some(c) == get_int_max(ty) + && let ctxt = expr.span.ctxt() + && ex.span.ctxt() == ctxt + && expr1.span.ctxt() == ctxt + && clippy_utils::SpanlessEq::new(cx).eq_expr(l, target) + && BinOpKind::Add == op1.node + && let ExprKind::Lit(lit) = value.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && block.expr.is_none() + { + let mut app = Applicability::MachineApplicable; + let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0; + let sugg = if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind + && else_.hir_id == expr.hir_id + { + format!("{{{code} = {code}.saturating_add(1); }}") + } else { + format!("{code} = {code}.saturating_add(1);") + }; + span_lint_and_sugg( + cx, + IMPLICIT_SATURATING_ADD, + expr.span, + "manual saturating add detected", + "use instead", + sugg, + app, + ); } } } diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 859404289..fc66f86ae 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; @@ -46,83 +45,76 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if expr.span.from_expansion() { return; } - if_chain! { - if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr); + if let Some(higher::If { cond, then, r#else: None }) = 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; + && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind // Ensure that the binary operator is >, !=, or < - if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; + && (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node) // Check if assign operation is done - if let Some(target) = subtracts_one(cx, then); + && let Some(target) = subtracts_one(cx, then) // Extracting out the variable name - if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind; - - then { - // Handle symmetric conditions in the if statement - let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { - if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_left, cond_right) - } else { - return; - } - } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { - if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_right, cond_left) - } else { - return; - } + && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind + { + // Handle symmetric conditions in the if statement + let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { + if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node { + (cond_left, cond_right) } else { return; - }; - - // Check if the variable in the condition statement is an integer - if !cx.typeck_results().expr_ty(cond_var).is_integral() { + } + } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { + if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node { + (cond_right, cond_left) + } else { return; } + } else { + return; + }; - // Get the variable name - let var_name = ares_path.segments[0].ident.name.as_str(); - match cond_num_val.kind { - ExprKind::Lit(cond_lit) => { - // Check if the constant is zero - if let LitKind::Int(0, _) = cond_lit.node { - if cx.typeck_results().expr_ty(cond_left).is_signed() { - } else { - print_lint_and_sugg(cx, var_name, expr); - }; - } - }, - ExprKind::Path(QPath::TypeRelative(_, name)) => { - if_chain! { - if name.ident.as_str() == "MIN"; - if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(const_id); - if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl - if cx.tcx.type_of(impl_id).instantiate_identity().is_integral(); - then { - print_lint_and_sugg(cx, var_name, expr) - } - } - }, - ExprKind::Call(func, []) => { - if_chain! { - if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind; - if name.ident.as_str() == "min_value"; - if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(func_id); - if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl - if cx.tcx.type_of(impl_id).instantiate_identity().is_integral(); - then { - print_lint_and_sugg(cx, var_name, expr) - } - } - }, - _ => (), - } + // Check if the variable in the condition statement is an integer + if !cx.typeck_results().expr_ty(cond_var).is_integral() { + return; + } + + // Get the variable name + let var_name = ares_path.segments[0].ident.name.as_str(); + match cond_num_val.kind { + ExprKind::Lit(cond_lit) => { + // Check if the constant is zero + if let LitKind::Int(0, _) = cond_lit.node { + if cx.typeck_results().expr_ty(cond_left).is_signed() { + } else { + print_lint_and_sugg(cx, var_name, expr); + }; + } + }, + ExprKind::Path(QPath::TypeRelative(_, name)) => { + if name.ident.as_str() == "MIN" + && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(const_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + ExprKind::Call(func, []) => { + if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind + && name.ident.as_str() == "min_value" + && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(func_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + _ => (), } } } @@ -135,18 +127,14 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target) }, ExprKind::Assign(target, value, _) => { - if_chain! { - if let ExprKind::Binary(ref op1, left1, right1) = value.kind; - if BinOpKind::Sub == op1.node; - - if SpanlessEq::new(cx).eq_expr(left1, target); - - if is_integer_literal(right1, 1); - then { - Some(target) - } else { - None - } + if let ExprKind::Binary(ref op1, left1, right1) = value.kind + && BinOpKind::Sub == op1.node + && SpanlessEq::new(cx).eq_expr(left1, target) + && is_integer_literal(right1, 1) + { + Some(target) + } else { + None } }, _ => None, diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index a84f7351a..f6e1281a2 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind}; @@ -66,54 +65,53 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::Struct(qpath, fields, base) = expr.kind; - let ty = cx.typeck_results().expr_ty(expr); - if let Some(adt_def) = ty.ty_adt_def(); - if adt_def.is_struct(); - if let Some(variant) = adt_def.variants().iter().next(); - if fields.iter().all(|f| f.is_shorthand); - then { - let mut def_order_map = FxHashMap::default(); - for (idx, field) in variant.fields.iter().enumerate() { - def_order_map.insert(field.name, idx); - } - - if is_consistent_order(fields, &def_order_map) { - return; - } - - let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); - ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); - - let mut fields_snippet = String::new(); - let (last_ident, idents) = ordered_fields.split_last().unwrap(); - for ident in idents { - let _: fmt::Result = write!(fields_snippet, "{ident}, "); - } - fields_snippet.push_str(&last_ident.to_string()); - - let base_snippet = if let Some(base) = base { - format!(", ..{}", snippet(cx, base.span, "..")) - } else { - String::new() - }; - - let sugg = format!("{} {{ {fields_snippet}{base_snippet} }}", - snippet(cx, qpath.span(), ".."), - ); - - span_lint_and_sugg( - cx, - INCONSISTENT_STRUCT_CONSTRUCTOR, - expr.span, - "struct constructor field order is inconsistent with struct definition field order", - "try", - sugg, - Applicability::MachineApplicable, - ) + if !expr.span.from_expansion() + && let ExprKind::Struct(qpath, fields, base) = expr.kind + && let ty = cx.typeck_results().expr_ty(expr) + && let Some(adt_def) = ty.ty_adt_def() + && adt_def.is_struct() + && let Some(variant) = adt_def.variants().iter().next() + && fields.iter().all(|f| f.is_shorthand) + { + let mut def_order_map = FxHashMap::default(); + for (idx, field) in variant.fields.iter().enumerate() { + def_order_map.insert(field.name, idx); } + + if is_consistent_order(fields, &def_order_map) { + return; + } + + let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); + ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); + + let mut fields_snippet = String::new(); + let (last_ident, idents) = ordered_fields.split_last().unwrap(); + for ident in idents { + let _: fmt::Result = write!(fields_snippet, "{ident}, "); + } + fields_snippet.push_str(&last_ident.to_string()); + + let base_snippet = if let Some(base) = base { + format!(", ..{}", snippet(cx, base.span, "..")) + } else { + String::new() + }; + + let sugg = format!( + "{} {{ {fields_snippet}{base_snippet} }}", + snippet(cx, qpath.span(), ".."), + ); + + span_lint_and_sugg( + cx, + INCONSISTENT_STRUCT_CONSTRUCTOR, + expr.span, + "struct constructor field order is inconsistent with struct definition field order", + "try", + sugg, + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index c2f1f18e3..fa6536db7 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::ty::is_copy; use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; -use if_chain::if_chain; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -70,20 +69,17 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); - if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); - if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); - if self.msrv.meets(msrvs::SLICE_PATTERNS); - - let found_slices = find_slice_values(cx, let_pat); - if !found_slices.is_empty(); - let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then); - if !filtered_slices.is_empty(); - then { - for slice in filtered_slices.values() { - lint_slice(cx, slice); - } + if (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) + && let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) + && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) + && self.msrv.meets(msrvs::SLICE_PATTERNS) + && let found_slices = find_slice_values(cx, let_pat) + && !found_slices.is_empty() + && let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then) + && !filtered_slices.is_empty() + { + for slice in filtered_slices.values() { + lint_slice(cx, slice); } } } @@ -245,28 +241,26 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { max_suggested_slice, } = *self; - if_chain! { + if let Some(use_info) = slice_lint_info.get_mut(&local_id) // Check if this is even a local we're interested in - if let Some(use_info) = slice_lint_info.get_mut(&local_id); - let map = cx.tcx.hir(); + && let map = cx.tcx.hir() // Checking for slice indexing - let parent_id = map.parent_id(expr.hir_id); - if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id); - if let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind; - if let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr); - if let Ok(index_value) = index_value.try_into(); - if index_value < max_suggested_slice; + && let parent_id = map.parent_id(expr.hir_id) + && let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id) + && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind + && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr) + && let Ok(index_value) = index_value.try_into() + && index_value < max_suggested_slice // Make sure that this slice index is read only - let maybe_addrof_id = map.parent_id(parent_id); - if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id); - if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind; - then { - use_info.index_use.push((index_value, map.span(parent_expr.hir_id))); - return; - } + && let maybe_addrof_id = map.parent_id(parent_id) + && let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id) + && let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind + { + use_info.index_use.push((index_value, map.span(parent_expr.hir_id))); + return; } // The slice was used for something other than indexing diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 32b2cb438..8e84c7366 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -89,27 +89,17 @@ impl LateLintPass<'_> for InstantSubtraction { rhs, ) = expr.kind { - if_chain! { - if is_instant_now_call(cx, lhs); - - if is_an_instant(cx, rhs); - if let Some(sugg) = Sugg::hir_opt(cx, rhs); - - then { - print_manual_instant_elapsed_sugg(cx, expr, sugg) - } else { - if_chain! { - if !expr.span.from_expansion(); - if self.msrv.meets(msrvs::TRY_FROM); - - if is_an_instant(cx, lhs); - if is_a_duration(cx, rhs); - - then { - print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr) - } - } - } + if is_instant_now_call(cx, lhs) + && is_an_instant(cx, rhs) + && let Some(sugg) = Sugg::hir_opt(cx, rhs) + { + print_manual_instant_elapsed_sugg(cx, expr, sugg); + } else if !expr.span.from_expansion() + && self.msrv.meets(msrvs::TRY_FROM) + && is_an_instant(cx, lhs) + && is_a_duration(cx, rhs) + { + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } } } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index a4f3d4983..7db088f98 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,43 +46,40 @@ impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if_chain! { - if !item.span.from_expansion(); - if let ItemKind::Const(_, generics, _) = &item.kind; + if !item.span.from_expansion() + && let ItemKind::Const(_, generics, _) = &item.kind // Since static items may not have generics, skip generic const items. // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it // doesn't account for empty where-clauses that only consist of keyword `where` IINM. - if generics.params.is_empty() && !generics.has_where_clause_predicates; - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - if let ty::Array(element_type, cst) = ty.kind(); - if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); - if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx); - if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()); - if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size); - - then { - let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); - let sugg_span = Span::new( - hi_pos - BytePos::from_usize("const".len()), - hi_pos, - item.span.ctxt(), - item.span.parent(), - ); - span_lint_and_then( - cx, - LARGE_CONST_ARRAYS, - item.span, - "large array defined as const", - |diag| { - diag.span_suggestion( - sugg_span, - "make this a static item", - "static", - Applicability::MachineApplicable, - ); - } - ); - } + && generics.params.is_empty() && !generics.has_where_clause_predicates + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let ty::Array(element_type, cst) = ty.kind() + && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() + && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) + && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) + && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) + { + let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); + let sugg_span = Span::new( + hi_pos - BytePos::from_usize("const".len()), + hi_pos, + item.span.ctxt(), + item.span.parent(), + ); + span_lint_and_then( + cx, + LARGE_CONST_ARRAYS, + item.span, + "large array defined as const", + |diag| { + diag.span_suggestion( + sugg_span, + "make this a static item", + "static", + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 566901de3..902b72ba5 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -50,37 +50,35 @@ impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); impl LateLintPass<'_> for LargeIncludeFile { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if_chain! { - if let Some(macro_call) = root_macro_call_first_node(cx, expr); - if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id); - if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) - || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id); - if let ExprKind::Lit(lit) = &expr.kind; - then { - let len = match &lit.node { - // include_bytes - LitKind::ByteStr(bstr, _) => bstr.len(), - // include_str - LitKind::Str(sym, _) => sym.as_str().len(), - _ => return, - }; + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id) + && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) + || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) + && let ExprKind::Lit(lit) = &expr.kind + { + let len = match &lit.node { + // include_bytes + LitKind::ByteStr(bstr, _) => bstr.len(), + // include_str + LitKind::Str(sym, _) => sym.as_str().len(), + _ => return, + }; - if len as u64 <= self.max_file_size { - return; - } - - span_lint_and_note( - cx, - LARGE_INCLUDE_FILE, - expr.span, - "attempted to include a large file", - None, - &format!( - "the configuration allows a maximum size of {} bytes", - self.max_file_size - ), - ); + if len as u64 <= self.max_file_size { + return; } + + span_lint_and_note( + cx, + LARGE_INCLUDE_FILE, + expr.span, + "attempted to include a large file", + None, + &format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + ), + ); } } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 08f095859..305dd2c7f 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -131,37 +130,33 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { - if_chain! { - if item.ident.name == sym::len; - if let ImplItemKind::Fn(sig, _) = &item.kind; - if sig.decl.implicit_self.has_implicit_self(); - if sig.decl.inputs.len() == 1; - if cx.effective_visibilities.is_exported(item.owner_id.def_id); - if matches!(sig.decl.output, FnRetTy::Return(_)); - if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); - if imp.of_trait.is_none(); - if let TyKind::Path(ty_path) = &imp.self_ty.kind; - if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id(); - if let Some(local_id) = ty_id.as_local(); - let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); - if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); - if let Some(output) = parse_len_output( - cx, - cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder() - ); - then { - let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { - Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), - Some(Node::Item(x)) => match x.kind { - ItemKind::Struct(..) => (x.ident.name, "struct"), - ItemKind::Enum(..) => (x.ident.name, "enum"), - ItemKind::Union(..) => (x.ident.name, "union"), - _ => (x.ident.name, "type"), - } - _ => return, - }; - check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind) - } + if item.ident.name == sym::len + && let ImplItemKind::Fn(sig, _) = &item.kind + && sig.decl.implicit_self.has_implicit_self() + && sig.decl.inputs.len() == 1 + && cx.effective_visibilities.is_exported(item.owner_id.def_id) + && matches!(sig.decl.output, FnRetTy::Return(_)) + && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()) + && imp.of_trait.is_none() + && let TyKind::Path(ty_path) = &imp.self_ty.kind + && let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id() + && let Some(local_id) = ty_id.as_local() + && let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id) + && !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id) + && let Some(output) = + parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()) + { + let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { + Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), + Some(Node::Item(x)) => match x.kind { + ItemKind::Struct(..) => (x.ident.name, "struct"), + ItemKind::Enum(..) => (x.ident.name, "enum"), + ItemKind::Union(..) => (x.ident.name, "union"), + _ => (x.ident.name, "type"), + }, + _ => return, + }; + check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind); } } diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 2f6f36c39..da269ec61 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local_id; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{BindingAnnotation, Mutability}; @@ -61,76 +60,85 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { let mut it = block.stmts.iter().peekable(); while let Some(stmt) = it.next() { - if_chain! { - if let Some(expr) = it.peek(); - 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(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind; - if !is_local_used(cx, *cond, canonical_id); - if let hir::ExprKind::Block(then, _) = then.kind; - if let Some(value) = check_assign(cx, canonical_id, then); - if !is_local_used(cx, value, canonical_id); - then { - let span = stmt.span.to(if_.span); + if let Some(expr) = it.peek() + && let hir::StmtKind::Local(local) = stmt.kind + && let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind + && let hir::StmtKind::Expr(if_) = expr.kind + && let hir::ExprKind::If( + hir::Expr { + kind: hir::ExprKind::DropTemps(cond), + .. + }, + then, + else_, + ) = if_.kind + && !is_local_used(cx, *cond, canonical_id) + && let hir::ExprKind::Block(then, _) = then.kind + && let Some(value) = check_assign(cx, canonical_id, then) + && !is_local_used(cx, value, canonical_id) + { + let span = stmt.span.to(if_.span); - let has_interior_mutability = !cx.typeck_results().node_type(canonical_id).is_freeze( - cx.tcx, - cx.param_env, - ); - if has_interior_mutability { return; } + let has_interior_mutability = !cx + .typeck_results() + .node_type(canonical_id) + .is_freeze(cx.tcx, cx.param_env); + if has_interior_mutability { + return; + } - 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) - } else if let Some(default) = local.init { - (true, default) - } else { - continue; - } + 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) + } else if let Some(default) = local.init { + (true, default) } else { continue; } - } else if let Some(default) = local.init { - (false, default) } else { continue; - }; + } + } else if let Some(default) = local.init { + (false, default) + } else { + continue; + }; - let mutability = match mode { - BindingAnnotation(_, Mutability::Mut) => " ", - _ => "", - }; + let mutability = match mode { + BindingAnnotation(_, Mutability::Mut) => " ", + _ => "", + }; - // FIXME: this should not suggest `mut` if we can detect that the variable is not - // use mutably after the `if` + // FIXME: this should not suggest `mut` if we can detect that the variable is not + // use mutably after the `if` - let sug = format!( - "let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", - name=ident.name, - cond=snippet(cx, cond.span, "_"), - then=if then.stmts.len() > 1 { " ..;" } else { "" }, - else=if default_multi_stmts { " ..;" } else { "" }, - value=snippet(cx, value.span, ""), - default=snippet(cx, default.span, ""), - ); - span_lint_and_then(cx, - USELESS_LET_IF_SEQ, - span, - "`if _ { .. } else { .. }` is an expression", - |diag| { - diag.span_suggestion( - span, - "it is more idiomatic to write", - sug, - Applicability::HasPlaceholders, - ); - if !mutability.is_empty() { - diag.note("you might not need `mut` at all"); - } - }); - } + let sug = format!( + "let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", + name=ident.name, + cond=snippet(cx, cond.span, "_"), + then=if then.stmts.len() > 1 { " ..;" } else { "" }, + else=if default_multi_stmts { " ..;" } else { "" }, + value=snippet(cx, value.span, ""), + default=snippet(cx, default.span, ""), + ); + span_lint_and_then( + cx, + USELESS_LET_IF_SEQ, + span, + "`if _ { .. } else { .. }` is an expression", + |diag| { + diag.span_suggestion( + span, + "it is more idiomatic to write", + sug, + Applicability::HasPlaceholders, + ); + if !mutability.is_empty() { + diag.note("you might not need `mut` at all"); + } + }, + ); } } } @@ -141,20 +149,23 @@ fn check_assign<'tcx>( decl: hir::HirId, block: &'tcx hir::Block<'_>, ) -> Option<&'tcx hir::Expr<'tcx>> { - if_chain! { - if block.expr.is_none(); - if let Some(expr) = block.stmts.iter().last(); - if let hir::StmtKind::Semi(expr) = expr.kind; - if let hir::ExprKind::Assign(var, value, _) = expr.kind; - if path_to_local_id(var, decl); - then { - if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) { - None - } else { - Some(value) - } - } else { + if block.expr.is_none() + && let Some(expr) = block.stmts.iter().last() + && let hir::StmtKind::Semi(expr) = expr.kind + && let hir::ExprKind::Assign(var, value, _) = expr.kind + && path_to_local_id(var, decl) + { + if block + .stmts + .iter() + .take(block.stmts.len() - 1) + .any(|stmt| is_local_used(cx, stmt, decl)) + { None + } else { + Some(value) } + } else { + None } } diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 79d728a02..d4f410de9 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -27,27 +27,25 @@ declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); impl LateLintPass<'_> for UnderscoreTyped { fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { - if_chain! { - if !in_external_macro(cx.tcx.sess, local.span); - if let Some(ty) = local.ty; // Ensure that it has a type defined - if let TyKind::Infer = &ty.kind; // that type is '_' - if local.span.eq_ctxt(ty.span); - then { - // NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized, - // this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty` - if snippet(cx, ty.span, "_").trim() != "_" { - return; - } - - span_lint_and_help( - cx, - LET_WITH_TYPE_UNDERSCORE, - local.span, - "variable declared with type underscore", - Some(ty.span.with_lo(local.pat.span.hi())), - "remove the explicit type `_` declaration" - ) + if !in_external_macro(cx.tcx.sess, local.span) + && let Some(ty) = local.ty // Ensure that it has a type defined + && let TyKind::Infer = &ty.kind // that type is '_' + && local.span.eq_ctxt(ty.span) + { + // NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized, + // this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty` + if snippet(cx, ty.span, "_").trim() != "_" { + return; } + + span_lint_and_help( + cx, + LET_WITH_TYPE_UNDERSCORE, + local.span, + "variable declared with type underscore", + Some(ty.span.with_lo(local.pat.span.hi())), + "remove the explicit type `_` declaration", + ); }; } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ab978a677..e5f8bd5c4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -521,7 +521,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| { Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new()) }); - store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle)); store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); store.register_late_pass(|_| { Box::::default() diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 4b89c0fa3..b190955d5 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -310,20 +310,17 @@ fn elision_suggestions( // elision doesn't work for explicit self types, see rust-lang/rust#69064 fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { - if_chain! { - if let Some(ident) = ident; - if ident.name == kw::SelfLower; - if !func.implicit_self.has_implicit_self(); + if let Some(ident) = ident + && ident.name == kw::SelfLower + && !func.implicit_self.has_implicit_self() + && let Some(self_ty) = func.inputs.first() + { + let mut visitor = RefVisitor::new(cx); + visitor.visit_ty(self_ty); - if let Some(self_ty) = func.inputs.first(); - then { - let mut visitor = RefVisitor::new(cx); - visitor.visit_ty(self_ty); - - !visitor.all_lts().is_empty() - } else { - false - } + !visitor.all_lts().is_empty() + } else { + false } } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 2c14bb72a..8f34a9b1f 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; -use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; @@ -255,56 +254,48 @@ impl LiteralDigitGrouping { } fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { - if_chain! { - if let Some(src) = snippet_opt(cx, span); - if let Ok(lit_kind) = LitKind::from_token_lit(lit); - if let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); - then { - if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) { - return; - } + if let Some(src) = snippet_opt(cx, span) + && let Ok(lit_kind) = LitKind::from_token_lit(lit) + && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) + { + if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) { + return; + } - if Self::is_literal_uuid_formatted(&num_lit) { - return; - } + if Self::is_literal_uuid_formatted(&num_lit) { + return; + } - let result = (|| { + let result = (|| { + let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?; + if let Some(fraction) = num_lit.fraction { + let fractional_group_size = + Self::get_group_size(fraction.rsplit('_'), num_lit.radix, self.lint_fraction_readability)?; - let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?; - if let Some(fraction) = num_lit.fraction { - let fractional_group_size = Self::get_group_size( - fraction.rsplit('_'), - num_lit.radix, - self.lint_fraction_readability)?; - - let consistent = Self::parts_consistent(integral_group_size, - fractional_group_size, - num_lit.integer.len(), - fraction.len()); - if !consistent { - return Err(WarningType::InconsistentDigitGrouping); - }; - } - - Ok(()) - })(); - - - if let Err(warning_type) = result { - let should_warn = match warning_type { - | WarningType::UnreadableLiteral - | WarningType::InconsistentDigitGrouping - | WarningType::UnusualByteGroupings - | WarningType::LargeDigitGroups => { - !span.from_expansion() - } - WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => { - true - } + let consistent = Self::parts_consistent( + integral_group_size, + fractional_group_size, + num_lit.integer.len(), + fraction.len(), + ); + if !consistent { + return Err(WarningType::InconsistentDigitGrouping); }; - if should_warn { - warning_type.display(num_lit.format(), cx, span); - } + } + + Ok(()) + })(); + + if let Err(warning_type) = result { + let should_warn = match warning_type { + WarningType::UnreadableLiteral + | WarningType::InconsistentDigitGrouping + | WarningType::UnusualByteGroupings + | WarningType::LargeDigitGroups => !span.from_expansion(), + WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => true, + }; + if should_warn { + warning_type.display(num_lit.format(), cx, span); } } } @@ -478,20 +469,18 @@ impl DecimalLiteralRepresentation { } fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // Lint integral literals. - if_chain! { - if let Ok(lit_kind) = LitKind::from_token_lit(lit); - if let LitKind::Int(val, _) = lit_kind; - if let Some(src) = snippet_opt(cx, span); - if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); - if num_lit.radix == Radix::Decimal; - if val >= u128::from(self.threshold); - then { - let hex = format!("{val:#X}"); - let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); - let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| { - warning_type.display(num_lit.format(), cx, span); - }); - } + if let Ok(lit_kind) = LitKind::from_token_lit(lit) + && let LitKind::Int(val, _) = lit_kind + && let Some(src) = snippet_opt(cx, span) + && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) + && num_lit.radix == Radix::Decimal + && val >= u128::from(self.threshold) + { + let hex = format!("{val:#X}"); + let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); + let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| { + warning_type.display(num_lit.format(), cx, span); + }); } } diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 1953ee8a7..277062a84 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -2,7 +2,6 @@ use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{get_enclosing_block, is_integer_const}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; use rustc_hir::{Expr, Pat}; @@ -30,59 +29,57 @@ pub(super) fn check<'tcx>( let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); walk_block(&mut initialize_visitor, block); - if_chain! { - if let Some((name, ty, initializer)) = initialize_visitor.get_result(); - if is_integer_const(cx, initializer, 0); - then { - let mut applicability = Applicability::MaybeIncorrect; - let span = expr.span.with_hi(arg.span.hi()); + if let Some((name, ty, initializer)) = initialize_visitor.get_result() + && is_integer_const(cx, initializer, 0) + { + let mut applicability = Applicability::MaybeIncorrect; + let span = expr.span.with_hi(arg.span.hi()); - let int_name = match ty.map(Ty::kind) { - // usize or inferred - Some(ty::Uint(UintTy::Usize)) | None => { - span_lint_and_sugg( - cx, - EXPLICIT_COUNTER_LOOP, - span, - &format!("the variable `{name}` is used as a loop counter"), - "consider using", - format!( - "for ({name}, {}) in {}.enumerate()", - snippet_with_applicability(cx, pat.span, "item", &mut applicability), - make_iterator_snippet(cx, arg, &mut applicability), - ), - applicability, - ); - return; - } - Some(ty::Int(int_ty)) => int_ty.name_str(), - Some(ty::Uint(uint_ty)) => uint_ty.name_str(), - _ => return, - }; + let int_name = match ty.map(Ty::kind) { + // usize or inferred + Some(ty::Uint(UintTy::Usize)) | None => { + span_lint_and_sugg( + cx, + EXPLICIT_COUNTER_LOOP, + span, + &format!("the variable `{name}` is used as a loop counter"), + "consider using", + format!( + "for ({name}, {}) in {}.enumerate()", + snippet_with_applicability(cx, pat.span, "item", &mut applicability), + make_iterator_snippet(cx, arg, &mut applicability), + ), + applicability, + ); + return; + }, + Some(ty::Int(int_ty)) => int_ty.name_str(), + Some(ty::Uint(uint_ty)) => uint_ty.name_str(), + _ => return, + }; - span_lint_and_then( - cx, - EXPLICIT_COUNTER_LOOP, - span, - &format!("the variable `{name}` is used as a loop counter"), - |diag| { - diag.span_suggestion( - span, - "consider using", - format!( - "for ({name}, {}) in (0_{int_name}..).zip({})", - snippet_with_applicability(cx, pat.span, "item", &mut applicability), - make_iterator_snippet(cx, arg, &mut applicability), - ), - applicability, - ); + span_lint_and_then( + cx, + EXPLICIT_COUNTER_LOOP, + span, + &format!("the variable `{name}` is used as a loop counter"), + |diag| { + diag.span_suggestion( + span, + "consider using", + format!( + "for ({name}, {}) in (0_{int_name}..).zip({})", + snippet_with_applicability(cx, pat.span, "item", &mut applicability), + make_iterator_snippet(cx, arg, &mut applicability), + ), + applicability, + ); - diag.note(format!( - "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" - )); - }, - ); - } + diag.note(format!( + "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" + )); + }, + ); } } } diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index 0aaa66e6b..c6c16f467 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::lang_items::LangItem; @@ -23,77 +22,79 @@ pub(super) fn check<'tcx>( let inner_expr = peel_blocks_with_stmt(body); // Check for the specific case that the result is returned and optimize suggestion for that (more // cases can be added later) - if_chain! { - if let Some(higher::If { cond, then, r#else: None, }) = higher::If::hir(inner_expr); - if let Some(binding_id) = get_binding(pat); - if let ExprKind::Block(block, _) = then.kind; - if let [stmt] = block.stmts; - if let StmtKind::Semi(semi) = stmt.kind; - if let ExprKind::Ret(Some(ret_value)) = semi.kind; - if let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind; - if is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome); - if path_res(cx, inner_ret) == Res::Local(binding_id); - if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr); - then { - let mut applicability = Applicability::MachineApplicable; - let mut snippet = make_iterator_snippet(cx, arg, &mut applicability); - // Checks if `pat` is a single reference to a binding (`&x`) - let is_ref_to_binding = - matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..))); - // If `pat` is not a binding or a reference to a binding (`x` or `&x`) - // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`) - if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) { - snippet.push_str( - &format!( - ".map(|{}| {})", - snippet_with_applicability(cx, pat.span, "..", &mut applicability), - snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), - )[..], - ); - } - let ty = cx.typeck_results().expr_ty(inner_ret); - if cx.tcx.lang_items().copy_trait().map_or(false, |id| implements_trait(cx, ty, id, &[])) { - snippet.push_str( - &format!( - ".find(|{}{}| {})", - "&".repeat(1 + usize::from(is_ref_to_binding)), - snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), - snippet_with_applicability(cx, cond.span, "..", &mut applicability), - )[..], - ); - if is_ref_to_binding { - snippet.push_str(".copied()"); - } - } else { - applicability = Applicability::MaybeIncorrect; - snippet.push_str( - &format!( - ".find(|{}| {})", - snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), - snippet_with_applicability(cx, cond.span, "..", &mut applicability), - )[..], - ); - } - // Extends to `last_stmt` to include semicolon in case of `return None;` - let lint_span = span.to(last_stmt.span).to(last_ret.span); - span_lint_and_then( - cx, - MANUAL_FIND, - lint_span, - "manual implementation of `Iterator::find`", - |diag| { - if applicability == Applicability::MaybeIncorrect { - diag.note("you may need to dereference some variables"); - } - diag.span_suggestion( - lint_span, - "replace with an iterator", - snippet, - applicability, - ); - }, + if let Some(higher::If { + cond, + then, + r#else: None, + }) = higher::If::hir(inner_expr) + && let Some(binding_id) = get_binding(pat) + && let ExprKind::Block(block, _) = then.kind + && let [stmt] = block.stmts + && let StmtKind::Semi(semi) = stmt.kind + && let ExprKind::Ret(Some(ret_value)) = semi.kind + && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind + && is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome) + && path_res(cx, inner_ret) == Res::Local(binding_id) + && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) + { + let mut applicability = Applicability::MachineApplicable; + let mut snippet = make_iterator_snippet(cx, arg, &mut applicability); + // Checks if `pat` is a single reference to a binding (`&x`) + let is_ref_to_binding = + matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..))); + // If `pat` is not a binding or a reference to a binding (`x` or `&x`) + // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`) + if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) { + snippet.push_str( + &format!( + ".map(|{}| {})", + snippet_with_applicability(cx, pat.span, "..", &mut applicability), + snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), + )[..], ); } + let ty = cx.typeck_results().expr_ty(inner_ret); + if cx + .tcx + .lang_items() + .copy_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + snippet.push_str( + &format!( + ".find(|{}{}| {})", + "&".repeat(1 + usize::from(is_ref_to_binding)), + snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), + snippet_with_applicability(cx, cond.span, "..", &mut applicability), + )[..], + ); + if is_ref_to_binding { + snippet.push_str(".copied()"); + } + } else { + applicability = Applicability::MaybeIncorrect; + snippet.push_str( + &format!( + ".find(|{}| {})", + snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), + snippet_with_applicability(cx, cond.span, "..", &mut applicability), + )[..], + ); + } + // Extends to `last_stmt` to include semicolon in case of `return None;` + let lint_span = span.to(last_stmt.span).to(last_ret.span); + span_lint_and_then( + cx, + MANUAL_FIND, + lint_span, + "manual implementation of `Iterator::find`", + |diag| { + if applicability == Applicability::MaybeIncorrect { + diag.note("you may need to dereference some variables"); + } + diag.span_suggestion(lint_span, "replace with an iterator", snippet, applicability); + }, + ); } } @@ -124,34 +125,30 @@ fn last_stmt_and_ret<'tcx>( if let Some(ret) = block.expr { return Some((last_stmt, ret)); } - if_chain! { - if let [.., snd_last, _] = block.stmts; - if let StmtKind::Semi(last_expr) = last_stmt.kind; - if let ExprKind::Ret(Some(ret)) = last_expr.kind; - then { - return Some((snd_last, ret)); - } + if let [.., snd_last, _] = block.stmts + && let StmtKind::Semi(last_expr) = last_stmt.kind + && let ExprKind::Ret(Some(ret)) = last_expr.kind + { + return Some((snd_last, ret)); } } None } let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id); - if_chain! { + if let Some((node_hir, Node::Stmt(..))) = parent_iter.next() // This should be the loop - if let Some((node_hir, Node::Stmt(..))) = parent_iter.next(); // This should be the function body - if let Some((_, Node::Block(block))) = parent_iter.next(); - if let Some((last_stmt, last_ret)) = extract(block); - if last_stmt.hir_id == node_hir; - if is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone); - if let Some((_, Node::Expr(_block))) = parent_iter.next(); + && let Some((_, Node::Block(block))) = parent_iter.next() + && let Some((last_stmt, last_ret)) = extract(block) + && last_stmt.hir_id == node_hir + && is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone) + && let Some((_, Node::Expr(_block))) = parent_iter.next() // This includes the function header - if let Some((_, func)) = parent_iter.next(); - if func.fn_kind().is_some(); - then { - Some((block.stmts.last().unwrap(), last_ret)) - } else { - None - } + && let Some((_, func)) = parent_iter.next() + && func.fn_kind().is_some() + { + Some((block.stmts.last().unwrap(), last_ret)) + } else { + None } } diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 559a2c03f..de4e13bd8 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -3,7 +3,6 @@ use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; @@ -21,66 +20,51 @@ pub(super) fn check<'tcx>( span: Span, ) { let inner_expr = peel_blocks_with_stmt(body); - if_chain! { - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None }) - = higher::IfLet::hir(cx, inner_expr); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None }) + = higher::IfLet::hir(cx, 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(let_expr, pat_hir_id); + && let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind + && 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, _, _) = let_pat.kind; - if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id); - if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); - let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id); - let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id); - if some_ctor || ok_ctor; + && let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind + && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + && let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id) + && let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id) + && (some_ctor || ok_ctor) // Ensure expr in `if let` is not used afterwards - if !is_local_used(cx, if_then, pat_hir_id); - then { - let if_let_type = if some_ctor { "Some" } else { "Ok" }; - // Prepare the error message - let msg = format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used"); + && !is_local_used(cx, if_then, pat_hir_id) + { + let if_let_type = if some_ctor { "Some" } else { "Ok" }; + // Prepare the error message + let msg = + format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used"); - // 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(let_expr).kind() { - ty::Ref(_, inner, _) => match inner.kind() { - ty::Ref(..) => ".copied()", - _ => "" - } - _ => "" - }; + // 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(let_expr).kind() { + ty::Ref(_, inner, _) => match inner.kind() { + ty::Ref(..) => ".copied()", + _ => "", + }, + _ => "", + }; - let sugg = format!("{arg_snippet}{copied}.flatten()"); + let sugg = format!("{arg_snippet}{copied}.flatten()"); - // If suggestion is not a one-liner, it won't be shown inline within the error message. In that case, - // it will be shown in the extra `help` message at the end, which is why the first `help_msg` needs - // to refer to the correct relative position of the suggestion. - let help_msg = if sugg.contains('\n') { - "remove the `if let` statement in the for loop and then..." - } else { - "...and remove the `if let` statement in the for loop" - }; + // If suggestion is not a one-liner, it won't be shown inline within the error message. In that + // case, it will be shown in the extra `help` message at the end, which is why the first + // `help_msg` needs to refer to the correct relative position of the suggestion. + let help_msg = if sugg.contains('\n') { + "remove the `if let` statement in the for loop and then..." + } else { + "...and remove the `if let` statement in the for loop" + }; - span_lint_and_then( - cx, - MANUAL_FLATTEN, - span, - &msg, - |diag| { - diag.span_suggestion( - arg.span, - "try", - sugg, - applicability, - ); - diag.span_help( - inner_expr.span, - help_msg, - ); - } - ); - } + span_lint_and_then(cx, MANUAL_FLATTEN, span, &msg, |diag| { + diag.span_suggestion(arg.span, "try", sugg, applicability); + diag.span_help(inner_expr.span, help_msg); + }); } } diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index d3fd0e863..89f70519c 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -4,7 +4,6 @@ use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir::intravisit::walk_block; @@ -59,22 +58,31 @@ pub(super) fn check<'tcx>( .map(|o| { o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); - if_chain! { - if let ExprKind::Index(base_left, idx_left, _) = lhs.kind; - if let ExprKind::Index(base_right, idx_right, _) = rhs.kind; - if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)); - if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some(); - if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts); - if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts); + if let ExprKind::Index(base_left, idx_left, _) = lhs.kind + && let ExprKind::Index(base_right, idx_right, _) = rhs.kind + && let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)) + && get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some() + && let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts) + && let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts) // Source and destination must be different - if path_to_local(base_left) != path_to_local(base_right); - then { - Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, - IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) - } else { - None - } + && path_to_local(base_left) != path_to_local(base_right) + { + Some(( + ty, + IndexExpr { + base: base_left, + idx: start_left, + idx_offset: offset_left, + }, + IndexExpr { + base: base_right, + idx: start_right, + idx_offset: offset_right, + }, + )) + } else { + None } }) }) @@ -118,23 +126,19 @@ fn build_manual_memcpy_suggestion<'tcx>( } let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { - if_chain! { - if let ExprKind::MethodCall(method, recv, [], _) = end.kind; - if method.ident.name == sym::len; - if path_to_local(recv) == path_to_local(base); - then { - if sugg.to_string() == end_str { - sugg::EMPTY.into() - } else { - sugg - } + if let ExprKind::MethodCall(method, recv, [], _) = end.kind + && method.ident.name == sym::len + && path_to_local(recv) == path_to_local(base) + { + if sugg.to_string() == end_str { + sugg::EMPTY.into() } else { - match limits { - ast::RangeLimits::Closed => { - sugg + &sugg::ONE.into() - }, - ast::RangeLimits::HalfOpen => sugg, - } + sugg + } + } else { + match limits { + ast::RangeLimits::Closed => sugg + &sugg::ONE.into(), + ast::RangeLimits::HalfOpen => sugg, } } }; @@ -331,10 +335,12 @@ fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Opti } fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { - if_chain! { - if let ExprKind::MethodCall(method, arg, [], _) = expr.kind; - if method.ident.name == sym::clone; - then { arg } else { expr } + if let ExprKind::MethodCall(method, arg, [], _) = expr.kind + && method.ident.name == sym::clone + { + arg + } else { + expr } } diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index 7b7d19c75..e405829b2 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -31,26 +31,30 @@ fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Block(Block { stmts: [], expr: None, ..}, _) = body.kind; - if let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind; - if [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name); - if let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind(); - if cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()); - then { - span_lint_and_sugg( - cx, - MISSING_SPIN_LOOP, - body.span, - "busy-waiting loop should at least have a spin loop hint", - "try", - (if is_no_std_crate(cx) { - "{ core::hint::spin_loop() }" - } else { - "{ std::hint::spin_loop() }" - }).into(), - Applicability::MachineApplicable - ); - } + if let ExprKind::Block( + Block { + stmts: [], expr: None, .. + }, + _, + ) = body.kind + && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind + && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) + && let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind() + && cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()) + { + span_lint_and_sugg( + cx, + MISSING_SPIN_LOOP, + body.span, + "busy-waiting loop should at least have a spin loop hint", + "try", + (if is_no_std_crate(cx) { + "{ core::hint::spin_loop() }" + } else { + "{ std::hint::spin_loop() }" + }) + .into(), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index b83d148b5..6e3477a1c 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -1,7 +1,6 @@ use super::MUT_RANGE_BOUND; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::{get_enclosing_block, higher, path_to_local}; -use if_chain::if_chain; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -12,19 +11,17 @@ use rustc_middle::ty; use rustc_span::source_map::Span; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { - if_chain! { - if let Some(higher::Range { - start: Some(start), - end: Some(end), - .. - }) = higher::Range::hir(arg); - let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end)); - if mut_id_start.is_some() || mut_id_end.is_some(); - then { - let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end); - mut_warn_with_span(cx, span_low); - mut_warn_with_span(cx, span_high); - } + if let Some(higher::Range { + start: Some(start), + end: Some(end), + .. + }) = higher::Range::hir(arg) + && let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end)) + && (mut_id_start.is_some() || mut_id_end.is_some()) + { + let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end); + mut_warn_with_span(cx, span_low); + mut_warn_with_span(cx, span_high); } } @@ -42,13 +39,11 @@ fn mut_warn_with_span(cx: &LateContext<'_>, span: Option) { } fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option { - if_chain! { - if let Some(hir_id) = path_to_local(bound); - if let Node::Pat(pat) = cx.tcx.hir().get(hir_id); - if let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind; - then { - return Some(hir_id); - } + if let Some(hir_id) = path_to_local(bound) + && let Node::Pat(pat) = cx.tcx.hir().get(hir_id) + && let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind + { + return Some(hir_id); } None } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index c4af46b8f..e2be861a7 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -4,7 +4,6 @@ use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; @@ -187,15 +186,13 @@ pub(super) fn check<'tcx>( } fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { - if_chain! { - if let ExprKind::MethodCall(method, recv, [], _) = expr.kind; - if method.ident.name == sym::len; - if let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind; - if path.segments.len() == 1; - if path.segments[0].ident.name == var; - then { - return true; - } + if let ExprKind::MethodCall(method, recv, [], _) = expr.kind + && method.ident.name == sym::len + && let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind + && path.segments.len() == 1 + && path.segments[0].ident.name == var + { + return true; } false @@ -207,17 +204,15 @@ fn is_end_eq_array_len<'tcx>( limits: ast::RangeLimits, indexed_ty: Ty<'tcx>, ) -> bool { - if_chain! { - if let ExprKind::Lit(lit) = end.kind; - if let ast::LitKind::Int(end_int, _) = lit.node; - if let ty::Array(_, arr_len_const) = indexed_ty.kind(); - if let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env); - then { - return match limits { - ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(), - ast::RangeLimits::HalfOpen => end_int >= arr_len.into(), - }; - } + if let ExprKind::Lit(lit) = end.kind + && let ast::LitKind::Int(end_int, _) = lit.node + && let ty::Array(_, arr_len_const) = indexed_ty.kind() + && let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env) + { + return match limits { + ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(), + ast::RangeLimits::HalfOpen => end_int >= arr_len.into(), + }; } false @@ -248,51 +243,49 @@ struct VarVisitor<'a, 'tcx> { impl<'a, 'tcx> VarVisitor<'a, 'tcx> { fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { - if_chain! { + if let ExprKind::Path(ref seqpath) = seqexpr.kind // the indexed container is referenced by a name - if let ExprKind::Path(ref seqpath) = seqexpr.kind; - if let QPath::Resolved(None, seqvar) = *seqpath; - if seqvar.segments.len() == 1; - if is_local_used(self.cx, idx, self.var); - then { - if self.prefer_mutable { - self.indexed_mut.insert(seqvar.segments[0].ident.name); - } - let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); - let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); - match res { - Res::Local(hir_id) => { - let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let extent = self - .cx - .tcx - .region_scope_tree(parent_def_id) - .var_scope(hir_id.local_id) - .unwrap(); - if index_used_directly { - self.indexed_directly.insert( - seqvar.segments[0].ident.name, - (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), - ); - } else { - self.indexed_indirectly - .insert(seqvar.segments[0].ident.name, Some(extent)); - } - return false; // no need to walk further *on the variable* - }, - Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { - if index_used_directly { - self.indexed_directly.insert( - seqvar.segments[0].ident.name, - (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), - ); - } else { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); - } - return false; // no need to walk further *on the variable* - }, - _ => (), - } + && let QPath::Resolved(None, seqvar) = *seqpath + && seqvar.segments.len() == 1 + && is_local_used(self.cx, idx, self.var) + { + if self.prefer_mutable { + self.indexed_mut.insert(seqvar.segments[0].ident.name); + } + let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); + let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); + match res { + Res::Local(hir_id) => { + let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); + let extent = self + .cx + .tcx + .region_scope_tree(parent_def_id) + .var_scope(hir_id.local_id) + .unwrap(); + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), + ); + } else { + self.indexed_indirectly + .insert(seqvar.segments[0].ident.name, Some(extent)); + } + return false; // no need to walk further *on the variable* + }, + Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), + ); + } else { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); + } + return false; // no need to walk further *on the variable* + }, + _ => (), } } true @@ -301,42 +294,36 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { + if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind // a range index op - if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind; - if let Some(trait_id) = self + && let Some(trait_id) = self .cx .typeck_results() .type_dependent_def_id(expr.hir_id) - .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)); - if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) - || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)); - if !self.check(args_1, args_0, expr); - then { - return; - } + .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)) + && ((meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) + || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id))) + && !self.check(args_1, args_0, expr) + { + return; } - if_chain! { + if let ExprKind::Index(seqexpr, idx, _) = expr.kind // an index op - if let ExprKind::Index(seqexpr, idx, _) = expr.kind; - if !self.check(idx, seqexpr, expr); - then { - return; - } + && !self.check(idx, seqexpr, expr) + { + return; } - if_chain! { + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind // directly using a variable - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind; - if let Res::Local(local_id) = path.res; - then { - if local_id == self.var { - self.nonindex = true; - } else { - // not the correct variable, but still a variable - self.referenced.insert(path.segments[0].ident.name); - } + && let Res::Local(local_id) = path.res + { + if local_id == self.var { + self.nonindex = true; + } else { + // not the correct variable, but still a variable + self.referenced.insert(path.segments[0].ident.name); } } diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 5fffb27cd..15e11fd38 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::path_to_local; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -44,54 +43,50 @@ pub(super) fn check<'tcx>( // Determine whether it is safe to lint the body let mut same_item_push_visitor = SameItemPushVisitor::new(cx); walk_expr(&mut same_item_push_visitor, body); - if_chain! { - if same_item_push_visitor.should_lint(); - if let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push; - let vec_ty = cx.typeck_results().expr_ty(vec); - let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); - if cx + if same_item_push_visitor.should_lint() + && let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push + && let vec_ty = cx.typeck_results().expr_ty(vec) + && let ty = vec_ty.walk().nth(1).unwrap().expect_ty() + && cx .tcx .lang_items() .clone_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])); - then { - // Make sure that the push does not involve possibly mutating values - match pushed_item.kind { - ExprKind::Path(ref qpath) => { - match cx.qpath_res(qpath, pushed_item.hir_id) { - // immutable bindings that are initialized with literal or constant - Res::Local(hir_id) => { - let node = cx.tcx.hir().get(hir_id); - if_chain! { - if let Node::Pat(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)); - let parent_node = cx.tcx.hir().parent_id(hir_id); - if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); - if let Some(init) = parent_let_expr.init; - then { - match init.kind { - // immutable bindings that are initialized with literal - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), - // immutable bindings that are initialized with constant - ExprKind::Path(ref path) => { - if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { - emit_lint(cx, vec, pushed_item, ctxt); - } - } - _ => {}, + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // Make sure that the push does not involve possibly mutating values + match pushed_item.kind { + ExprKind::Path(ref qpath) => { + match cx.qpath_res(qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + let node = cx.tcx.hir().get(hir_id); + if let Node::Pat(pat) = node + && let PatKind::Binding(bind_ann, ..) = pat.kind + && !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)) + && let parent_node = cx.tcx.hir().parent_id(hir_id) + && let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node) + && let Some(init) = parent_let_expr.init + { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { + emit_lint(cx, vec, pushed_item, ctxt); } - } + }, + _ => {}, } - }, - // constant - Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt), - _ => {}, - } - }, - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), - _ => {}, - } + } + }, + // constant + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt), + _ => {}, + } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + _ => {}, } } } @@ -118,16 +113,14 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { } fn should_lint(&self) -> bool { - if_chain! { - if !self.non_deterministic_expr; - if !self.multiple_pushes; - if let Some((vec, _, _)) = self.vec_push; - if let Some(hir_id) = path_to_local(vec); - then { - !self.used_locals.contains(&hir_id) - } else { - false - } + if !self.non_deterministic_expr + && !self.multiple_pushes + && let Some((vec, _, _)) = self.vec_push + && let Some(hir_id) = path_to_local(vec) + { + !self.used_locals.contains(&hir_id) + } else { + false } } } @@ -180,18 +173,16 @@ fn get_vec_push<'tcx>( cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> { - if_chain! { + if let StmtKind::Semi(semi_stmt) = &stmt.kind // Extract method being called - if let StmtKind::Semi(semi_stmt) = &stmt.kind; - if let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind; + && let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind // Figure out the parameters for the method call - if let Some(pushed_item) = args.first(); + && let Some(pushed_item) = args.first() // Check that the method being called is push() on a Vec - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec); - if path.ident.name.as_str() == "push"; - then { - return Some((self_expr, pushed_item, semi_stmt.span.ctxt())) - } + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec) + && path.ident.name.as_str() == "push" + { + return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } None } diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index dfb800ccf..d860b297a 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -2,7 +2,6 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet_with_applicability}; use clippy_utils::visitors::contains_break_or_continue; -use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; use rustc_errors::Applicability; @@ -66,36 +65,36 @@ pub(super) fn check<'tcx>( ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""), _ => return, }; - if_chain! { - if let ExprKind::Block(block, _) = body.kind; - if !block.stmts.is_empty(); - if !contains_break_or_continue(body); - then { - let mut applicability = Applicability::MachineApplicable; - let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); - let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); - let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned(); - block_str.remove(0); - block_str.pop(); - let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)); + if let ExprKind::Block(block, _) = body.kind + && !block.stmts.is_empty() + && !contains_break_or_continue(body) + { + let mut applicability = Applicability::MachineApplicable; + let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); + let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); + let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned(); + block_str.remove(0); + block_str.pop(); + let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)); - // Reference iterator from `&(mut) []` or `[].iter(_mut)()`. - if !prefix.is_empty() && ( + // Reference iterator from `&(mut) []` or `[].iter(_mut)()`. + if !prefix.is_empty() + && ( // Precedence of internal expression is less than or equal to precedence of `&expr`. arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression) - ) { - arg_snip = format!("({arg_snip})").into(); - } - - span_lint_and_sugg( - cx, - SINGLE_ELEMENT_LOOP, - expr.span, - "for loop over a single element", - "try", - format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"), - applicability, ) + { + arg_snip = format!("({arg_snip})").into(); } + + span_lint_and_sugg( + cx, + SINGLE_ELEMENT_LOOP, + expr.span, + "for loop over a single element", + "try", + format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"), + applicability, + ); } } diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 0a2bd89eb..38fdca573 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,6 +1,5 @@ use clippy_utils::ty::{has_iter_method, implements_trait}; use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; -use if_chain::if_chain; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; @@ -145,20 +144,18 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { fn visit_local(&mut self, l: &'tcx Local<'_>) { // Look for declarations of the variable - if_chain! { - if l.pat.hir_id == self.var_id; - if let PatKind::Binding(.., ident, _) = l.pat.kind; - then { - let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat)); + if l.pat.hir_id == self.var_id + && let PatKind::Binding(.., ident, _) = l.pat.kind + { + let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat)); - self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| { - InitializeVisitorState::Initialized { - initializer: init, - ty, - name: ident.name, - } - }) - } + self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| { + InitializeVisitorState::Initialized { + initializer: init, + ty, + name: ident.name, + } + }); } walk_local(self, l); diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index 7f24f3c5d..9fd9b7a16 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -2,7 +2,6 @@ use super::WHILE_IMMUTABLE_CONDITION; use clippy_utils::consts::constant; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::usage::mutated_variables; -use if_chain::if_chain; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -95,20 +94,18 @@ struct VarCollectorVisitor<'a, 'tcx> { impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Path(ref qpath) = ex.kind; - if let QPath::Resolved(None, _) = *qpath; - then { - match self.cx.qpath_res(qpath, ex.hir_id) { - Res::Local(hir_id) => { - self.ids.insert(hir_id); - }, - Res::Def(DefKind::Static(_), def_id) => { - let mutable = self.cx.tcx.is_mutable_static(def_id); - self.def_ids.insert(def_id, mutable); - }, - _ => {}, - } + if let ExprKind::Path(ref qpath) = ex.kind + && let QPath::Resolved(None, _) = *qpath + { + match self.cx.qpath_res(qpath, ex.hir_id) { + Res::Local(hir_id) => { + self.ids.insert(hir_id); + }, + Res::Def(DefKind::Static(_), def_id) => { + let mutable = self.cx.tcx.is_mutable_static(def_id); + self.def_ids.insert(def_id, mutable); + }, + _ => {}, } } } diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 5153070cf..21b9efba5 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -15,59 +14,53 @@ use rustc_span::symbol::sym; use rustc_span::Symbol; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { - if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); + if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern - if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind; - if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); + && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind + && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) // check for call to `Iterator::next` - if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind; - if method_name.ident.name == sym::next; - if is_trait_method(cx, let_expr, sym::Iterator); - if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr); + && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind + && method_name.ident.name == sym::next + && is_trait_method(cx, let_expr, sym::Iterator) + && let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr) // get the loop containing the match expression - if !uses_iter(cx, &iter_expr_struct, if_then); - then { - (let_expr, iter_expr_struct, iter_expr, some_pat, expr) - } else { - return; - } - }; - - let mut applicability = Applicability::MachineApplicable; - let loop_var = if let Some(some_pat) = some_pat.first() { - if is_refutable(cx, some_pat) { - // Refutable patterns don't work with for loops. - return; - } - snippet_with_applicability(cx, some_pat.span, "..", &mut applicability) - } else { - "_".into() - }; - - // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be - // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used - // afterwards a mutable borrow of a field isn't necessary. - let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) - || !iter_expr_struct.can_move - || !iter_expr_struct.fields.is_empty() - || needs_mutable_borrow(cx, &iter_expr_struct, loop_expr) + && !uses_iter(cx, &iter_expr_struct, if_then) { - ".by_ref()" - } else { - "" - }; + let mut applicability = Applicability::MachineApplicable; + let loop_var = if let Some(some_pat) = some_pat.first() { + if is_refutable(cx, some_pat) { + // Refutable patterns don't work with for loops. + return; + } + snippet_with_applicability(cx, some_pat.span, "..", &mut applicability) + } else { + "_".into() + }; - let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - WHILE_LET_ON_ITERATOR, - expr.span.with_hi(scrutinee_expr.span.hi()), - "this loop could be written as a `for` loop", - "try", - format!("for {loop_var} in {iterator}{by_ref}"), - applicability, - ); + // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be + // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used + // afterwards a mutable borrow of a field isn't necessary. + let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) + || !iter_expr_struct.can_move + || !iter_expr_struct.fields.is_empty() + || needs_mutable_borrow(cx, &iter_expr_struct, expr) + { + ".by_ref()" + } else { + "" + }; + + let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + WHILE_LET_ON_ITERATOR, + expr.span.with_hi(let_expr.span.hi()), + "this loop could be written as a `for` loop", + "try", + format!("for {loop_var} in {iterator}{by_ref}"), + applicability, + ); + } } #[derive(Debug)] diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 70c518247..6f1c5461d 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -90,30 +89,26 @@ impl MacroUseImports { impl<'tcx> LateLintPass<'tcx> for MacroUseImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if_chain! { - if cx.sess().opts.edition >= Edition::Edition2018; - if let hir::ItemKind::Use(path, _kind) = &item.kind; - let hir_id = item.hir_id(); - let attrs = cx.tcx.hir().attrs(hir_id); - if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); - if let Some(id) = path.res.iter().find_map(|res| match res { + if cx.sess().opts.edition >= Edition::Edition2018 + && let hir::ItemKind::Use(path, _kind) = &item.kind + && let hir_id = item.hir_id() + && let attrs = cx.tcx.hir().attrs(hir_id) + && let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)) + && let Some(id) = path.res.iter().find_map(|res| match res { Res::Def(DefKind::Mod, id) => Some(id), _ => None, - }); - if !id.is_local(); - then { - for kid in cx.tcx.module_children(id) { - if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { - let span = mac_attr.span; - let def_path = cx.tcx.def_path_str(mac_id); - self.imports.push((def_path, span, hir_id)); - } - } - } else { - if item.span.from_expansion() { - self.push_unique_macro_pat_ty(cx, item.span); + }) + && !id.is_local() + { + for kid in cx.tcx.module_children(id) { + if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { + let span = mac_attr.span; + let def_path = cx.tcx.def_path_str(mac_id); + self.imports.push((def_path, span, hir_id)); } } + } else if item.span.from_expansion() { + self.push_unique_macro_pat_ty(cx, item.span); } } fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { diff --git a/clippy_lints/src/main_recursion.rs b/clippy_lints/src/main_recursion.rs index 20333c150..ea1d25d80 100644 --- a/clippy_lints/src/main_recursion.rs +++ b/clippy_lints/src/main_recursion.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use clippy_utils::{is_entrypoint_fn, is_no_std_crate}; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -43,21 +42,19 @@ impl LateLintPass<'_> for MainRecursion { return; } - if_chain! { - if let ExprKind::Call(func, _) = &expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind; - if let Some(def_id) = path.res.opt_def_id(); - if is_entrypoint_fn(cx, def_id); - then { - span_lint_and_help( - cx, - MAIN_RECURSION, - func.span, - &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")), - None, - "consider using another function for this recursion" - ) - } + if let ExprKind::Call(func, _) = &expr.kind + && let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind + && let Some(def_id) = path.res.opt_def_id() + && is_entrypoint_fn(cx, def_id) + { + span_lint_and_help( + cx, + MAIN_RECURSION, + func.span, + &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")), + None, + "consider using another function for this recursion", + ); } } } diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 998de38a9..a5d91c949 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -47,61 +46,57 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { span: Span, def_id: LocalDefId, ) { - if_chain! { - if let Some(header) = kind.header(); - if !header.asyncness.is_async(); + if let Some(header) = kind.header() + && !header.asyncness.is_async() // Check that this function returns `impl Future` - if let FnRetTy::Return(ret_ty) = decl.output; - if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); - if let Some(output) = future_output_ty(trait_ref); - if captures_all_lifetimes(decl.inputs, &output_lifetimes); + && let FnRetTy::Return(ret_ty) = decl.output + && let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty) + && let Some(output) = future_output_ty(trait_ref) + && captures_all_lifetimes(decl.inputs, &output_lifetimes) // Check that the body of the function consists of one async block - if let ExprKind::Block(block, _) = body.value.kind; - if block.stmts.is_empty(); - if let Some(closure_body) = desugared_async_block(cx, block); - if let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = - cx.tcx.hir().get_by_def_id(def_id); - then { - let header_span = span.with_hi(ret_ty.span.hi()); + && let ExprKind::Block(block, _) = body.value.kind + && block.stmts.is_empty() + && let Some(closure_body) = desugared_async_block(cx, block) + && let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = + cx.tcx.hir().get_by_def_id(def_id) + { + let header_span = span.with_hi(ret_ty.span.hi()); - span_lint_and_then( - cx, - MANUAL_ASYNC_FN, - header_span, - "this function can be simplified using the `async fn` syntax", - |diag| { - if_chain! { - if let Some(vis_snip) = snippet_opt(cx, *vis_span); - if let Some(header_snip) = snippet_opt(cx, header_span); - if let Some(ret_pos) = position_before_rarrow(&header_snip); - if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); - then { - let header_snip = if vis_snip.is_empty() { - format!("async {}", &header_snip[..ret_pos]) - } else { - format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) - }; + span_lint_and_then( + cx, + MANUAL_ASYNC_FN, + header_span, + "this function can be simplified using the `async fn` syntax", + |diag| { + if let Some(vis_snip) = snippet_opt(cx, *vis_span) + && let Some(header_snip) = snippet_opt(cx, header_span) + && let Some(ret_pos) = position_before_rarrow(&header_snip) + && let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output) + { + let header_snip = if vis_snip.is_empty() { + format!("async {}", &header_snip[..ret_pos]) + } else { + format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) + }; - let help = format!("make the function `async` and {ret_sugg}"); - diag.span_suggestion( - header_span, - help, - format!("{header_snip}{ret_snip}"), - Applicability::MachineApplicable - ); + let help = format!("make the function `async` and {ret_sugg}"); + diag.span_suggestion( + header_span, + help, + format!("{header_snip}{ret_snip}"), + Applicability::MachineApplicable, + ); - let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); - diag.span_suggestion( - block.span, - "move the body of the async block to the enclosing function", - body_snip, - Applicability::MachineApplicable - ); - } - } - }, - ); - } + let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); + diag.span_suggestion( + block.span, + "move the body of the async block to the enclosing function", + body_snip, + Applicability::MachineApplicable, + ); + } + }, + ); } } } @@ -110,48 +105,44 @@ fn future_trait_ref<'tcx>( cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>, ) -> Option<(&'tcx TraitRef<'tcx>, Vec)> { - if_chain! { - if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind; - let item = cx.tcx.hir().item(item_id); - if let ItemKind::OpaqueTy(opaque) = &item.kind; - if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { + if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind + && let item = cx.tcx.hir().item(item_id) + && let ItemKind::OpaqueTy(opaque) = &item.kind + && let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { if let GenericBound::Trait(poly, _) = bound { Some(&poly.trait_ref) } else { None } - }); - if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); - then { - let output_lifetimes = bounds - .iter() - .filter_map(|bound| { - if let GenericArg::Lifetime(lt) = bound { - Some(lt.res) - } else { - None - } - }) - .collect(); + }) + && trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait() + { + let output_lifetimes = bounds + .iter() + .filter_map(|bound| { + if let GenericArg::Lifetime(lt) = bound { + Some(lt.res) + } else { + None + } + }) + .collect(); - return Some((trait_ref, output_lifetimes)); - } + return Some((trait_ref, output_lifetimes)); } None } fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> { - if_chain! { - if let Some(segment) = trait_ref.path.segments.last(); - if let Some(args) = segment.args; - if args.bindings.len() == 1; - let binding = &args.bindings[0]; - if binding.ident.name == sym::Output; - if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind; - then { - return Some(output); - } + if let Some(segment) = trait_ref.path.segments.last() + && let Some(args) = segment.args + && args.bindings.len() == 1 + && let binding = &args.bindings[0] + && binding.ident.name == sym::Output + && let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind + { + return Some(output); } None @@ -181,17 +172,15 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) } fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { - if_chain! { - if let Some(block_expr) = block.expr; - if let Expr { + if let Some(block_expr) = block.expr + && let Expr { kind: ExprKind::Closure(&Closure { body, .. }), .. - } = block_expr; - let closure_body = cx.tcx.hir().body(body); - if closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block)); - then { - return Some(closure_body); - } + } = block_expr + && let closure_body = cx.tcx.hir().body(body) + && closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block)) + { + return Some(closure_body); } None diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index cd614c895..69c65cf30 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -53,32 +53,30 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { return; } - if_chain! { - if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; - if let BinOpKind::Mul = &bin_op.node; - if !in_external_macro(cx.sess(), expr.span); - let ctxt = expr.span.ctxt(); - if left_expr.span.ctxt() == ctxt; - if right_expr.span.ctxt() == ctxt; - if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); - if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); - if let ExprKind::Lit(lit) = &other_expr.kind; - if let LitKind::Int(8, _) = lit.node; - then { - let mut app = Applicability::MachineApplicable; - let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0; - let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); + if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind + && let BinOpKind::Mul = &bin_op.node + && !in_external_macro(cx.sess(), expr.span) + && let ctxt = expr.span.ctxt() + && left_expr.span.ctxt() == ctxt + && right_expr.span.ctxt() == ctxt + && let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr) + && matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)) + && let ExprKind::Lit(lit) = &other_expr.kind + && let LitKind::Int(8, _) = lit.node + { + let mut app = Applicability::MachineApplicable; + let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0; + let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); - span_lint_and_sugg( - cx, - MANUAL_BITS, - expr.span, - "usage of `mem::size_of::()` to obtain the size of `T` in bits", - "consider using", - sugg, - app, - ); - } + span_lint_and_sugg( + cx, + MANUAL_BITS, + expr.span, + "usage of `mem::size_of::()` to obtain the size of `T` in bits", + "consider using", + sugg, + app, + ); } } @@ -98,22 +96,22 @@ fn get_one_size_of_ty<'tcx>( } fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> { - if_chain! { - if let ExprKind::Call(count_func, _func_args) = expr.kind; - if let ExprKind::Path(ref count_func_qpath) = count_func.kind; - - if let QPath::Resolved(_, count_func_path) = count_func_qpath; - if let Some(segment_zero) = count_func_path.segments.first(); - if let Some(args) = segment_zero.args; - if let Some(GenericArg::Type(real_ty)) = args.args.first(); - - if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id); - then { - cx.typeck_results().node_args(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty)) - } else { - None - } + if let ExprKind::Call(count_func, _func_args) = expr.kind + && let ExprKind::Path(ref count_func_qpath) = count_func.kind + && let QPath::Resolved(_, count_func_path) = count_func_qpath + && let Some(segment_zero) = count_func_path.segments.first() + && let Some(args) = segment_zero.args + && let Some(GenericArg::Type(real_ty)) = args.args.first() + && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id) + { + cx.typeck_results() + .node_args(count_func.hir_id) + .types() + .next() + .map(|resolved_ty| (*real_ty, resolved_ty)) + } else { + None } } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 9a9e6af50..b41bf2d76 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; use clippy_utils::{eq_expr_value, higher, match_def_path, paths}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -71,55 +70,61 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { return; } - if_chain! { - 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; - then { - let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { - StripKind::Prefix - } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) { - StripKind::Suffix - } else { - return; - }; - let target_res = cx.qpath_res(target_path, target_arg.hir_id); - if target_res == Res::Err { - return; + if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + && let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id) + && let ExprKind::Path(target_path) = &target_arg.kind + { + let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { + StripKind::Prefix + } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) { + StripKind::Suffix + } else { + return; + }; + let target_res = cx.qpath_res(target_path, target_arg.hir_id); + if target_res == Res::Err { + return; + }; + + if let Res::Local(hir_id) = target_res + && let Some(used_mutably) = mutated_variables(then, cx) + && used_mutably.contains(&hir_id) + { + return; + } + + let strippings = find_stripping(cx, strip_kind, target_res, pattern, then); + if !strippings.is_empty() { + let kind_word = match strip_kind { + StripKind::Prefix => "prefix", + StripKind::Suffix => "suffix", }; - if_chain! { - if let Res::Local(hir_id) = target_res; - if let Some(used_mutably) = mutated_variables(then, cx); - if used_mutably.contains(&hir_id); - then { - return; - } - } - - let strippings = find_stripping(cx, strip_kind, target_res, pattern, then); - if !strippings.is_empty() { - - let kind_word = match strip_kind { - StripKind::Prefix => "prefix", - StripKind::Suffix => "suffix", - }; - - let test_span = expr.span.until(then.span); - span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { + let test_span = expr.span.until(then.span); + span_lint_and_then( + cx, + MANUAL_STRIP, + strippings[0], + &format!("stripping a {kind_word} manually"), + |diag| { diag.span_note(test_span, format!("the {kind_word} was tested here")); multispan_sugg( diag, &format!("try using the `strip_{kind_word}` method"), - vec![(test_span, - format!("if let Some() = {}.strip_{kind_word}({}) ", - snippet(cx, target_arg.span, ".."), - snippet(cx, pattern.span, "..")))] - .into_iter().chain(strippings.into_iter().map(|span| (span, "".into()))), + vec![( + test_span, + format!( + "if let Some() = {}.strip_{kind_word}({}) ", + snippet(cx, target_arg.span, ".."), + snippet(cx, pattern.span, "..") + ), + )] + .into_iter() + .chain(strippings.into_iter().map(|span| (span, "".into()))), ); - }); - } + }, + ); } } } @@ -129,15 +134,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { // Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::MethodCall(_, arg, [], _) = expr.kind; - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, method_def_id, &paths::STR_LEN); - then { - Some(arg) - } else { - None - } + if let ExprKind::MethodCall(_, arg, [], _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && match_def_path(cx, method_def_id, &paths::STR_LEN) + { + Some(arg) + } else { + None } } @@ -201,36 +204,38 @@ fn find_stripping<'tcx>( impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - if_chain! { - 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::hir(index); - if let ExprKind::Path(path) = &indexed.kind; - if self.cx.qpath_res(path, ex.hir_id) == self.target; - then { - match (self.strip_kind, start, end) { - (StripKind::Prefix, Some(start), None) => { - if eq_pattern_length(self.cx, self.pattern, start) { - self.results.push(ex.span); - return; - } - }, - (StripKind::Suffix, None, Some(end)) => { - if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind; - if let Some(left_arg) = len_arg(self.cx, left); - if let ExprKind::Path(left_path) = &left_arg.kind; - if self.cx.qpath_res(left_path, left_arg.hir_id) == self.target; - if eq_pattern_length(self.cx, self.pattern, right); - then { - self.results.push(ex.span); - return; - } - } - }, - _ => {} - } + if is_ref_str(self.cx, ex) + && let unref = peel_ref(ex) + && let ExprKind::Index(indexed, index, _) = &unref.kind + && let Some(higher::Range { start, end, .. }) = higher::Range::hir(index) + && let ExprKind::Path(path) = &indexed.kind + && self.cx.qpath_res(path, ex.hir_id) == self.target + { + match (self.strip_kind, start, end) { + (StripKind::Prefix, Some(start), None) => { + if eq_pattern_length(self.cx, self.pattern, start) { + self.results.push(ex.span); + return; + } + }, + (StripKind::Suffix, None, Some(end)) => { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + left, + right, + ) = end.kind + && let Some(left_arg) = len_arg(self.cx, left) + && let ExprKind::Path(left_path) = &left_arg.kind + && self.cx.qpath_res(left_path, left_arg.hir_id) == self.target + && eq_pattern_length(self.cx, self.pattern, right) + { + self.results.push(ex.span); + return; + } + }, + _ => {}, } } diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 766beb980..5cef4d91b 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{iter_input_pats, method_chain_args}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -164,16 +163,14 @@ fn unit_closure<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, ) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> { - if_chain! { - if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind; - let body = cx.tcx.hir().body(body); - let body_expr = &body.value; - if fn_decl.inputs.len() == 1; - if is_unit_expression(cx, body_expr); - if let Some(binding) = iter_input_pats(fn_decl, body).next(); - then { - return Some((binding, body_expr)); - } + if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind + && let body = cx.tcx.hir().body(body) + && let body_expr = &body.value + && fn_decl.inputs.len() == 1 + && is_unit_expression(cx, body_expr) + && let Some(binding) = iter_input_pats(fn_decl, body).next() + { + return Some((binding, body_expr)); } None } diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index 841c020f2..bf0359694 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, is_res_lang_ctor}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -56,33 +55,31 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { return; }; - if_chain! { - if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) - if let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind; //get operation - if ok_path.ident.as_str() == "ok"; - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); - let ctxt = expr.span.ctxt(); - if let_expr.span.ctxt() == ctxt; - if let_pat.span.ctxt() == ctxt; - then { - let mut applicability = Applicability::MachineApplicable; - let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0; - let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0; - let sugg = format!( - "{ifwhile} let Ok({some_expr_string}) = {}", - trimmed_ok.trim().trim_end_matches('.'), - ); - span_lint_and_sugg( - cx, - MATCH_RESULT_OK, - expr.span.with_hi(let_expr.span.hi()), - "matching on `Some` with `ok()` is redundant", - &format!("consider matching on `Ok({some_expr_string})` and removing the call to `ok` instead"), - sugg, - applicability, - ); - } + if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result.ok(, _) + && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation + && ok_path.ident.as_str() == "ok" + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && let ctxt = expr.span.ctxt() + && let_expr.span.ctxt() == ctxt + && let_pat.span.ctxt() == ctxt + { + let mut applicability = Applicability::MachineApplicable; + let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0; + let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0; + let sugg = format!( + "{ifwhile} let Ok({some_expr_string}) = {}", + trimmed_ok.trim().trim_end_matches('.'), + ); + span_lint_and_sugg( + cx, + MATCH_RESULT_OK, + expr.span.with_hi(let_expr.span.hi()), + "matching on `Some` with `ok()` is redundant", + &format!("consider matching on `Ok({some_expr_string})` and removing the call to `ok` instead"), + sugg, + applicability, + ); } } } diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 29b935fb6..48fc5746b 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -5,7 +5,6 @@ use clippy_utils::visitors::is_local_used; use clippy_utils::{ is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, }; -use if_chain::if_chain; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind}; @@ -40,76 +39,73 @@ fn check_arm<'tcx>( outer_else_body: Option<&'tcx Expr<'tcx>>, ) { let inner_expr = peel_blocks_with_stmt(outer_then_body); - if_chain! { - if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr); - if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { + if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) + && let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)), - IfLetOrMatch::Match(scrutinee, arms, ..) => if_chain! { + IfLetOrMatch::Match(scrutinee, arms, ..) => if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) // if there are more than two arms, collapsing would be non-trivial - if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()); // one of the arms must be "wild-like" - if let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)); - then { - let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); - Some((scrutinee, then.pat, Some(els.body))) - } else { - None - } + && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) + { + let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); + Some((scrutinee, then.pat, Some(els.body))) + } else { + None }, - }; - if outer_pat.span.eq_ctxt(inner_scrutinee.span); + } + && outer_pat.span.eq_ctxt(inner_scrutinee.span) // match expression must be a local binding // match { .. } - if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)); - if !pat_contains_or(inner_then_pat); + && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)) + && !pat_contains_or(inner_then_pat) // the binding must come from the pattern of the containing match arm // .... => match { .. } - if let (Some(binding_span), is_innermost_parent_pat_struct) - = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id); + && let (Some(binding_span), is_innermost_parent_pat_struct) + = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) // the "else" branches must be equal - if match (outer_else_body, inner_else_body) { + && match (outer_else_body, inner_else_body) { (None, None) => true, (None, Some(e)) | (Some(e), None) => is_unit_expr(e), (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), - }; + } // the binding must not be used in the if guard - if outer_guard.map_or( + && outer_guard.map_or( true, |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id) - ); + ) // ...or anywhere in the inner expression - if match inner { + && match inner { IfLetOrMatch::IfLet(_, _, body, els) => { !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id)) }, IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)), - }; - then { - let msg = format!( - "this `{}` can be collapsed into the outer `{}`", - if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, - if outer_is_match { "match" } else { "if let" }, - ); - // collapsing patterns need an explicit field name in struct pattern matching - // ex: Struct {x: Some(1)} - let replace_msg = if is_innermost_parent_pat_struct { - format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) - } else { - String::new() - }; - span_lint_and_then( - cx, - COLLAPSIBLE_MATCH, - inner_expr.span, - &msg, - |diag| { - let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); - help_span.push_span_label(binding_span, "replace this binding"); - help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); - diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); - }, - ); } + { + let msg = format!( + "this `{}` can be collapsed into the outer `{}`", + if matches!(inner, IfLetOrMatch::Match(..)) { + "match" + } else { + "if let" + }, + if outer_is_match { "match" } else { "if let" }, + ); + // collapsing patterns need an explicit field name in struct pattern matching + // ex: Struct {x: Some(1)} + let replace_msg = if is_innermost_parent_pat_struct { + format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) + } else { + String::new() + }; + span_lint_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.span, &msg, |diag| { + let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); + help_span.push_span_label(binding_span, "replace this binding"); + help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); + diag.span_help( + help_span, + "the outer pattern can be modified to include the inner pattern", + ); + }); } } diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index 3329f93b7..c8a48246e 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -8,38 +8,35 @@ use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { - if_chain! { - if !local.span.from_expansion(); - if let Some(expr) = local.init; - if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind; - if arms.len() == 1 && arms[0].guard.is_none(); - if let PatKind::TupleStruct( - QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; - if args.len() == 1; - if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind; - let body = peel_blocks(arms[0].body); - if path_to_local_id(body, arg); - - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - INFALLIBLE_DESTRUCTURING_MATCH, - local.span, - "you seem to be trying to use `match` to destructure a single infallible pattern. \ - Consider using `let`", - "try", - format!( - "let {}({}{}) = {};", - snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), - if binding.0 == ByRef::Yes { "ref " } else { "" }, - snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), - snippet_with_applicability(cx, target.span, "..", &mut applicability), - ), - applicability, - ); - return true; - } + if !local.span.from_expansion() + && let Some(expr) = local.init + && let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind + && arms.len() == 1 + && arms[0].guard.is_none() + && let PatKind::TupleStruct(QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind + && args.len() == 1 + && let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind + && let body = peel_blocks(arms[0].body) + && path_to_local_id(body, arg) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + INFALLIBLE_DESTRUCTURING_MATCH, + local.span, + "you seem to be trying to use `match` to destructure a single infallible pattern. \ + Consider using `let`", + "try", + format!( + "let {}({}{}) = {};", + snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), + if binding.0 == ByRef::Yes { "ref " } else { "" }, + snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), + snippet_with_applicability(cx, target.span, "..", &mut applicability), + ), + applicability, + ); + return true; } false } diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index cdb51c33a..619ec8312 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -21,19 +21,19 @@ fn get_cond_expr<'tcx>( expr: &'tcx Expr<'_>, ctxt: SyntaxContext, ) -> Option> { - if_chain! { - if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); - if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; - if let PatKind::Binding(_,target, ..) = pat.kind; - if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr) - || is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None` - then { - return Some(SomeExpr { - expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), - needs_unsafe_block: contains_unsafe_block(cx, expr), - needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond - }) - } + if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr) + && let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind + && let PatKind::Binding(_, target, ..) = pat.kind + && (is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr) + || is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr)) + // check that one expr resolves to `Some(x)`, the other to `None` + { + return Some(SomeExpr { + expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), + needs_unsafe_block: contains_unsafe_block(cx, expr), + needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the + * cond */ + }); }; None } diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index b94501bf0..3e79cabd7 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -4,7 +4,6 @@ use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; use clippy_utils::{is_res_lang_ctor, path_to_local_id, sugg}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{OptionNone, ResultErr}; @@ -16,65 +15,57 @@ use super::MANUAL_UNWRAP_OR; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { let ty = cx.typeck_results().expr_ty(scrutinee); - if_chain! { - if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { - Some("Option") - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - Some("Result") - } else { - None - }; - if let Some(or_arm) = applicable_or_arm(cx, arms); - if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); - if let Some(indent) = indent_of(cx, expr.span); - if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); - then { - let reindented_or_body = - reindent_multiline(or_body_snippet.into(), true, Some(indent)); + if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { + Some("Option") + } else if is_type_diagnostic_item(cx, ty, sym::Result) { + Some("Result") + } else { + None + } && let Some(or_arm) = applicable_or_arm(cx, arms) + && let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span) + && let Some(indent) = indent_of(cx, expr.span) + && constant_simple(cx, cx.typeck_results(), or_arm.body).is_some() + { + let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent)); - let mut app = Applicability::MachineApplicable; - let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par(); - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{ty_name}::unwrap_or`"), - "replace with", - format!( - "{suggestion}.unwrap_or({reindented_or_body})", - ), - app, - ); - } + let mut app = Applicability::MachineApplicable; + let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par(); + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, + expr.span, + &format!("this pattern reimplements `{ty_name}::unwrap_or`"), + "replace with", + format!("{suggestion}.unwrap_or({reindented_or_body})",), + app, + ); } } fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { - if_chain! { - if arms.len() == 2; - if arms.iter().all(|arm| arm.guard.is_none()); - if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { - match arm.pat.kind { - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), - PatKind::TupleStruct(ref qpath, [pat], _) => - matches!(pat.kind, PatKind::Wild) - && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr), - _ => false, - } - }); - let unwrap_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; - if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, unwrap_arm.pat.hir_id); - if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); - if cx.tcx.lang_items().option_some_variant() == Some(variant_id) - || cx.tcx.lang_items().result_ok_variant() == Some(variant_id); - if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; - if path_to_local_id(unwrap_arm.body, binding_hir_id); - if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); - if !contains_return_break_continue_macro(or_arm.body); - then { - Some(or_arm) - } else { - None - } + if arms.len() == 2 + && arms.iter().all(|arm| arm.guard.is_none()) + && let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind { + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::TupleStruct(ref qpath, [pat], _) => { + matches!(pat.kind, PatKind::Wild) + && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) + }, + _ => false, + }) + && let unwrap_arm = &arms[1 - idx] + && let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind + && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, unwrap_arm.pat.hir_id) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + && (cx.tcx.lang_items().option_some_variant() == Some(variant_id) + || cx.tcx.lang_items().result_ok_variant() == Some(variant_id)) + && let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind + && path_to_local_id(unwrap_arm.body, binding_hir_id) + && cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty() + && !contains_return_break_continue_macro(or_arm.body) + { + Some(or_arm) + } else { + None } } diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 781ee138c..0627e458d 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -127,32 +127,30 @@ where let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.eq_ctxt(some_expr.expr.span); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + if !some_expr.needs_unsafe_block + && let Some(func) = can_pass_as_func(cx, id, some_expr.expr) + && func.span.eq_ctxt(some_expr.expr.span) + { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return None; + } + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::MUT) { + "mut " } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return None; - } + "" + }; - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::MUT) { - "mut " - } else { - "" - }; - - if some_expr.needs_unsafe_block { - format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") - } else { - format!("|{annotation}{some_binding}| {closure_expr_snip}") - } + if some_expr.needs_unsafe_block { + format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{annotation}{some_binding}| {closure_expr_snip}") } } } else if !is_wild_none && explicit_ref.is_none() { diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index d51cca040..3f737da92 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -26,18 +26,16 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: let output_ty = cx.typeck_results().expr_ty(expr); let input_ty = cx.typeck_results().expr_ty(ex); - let cast = if_chain! { - if let ty::Adt(_, args) = input_ty.kind(); - let input_ty = args.type_at(0); - if let ty::Adt(_, args) = output_ty.kind(); - let output_ty = args.type_at(0); - if let ty::Ref(_, output_ty, _) = *output_ty.kind(); - if input_ty != output_ty; - then { - ".map(|x| x as _)" - } else { - "" - } + let cast = if let ty::Adt(_, args) = input_ty.kind() + && let input_ty = args.type_at(0) + && let ty::Adt(_, args) = output_ty.kind() + && let output_ty = args.type_at(0) + && let ty::Ref(_, output_ty, _) = *output_ty.kind() + && input_ty != output_ty + { + ".map(|x| x as _)" + } else { + "" }; let mut applicability = Applicability::MachineApplicable; @@ -67,17 +65,16 @@ fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { - if_chain! { - if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind; - if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome); - if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind; - if let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind; - if is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome); - if let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind; - if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; - then { - return Some(mutabl) - } + if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind + && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) + && let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind + && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind + && is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome) + && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind + && path2.segments.len() == 1 + && ident.name == path2.segments[0].ident.name + { + return Some(mutabl); } None } diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index e2ddf11ab..56123326f 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -76,79 +76,80 @@ where ), >, { - if_chain! { - if !span_contains_comment(cx.sess().source_map(), expr.span); - 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); - if let Some(b1) = find_bool_lit(&last_expr.kind); - 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).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty() - }); - then { - if let Some(last_pat) = last_pat_opt { - if !is_wild(last_pat) { + if !span_contains_comment(cx.sess().source_map(), expr.span) + && iter.len() >= 2 + && cx.typeck_results().expr_ty(expr).is_bool() + && let Some((_, last_pat_opt, last_expr, _)) = iter.next_back() + && let iter_without_last = iter.clone() + && let Some((first_attrs, _, first_expr, first_guard)) = iter.next() + && let Some(b0) = find_bool_lit(&first_expr.kind) + && let Some(b1) = find_bool_lit(&last_expr.kind) + && b0 != b1 + && (first_guard.is_none() || iter.len() == 0) + && first_attrs.is_empty() + && iter.all(|arm| find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()) + { + if let Some(last_pat) = last_pat_opt { + if !is_wild(last_pat) { + return false; + } + } + + for arm in iter_without_last.clone() { + if let Some(pat) = arm.1 { + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some(pat.kind) { return false; } } - - for arm in iter_without_last.clone() { - if let Some(pat) = arm.1 { - if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some(pat.kind) { - return false; - } - } - } - - // 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 _; - 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)) = first_guard { - format!("{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability)) - } else { - pat - }; - - // strip potential borrows (#6503), but only if the type is a reference - let mut ex_new = ex; - if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { - ex_new = ex_inner; - } - }; - span_lint_and_sugg( - cx, - MATCH_LIKE_MATCHES_MACRO, - expr.span, - &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }), - "try", - format!( - "{}matches!({}, {pat_and_guard})", - if b0 { "" } else { "!" }, - snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), - ), - applicability, - ); - true - } else { - false } + + // 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 _; + 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)) = first_guard { + format!( + "{pat} if {}", + snippet_with_applicability(cx, g.span, "..", &mut applicability) + ) + } else { + pat + }; + + // strip potential borrows (#6503), but only if the type is a reference + let mut ex_new = ex; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { + if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { + ex_new = ex_inner; + } + }; + span_lint_and_sugg( + cx, + MATCH_LIKE_MATCHES_MACRO, + expr.span, + &format!( + "{} expression looks like `matches!` macro", + if is_if_let { "if let .. else" } else { "match" } + ), + "try", + format!( + "{}matches!({}, {pat_and_guard})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + true + } else { + false } } diff --git a/clippy_lints/src/matches/match_on_vec_items.rs b/clippy_lints/src/matches/match_on_vec_items.rs index bd53ebd48..dd71560e1 100644 --- a/clippy_lints/src/matches/match_on_vec_items.rs +++ b/clippy_lints/src/matches/match_on_vec_items.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -10,39 +9,29 @@ use rustc_span::sym; use super::MATCH_ON_VEC_ITEMS; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { - if_chain! { - if let Some(idx_expr) = is_vec_indexing(cx, scrutinee); - if let ExprKind::Index(vec, idx, _) = idx_expr.kind; - - then { - // FIXME: could be improved to suggest surrounding every pattern with Some(_), - // but only when `or_patterns` are stabilized. - span_lint_and_sugg( - cx, - MATCH_ON_VEC_ITEMS, - scrutinee.span, - "indexing into a vector may panic", - "try", - format!( - "{}.get({})", - snippet(cx, vec.span, ".."), - snippet(cx, idx.span, "..") - ), - Applicability::MaybeIncorrect - ); - } + if let Some(idx_expr) = is_vec_indexing(cx, scrutinee) + && let ExprKind::Index(vec, idx, _) = idx_expr.kind + { + // FIXME: could be improved to suggest surrounding every pattern with Some(_), + // but only when `or_patterns` are stabilized. + span_lint_and_sugg( + cx, + MATCH_ON_VEC_ITEMS, + scrutinee.span, + "indexing into a vector may panic", + "try", + format!("{}.get({})", snippet(cx, vec.span, ".."), snippet(cx, idx.span, "..")), + Applicability::MaybeIncorrect, + ); } } fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::Index(array, index, _) = expr.kind; - if is_vector(cx, array); - if !is_full_range(cx, index); - - then { - return Some(expr); - } + if let ExprKind::Index(array, index, _) = expr.kind + && is_vector(cx, array) + && !is_full_range(cx, index) + { + return Some(expr); } None diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 6fc79fadd..745be671b 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -66,25 +66,23 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let mut local_map: HirIdMap = HirIdMap::default(); let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - if_chain! { - if let Some(a_id) = path_to_local(a); - if let Some(b_id) = path_to_local(b); - let entry = match local_map.entry(a_id) { + if let Some(a_id) = path_to_local(a) + && let Some(b_id) = path_to_local(b) + && let entry = match local_map.entry(a_id) { HirIdMapEntry::Vacant(entry) => entry, // check if using the same bindings as before HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id, - }; - // the names technically don't have to match; this makes the lint more conservative - if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id); - if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b); - if pat_contains_local(lhs.pat, a_id); - if pat_contains_local(rhs.pat, b_id); - then { - entry.insert(b_id); - true - } else { - false } + // the names technically don't have to match; this makes the lint more conservative + && cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id) + && cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b) + && pat_contains_local(lhs.pat, a_id) + && pat_contains_local(rhs.pat, b_id) + { + entry.insert(b_id); + true + } else { + false } }; // Arms with a guard are ignored, those can’t always be merged together diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 675a85ae5..bd38648bc 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -20,21 +20,16 @@ enum CaseMethod { } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { - if_chain! { - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind(); - if let ty::Str = ty.kind(); - then { - let mut visitor = MatchExprVisitor { - cx, - case_method: None, - }; + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind() + && let ty::Str = ty.kind() + { + let mut visitor = MatchExprVisitor { cx, case_method: None }; - visitor.visit_expr(scrutinee); + visitor.visit_expr(scrutinee); - if let Some(case_method) = visitor.case_method { - if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { - lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); - } + if let Some(case_method) = visitor.case_method { + if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { + lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); } } } @@ -88,17 +83,15 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<( }; for arm in arms { - if_chain! { - if let PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), - .. - }) = arm.pat.kind; - if let LitKind::Str(symbol, _) = lit.node; - let input = symbol.as_str(); - if !case_check(input); - then { - return Some((lit.span, symbol)); - } + if let PatKind::Lit(Expr { + kind: ExprKind::Lit(lit), + .. + }) = arm.pat.kind + && let LitKind::Str(symbol, _) = lit.node + && let input = symbol.as_str() + && !case_check(input) + { + return Some((lit.span, symbol)); } } diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index a2903e52a..8a4c0ab90 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -34,20 +34,19 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' } } } - if_chain! { - if matching_wild; - if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span); - if is_panic(cx, macro_call.def_id); - then { - // `Err(_)` or `Err(_e)` arm with `panic!` found - span_lint_and_note(cx, - MATCH_WILD_ERR_ARM, - arm.pat.span, - &format!("`Err({ident_bind_name})` matches all errors"), - None, - "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable", - ); - } + if matching_wild + && let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span) + && is_panic(cx, macro_call.def_id) + { + // `Err(_)` or `Err(_e)` arm with `panic!` found + span_lint_and_note( + cx, + MATCH_WILD_ERR_ARM, + arm.pat.span, + &format!("`Err({ident_bind_name})` matches all errors"), + None, + "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable", + ); } } } diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 9a7c00823..2582f7edc 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -5,7 +5,6 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr}; use clippy_utils::{higher, is_expn_of, is_trait_method}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -35,15 +34,13 @@ pub(super) fn check_if_let<'tcx>( // Extract the generic arguments out of a type fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { - if_chain! { - if let ty::Adt(_, subs) = ty.kind(); - if let Some(sub) = subs.get(index); - if let GenericArgKind::Type(sub_ty) = sub.unpack(); - then { - Some(sub_ty) - } else { - None - } + if let ty::Adt(_, subs) = ty.kind() + && let Some(sub) = subs.get(index) + && let GenericArgKind::Type(sub_ty) = sub.unpack() + { + Some(sub_ty) + } else { + None } } @@ -142,14 +139,12 @@ fn find_sugg_for_if_let<'tcx>( let needs_drop = needs_ordered_drop(cx, check_ty) || any_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, ..) = let_expr.kind; - if method_path.ident.name == sym::next; - if is_trait_method(cx, let_expr, sym::Iterator); - then { - return; - } + if keyword == "while" + && let ExprKind::MethodCall(method_path, ..) = let_expr.kind + && method_path.ident.name == sym::next + && is_trait_method(cx, let_expr, sym::Iterator) + { + return; } let result_expr = match &let_expr.kind { diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index 4efe93d4b..316b2f63e 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -6,25 +6,22 @@ use rustc_middle::ty; use super::REST_PAT_IN_FULLY_BOUND_STRUCTS; pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { - if_chain! { - if !pat.span.from_expansion(); - if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind; - if let Some(def_id) = path.res.opt_def_id(); - let ty = cx.tcx.type_of(def_id).instantiate_identity(); - if let ty::Adt(def, _) = ty.kind(); - if def.is_struct() || def.is_union(); - if fields.len() == def.non_enum_variant().fields.len(); - if !def.non_enum_variant().is_field_list_non_exhaustive(); - - then { - span_lint_and_help( - cx, - REST_PAT_IN_FULLY_BOUND_STRUCTS, - pat.span, - "unnecessary use of `..` pattern in struct binding. All fields were already bound", - None, - "consider removing `..` from this binding", - ); - } + if !pat.span.from_expansion() + && let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind + && let Some(def_id) = path.res.opt_def_id() + && let ty = cx.tcx.type_of(def_id).instantiate_identity() + && let ty::Adt(def, _) = ty.kind() + && (def.is_struct() || def.is_union()) + && fields.len() == def.non_enum_variant().fields.len() + && !def.non_enum_variant().is_field_list_non_exhaustive() + { + span_lint_and_help( + cx, + REST_PAT_IN_FULLY_BOUND_STRUCTS, + pat.span, + "unnecessary use of `..` pattern in struct binding. All fields were already bound", + None, + "consider removing `..` from this binding", + ); } } diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 48efd0230..86c414daf 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -89,50 +89,52 @@ fn report_single_pattern( }); let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); - let (msg, sugg) = if_chain! { - if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; - let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)); - if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait(); - if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait(); - if ty.is_integral() || ty.is_char() || ty.is_str() - || (implements_trait(cx, ty, spe_trait_id, &[]) - && implements_trait(cx, ty, pe_trait_id, &[ty.into()])); - then { - // scrutinee derives PartialEq and the pattern is a constant. - let pat_ref_count = match pat.kind { - // string literals are already a reference. - PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1, - _ => pat_ref_count, - }; - // References are only implicitly added to the pattern, so no overflow here. - // e.g. will work: match &Some(_) { Some(_) => () } - // will not: match Some(_) { &Some(_) => () } - let ref_count_diff = ty_ref_count - pat_ref_count; + let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind + && let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)) + && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() + && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() + && (ty.is_integral() + || ty.is_char() + || ty.is_str() + || (implements_trait(cx, ty, spe_trait_id, &[]) && implements_trait(cx, ty, pe_trait_id, &[ty.into()]))) + { + // scrutinee derives PartialEq and the pattern is a constant. + let pat_ref_count = match pat.kind { + // string literals are already a reference. + PatKind::Lit(Expr { + kind: ExprKind::Lit(lit), + .. + }) if lit.node.is_str() => pat_ref_count + 1, + _ => pat_ref_count, + }; + // References are only implicitly added to the pattern, so no overflow here. + // e.g. will work: match &Some(_) { Some(_) => () } + // will not: match Some(_) { &Some(_) => () } + let ref_count_diff = ty_ref_count - pat_ref_count; - // Try to remove address of expressions first. - let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff); - let ref_count_diff = ref_count_diff - removed; + // Try to remove address of expressions first. + let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff); + let ref_count_diff = ref_count_diff - removed; - let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; - let sugg = format!( - "if {} == {}{} {}{els_str}", - snippet(cx, ex.span, ".."), - // PartialEq for different reference counts may not exist. - "&".repeat(ref_count_diff), - snippet(cx, arms[0].pat.span, ".."), - expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), - ); - (msg, sugg) - } else { - let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; - let sugg = format!( - "if let {} = {} {}{els_str}", - snippet(cx, arms[0].pat.span, ".."), - snippet(cx, ex.span, ".."), - expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), - ); - (msg, sugg) - } + let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; + let sugg = format!( + "if {} == {}{} {}{els_str}", + snippet(cx, ex.span, ".."), + // PartialEq for different reference counts may not exist. + "&".repeat(ref_count_diff), + snippet(cx, arms[0].pat.span, ".."), + expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), + ); + (msg, sugg) + } else { + let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; + let sugg = format!( + "if let {} = {} {}{els_str}", + snippet(cx, arms[0].pat.span, ".."), + snippet(cx, ex.span, ".."), + expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), + ); + (msg, sugg) }; span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 0fd6f533d..dd489fc25 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -22,59 +21,57 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine // #[allow(unreachable_code)] // val, // }; - if_chain! { - if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind; - if let ExprKind::Path(ref match_fun_path) = match_fun.kind; - if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); - if let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind; - if is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr); - if let Some(return_ty) = find_return_type(cx, &expr.kind); - then { - let prefix; - let suffix; - let err_ty; + if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind + && let ExprKind::Path(ref match_fun_path) = match_fun.kind + && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) + && let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind + && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) + && let Some(return_ty) = find_return_type(cx, &expr.kind) + { + let prefix; + let suffix; + let err_ty; - if let Some(ty) = result_error_type(cx, return_ty) { - prefix = "Err("; - suffix = ")"; - err_ty = ty; - } else if let Some(ty) = poll_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Err("; - suffix = "))"; - err_ty = ty; - } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Some(Err("; - suffix = ")))"; - err_ty = ty; - } else { - return; - }; + if let Some(ty) = result_error_type(cx, return_ty) { + prefix = "Err("; + suffix = ")"; + err_ty = ty; + } else if let Some(ty) = poll_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Err("; + suffix = "))"; + err_ty = ty; + } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Some(Err("; + suffix = ")))"; + err_ty = ty; + } else { + return; + }; - let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); - let mut applicability = Applicability::MachineApplicable; - let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); - let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { - "" // already returns - } else { - "return " - }; - let suggestion = if err_ty == expr_err_ty { - format!("{ret_prefix}{prefix}{origin_snippet}{suffix}") - } else { - format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}") - }; + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); + let mut applicability = Applicability::MachineApplicable; + let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); + let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + "" // already returns + } else { + "return " + }; + let suggestion = if err_ty == expr_err_ty { + format!("{ret_prefix}{prefix}{origin_snippet}{suffix}") + } else { + format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}") + }; - span_lint_and_sugg( - cx, - TRY_ERR, - expr.span, - "returning an `Err(_)` with the `?` operator", - "try", - suggestion, - applicability, - ); - } + span_lint_and_sugg( + cx, + TRY_ERR, + expr.span, + "returning an `Err(_)` with the `?` operator", + "try", + suggestion, + applicability, + ); } } @@ -92,51 +89,42 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O /// Extracts the error type from Result. fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(_, subst) = ty.kind(); - if is_type_diagnostic_item(cx, ty, sym::Result); - then { - Some(subst.type_at(1)) - } else { - None - } + if let ty::Adt(_, subst) = ty.kind() + && is_type_diagnostic_item(cx, ty, sym::Result) + { + Some(subst.type_at(1)) + } else { + None } } /// Extracts the error type from Poll>. fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()); - then { - Some(ready_subst.type_at(1)) - } else { - None - } + if let ty::Adt(def, subst) = ty.kind() + && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) + && let ready_ty = subst.type_at(0) + && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() + && cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()) + { + Some(ready_subst.type_at(1)) + } else { + None } } /// Extracts the error type from Poll>>. fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()); - let some_ty = ready_subst.type_at(0); - - if let ty::Adt(some_def, some_subst) = some_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, some_def.did()); - then { - Some(some_subst.type_at(1)) - } else { - None - } + if let ty::Adt(def, subst) = ty.kind() + && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) + && let ready_ty = subst.type_at(0) + && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() + && cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()) + && let some_ty = ready_subst.type_at(0) + && let ty::Adt(some_def, some_subst) = some_ty.kind() + && cx.tcx.is_diagnostic_item(sym::Result, some_def.did()) + { + Some(some_subst.type_at(1)) + } else { + None } } diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index c6a1b45a9..13d6f5112 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -4,7 +4,6 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Expr, ExprKind}; @@ -125,17 +124,37 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, dest: &Expr<'_>, expr_sp } fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { - if_chain! { + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id) // check if replacement is mem::MaybeUninit::uninit().assume_init() - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id); - if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id); - then { + && cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::MaybeUninit::uninit().assume_init()`", + "consider using", + format!( + "std::ptr::read({})", + snippet_with_applicability(cx, dest.span, "", &mut applicability) + ), + applicability, + ); + return; + } + + if let ExprKind::Call(repl_func, []) = src.kind + && let ExprKind::Path(ref repl_func_qpath) = repl_func.kind + && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() + { + if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, MEM_REPLACE_WITH_UNINIT, expr_span, - "replacing with `mem::MaybeUninit::uninit().assume_init()`", + "replacing with `mem::uninitialized()`", "consider using", format!( "std::ptr::read({})", @@ -143,40 +162,17 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' ), applicability, ); - return; - } - } - - if_chain! { - if let ExprKind::Call(repl_func, []) = src.kind; - if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - then { - if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - MEM_REPLACE_WITH_UNINIT, - expr_span, - "replacing with `mem::uninitialized()`", - "consider using", - format!( - "std::ptr::read({})", - snippet_with_applicability(cx, dest.span, "", &mut applicability) - ), - applicability, - ); - } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) && - !cx.typeck_results().expr_ty(src).is_primitive() { - span_lint_and_help( - cx, - MEM_REPLACE_WITH_UNINIT, - expr_span, - "replacing with `mem::zeroed()`", - None, - "consider using a default value or the `take_mut` crate instead", - ); - } + } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) + && !cx.typeck_results().expr_ty(src).is_primitive() + { + span_lint_and_help( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::zeroed()`", + None, + "consider using a default value or the `take_mut` crate instead", + ); } } } @@ -222,21 +218,19 @@ impl MemReplace { impl<'tcx> LateLintPass<'tcx> for MemReplace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { + if let ExprKind::Call(func, [dest, src]) = expr.kind // Check that `expr` is a call to `mem::replace()` - if let ExprKind::Call(func, [dest, src]) = expr.kind; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id); - then { - // Check that second argument is `Option::None` - if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { - check_replace_option_with_none(cx, dest, expr.span); - } else if self.msrv.meets(msrvs::MEM_TAKE) { - check_replace_with_default(cx, src, dest, expr.span); - } - check_replace_with_uninit(cx, src, dest, expr.span); + && let ExprKind::Path(ref func_qpath) = func.kind + && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::mem_replace, def_id) + { + // Check that second argument is `Option::None` + if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + check_replace_option_with_none(cx, dest, expr.span); + } else if self.msrv.meets(msrvs::MEM_TAKE) { + check_replace_with_default(cx, src, dest, expr.span); } + check_replace_with_uninit(cx, src, dest, expr.span); } } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 3a8cc4174..08bfa2e00 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::visitors::find_all_ret_expressions; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -70,57 +69,50 @@ pub(crate) trait BindInsteadOfMap { closure_expr: &hir::Expr<'_>, closure_args_span: Span, ) -> bool { - if_chain! { - if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind; - if let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind; - if Self::is_variant(cx, path.res); - if !contains_return(inner_expr); - if let Some(msg) = Self::lint_msg(cx); - then { - let mut app = Applicability::MachineApplicable; - let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0; + if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind + && let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind + && Self::is_variant(cx, path.res) + && !contains_return(inner_expr) + && let Some(msg) = Self::lint_msg(cx) + { + let mut app = Applicability::MachineApplicable; + let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0; - let closure_args_snip = snippet(cx, closure_args_span, ".."); - let option_snip = snippet(cx, recv.span, ".."); - let note = format!("{option_snip}.{}({closure_args_snip} {some_inner_snip})", Self::GOOD_METHOD_NAME); - span_lint_and_sugg( - cx, - BIND_INSTEAD_OF_MAP, - expr.span, - &msg, - "try", - note, - app, - ); - true - } else { - false - } + let closure_args_snip = snippet(cx, closure_args_span, ".."); + let option_snip = snippet(cx, recv.span, ".."); + let note = format!( + "{option_snip}.{}({closure_args_snip} {some_inner_snip})", + Self::GOOD_METHOD_NAME + ); + span_lint_and_sugg(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, "try", note, app); + true + } else { + false } } fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool { let mut suggs = Vec::new(); let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| { - if_chain! { - if !ret_expr.span.from_expansion(); - if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind; - if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind; - if Self::is_variant(cx, path.res); - if !contains_return(arg); - then { - suggs.push((ret_expr.span, arg.span.source_callsite())); - true - } else { - false - } + if !ret_expr.span.from_expansion() + && let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind + && let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind + && Self::is_variant(cx, path.res) + && !contains_return(arg) + { + suggs.push((ret_expr.span, arg.span.source_callsite())); + true + } else { + false } }); - let (span, msg) = if_chain! { - if can_sugg; - if let hir::ExprKind::MethodCall(segment, ..) = expr.kind; - if let Some(msg) = Self::lint_msg(cx); - then { (segment.ident.span, msg) } else { return false; } + let (span, msg) = if can_sugg + && let hir::ExprKind::MethodCall(segment, ..) = expr.kind + && let Some(msg) = Self::lint_msg(cx) + { + (segment.ident.span, msg) + } else { + return false; }; span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| { multispan_sugg_with_applicability( @@ -139,11 +131,12 @@ pub(crate) trait BindInsteadOfMap { /// Lint use of `_.and_then(|x| Some(y))` for `Option`s fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { - if_chain! { - if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def(); - if let Some(vid) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM); - if adt.did() == cx.tcx.parent(vid); - then {} else { return false; } + if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() + && let Some(vid) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM) + && adt.did() == cx.tcx.parent(vid) + { + } else { + return false; } match arg.kind { diff --git a/clippy_lints/src/methods/bytecount.rs b/clippy_lints/src/methods/bytecount.rs index 35370355f..4a2124c74 100644 --- a/clippy_lints/src/methods/bytecount.rs +++ b/clippy_lints/src/methods/bytecount.rs @@ -3,7 +3,6 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -18,53 +17,50 @@ pub(super) fn check<'tcx>( filter_recv: &'tcx Expr<'_>, filter_arg: &'tcx Expr<'_>, ) { - if_chain! { - if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind; - let body = cx.tcx.hir().body(body); - if let [param] = body.params; - if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind; - if let ExprKind::Binary(ref op, l, r) = body.value.kind; - if op.node == BinOpKind::Eq; - if is_type_diagnostic_item(cx, - cx.typeck_results().expr_ty(filter_recv).peel_refs(), - sym::SliceIter); - let operand_is_arg = |expr| { + if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind + && let body = cx.tcx.hir().body(body) + && let [param] = body.params + && let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind + && let ExprKind::Binary(ref op, l, r) = body.value.kind + && op.node == BinOpKind::Eq + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv).peel_refs(), sym::SliceIter) + && let operand_is_arg = (|expr| { let expr = peel_ref_operators(cx, peel_blocks(expr)); path_to_local_id(expr, arg_id) - }; - let needle = if operand_is_arg(l) { + }) + && let needle = if operand_is_arg(l) { r } else if operand_is_arg(r) { l } else { return; - }; - if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind(); - if !is_local_used(cx, needle, arg_id); - then { - let haystack = if let ExprKind::MethodCall(path, receiver, [], _) = - filter_recv.kind { - let p = path.ident.name; - if p == sym::iter || p == sym::iter_mut { - receiver - } else { - filter_recv - } + } + && ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind() + && !is_local_used(cx, needle, arg_id) + { + let haystack = if let ExprKind::MethodCall(path, receiver, [], _) = filter_recv.kind { + let p = path.ident.name; + if p == sym::iter || p == sym::iter_mut { + receiver } else { filter_recv - }; - let mut applicability = Applicability::MaybeIncorrect; - span_lint_and_sugg( - cx, - NAIVE_BYTECOUNT, - expr.span, - "you appear to be counting bytes the naive way", - "consider using the bytecount crate", - format!("bytecount::count({}, {})", - snippet_with_applicability(cx, haystack.span, "..", &mut applicability), - snippet_with_applicability(cx, needle.span, "..", &mut applicability)), - applicability, - ); - } + } + } else { + filter_recv + }; + let mut applicability = Applicability::MaybeIncorrect; + span_lint_and_sugg( + cx, + NAIVE_BYTECOUNT, + expr.span, + "you appear to be counting bytes the naive way", + "consider using the bytecount crate", + format!( + "bytecount::count({}, {})", + snippet_with_applicability(cx, haystack.span, "..", &mut applicability), + snippet_with_applicability(cx, needle.span, "..", &mut applicability) + ), + applicability, + ); }; } diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs index 649fc46e4..34159f2d1 100644 --- a/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_lang_item; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,23 +13,24 @@ pub(super) fn check<'tcx>( count_recv: &'tcx hir::Expr<'_>, bytes_recv: &'tcx hir::Expr<'_>, ) { - if_chain! { - if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id); - if cx.tcx.type_of(impl_id).instantiate_identity().is_str(); - let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs(); - if ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String); - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BYTES_COUNT_TO_LEN, - expr.span, - "using long and hard to read `.bytes().count()`", - "consider calling `.len()` instead", - format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)), - applicability - ); - } + if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(bytes_id) + && cx.tcx.type_of(impl_id).instantiate_identity().is_str() + && let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs() + && (ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String)) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BYTES_COUNT_TO_LEN, + expr.span, + "using long and hard to read `.bytes().count()`", + "consider calling `.len()` instead", + format!( + "{}.len()", + snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability) + ), + applicability, + ); }; } diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index d5897822e..a37087d0a 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_lang_item; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -27,51 +26,54 @@ pub(super) fn check<'tcx>( } } - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if cx.tcx.type_of(impl_id).instantiate_identity().is_str(); - if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind; - if (2..=6).contains(&ext_literal.as_str().len()); - let ext_str = ext_literal.as_str(); - if ext_str.starts_with('.'); - if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) - || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); - let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String); - then { - span_lint_and_then( - cx, - CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - recv.span.to(call_span), - "case-sensitive file extension comparison", - |diag| { - diag.help("consider using a case-insensitive comparison instead"); - if let Some(mut recv_source) = snippet_opt(cx, recv.span) { - - if !cx.typeck_results().expr_ty(recv).is_ref() { - recv_source = format!("&{recv_source}"); - } - - let suggestion_source = reindent_multiline( - format!( - "std::path::Path::new({}) - .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", - recv_source, ext_str.strip_prefix('.').unwrap()).into(), - true, - Some(indent_of(cx, call_span).unwrap_or(0) + 4) - ); - - diag.span_suggestion( - recv.span.to(call_span), - "use std::path::Path", - suggestion_source, - Applicability::MaybeIncorrect, - ); + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && cx.tcx.type_of(impl_id).instantiate_identity().is_str() + && let ExprKind::Lit(Spanned { + node: LitKind::Str(ext_literal, ..), + .. + }) = arg.kind + && (2..=6).contains(&ext_literal.as_str().len()) + && let ext_str = ext_literal.as_str() + && ext_str.starts_with('.') + && (ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) + || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit())) + && let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs() + && (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String)) + { + span_lint_and_then( + cx, + CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + recv.span.to(call_span), + "case-sensitive file extension comparison", + |diag| { + diag.help("consider using a case-insensitive comparison instead"); + if let Some(mut recv_source) = snippet_opt(cx, recv.span) { + if !cx.typeck_results().expr_ty(recv).is_ref() { + recv_source = format!("&{recv_source}"); } + + let suggestion_source = reindent_multiline( + format!( + "std::path::Path::new({}) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", + recv_source, + ext_str.strip_prefix('.').unwrap() + ) + .into(), + true, + Some(indent_of(cx, call_span).unwrap_or(0) + 4), + ); + + diag.span_suggestion( + recv.span.to(call_span), + "use std::path::Path", + suggestion_source, + Applicability::MaybeIncorrect, + ); } - ); - } + }, + ); } } diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index 0e41f3c21..c99cec067 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{method_chain_args, path_def_id}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; @@ -15,34 +14,34 @@ pub(super) fn check( lint: &'static Lint, suggest: &str, ) -> bool { - if_chain! { - if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind; - if let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)); - if Some(id) == cx.tcx.lang_items().option_some_variant(); - then { - let mut applicability = Applicability::MachineApplicable; - let self_ty = cx.typeck_results().expr_ty_adjusted(args[0].0).peel_refs(); + if let Some(args) = method_chain_args(info.chain, chain_methods) + && let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind + && let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)) + && Some(id) == cx.tcx.lang_items().option_some_variant() + { + let mut applicability = Applicability::MachineApplicable; + let self_ty = cx.typeck_results().expr_ty_adjusted(args[0].0).peel_refs(); - if *self_ty.kind() != ty::Str { - return false; - } - - span_lint_and_sugg( - cx, - lint, - info.expr.span, - &format!("you should use the `{suggest}` method"), - "like this", - format!("{}{}.{suggest}({})", - if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)), - applicability, - ); - - return true; + if *self_ty.kind() != ty::Str { + return false; } + + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{suggest}` method"), + "like this", + format!( + "{}{}.{suggest}({})", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), + snippet_with_applicability(cx, arg_char.span, "..", &mut applicability) + ), + applicability, + ); + + return true; } false diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index c9d50a5b0..d07e45434 100644 --- a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -15,28 +14,28 @@ pub(super) fn check( lint: &'static Lint, suggest: &str, ) -> bool { - if_chain! { - if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Lit(lit) = info.other.kind; - if let ast::LitKind::Char(c) = lit.node; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - lint, - info.expr.span, - &format!("you should use the `{suggest}` method"), - "like this", - format!("{}{}.{suggest}('{}')", - if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - c.escape_default()), - applicability, - ); + if let Some(args) = method_chain_args(info.chain, chain_methods) + && let hir::ExprKind::Lit(lit) = info.other.kind + && let ast::LitKind::Char(c) = lit.node + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{suggest}` method"), + "like this", + format!( + "{}{}.{suggest}('{}')", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), + c.escape_default() + ), + applicability, + ); - true - } else { - false - } + true + } else { + false } } diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index a8d4dd5e4..dc978c8a5 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -16,30 +16,27 @@ pub(super) fn check( err_span: Span, msrv: &Msrv, ) { - if_chain! { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) // Test the version to make sure the lint can be showed (expect_err has been // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) - if msrv.meets(msrvs::EXPECT_ERR); + && msrv.meets(msrvs::EXPECT_ERR) // Grabs the `Result` type - let result_type = cx.typeck_results().expr_ty(recv); + && let result_type = cx.typeck_results().expr_ty(recv) // Tests if the T type in a `Result` is not None - if let Some(data_type) = get_data_type(cx, result_type); + && let Some(data_type) = get_data_type(cx, result_type) // Tests if the T type in a `Result` implements debug - if has_debug_impl(cx, data_type); - - then { - span_lint_and_sugg( - cx, - ERR_EXPECT, - err_span.to(expect_span), - "called `.err().expect()` on a `Result` value", - "try", - "expect_err".to_string(), - Applicability::MachineApplicable + && has_debug_impl(cx, data_type) + { + span_lint_and_sugg( + cx, + ERR_EXPECT, + err_span.to(expect_span), + "called `.err().expect()` on a `Result` value", + "try", + "expect_err".to_string(), + Applicability::MachineApplicable, ); - } }; } diff --git a/clippy_lints/src/methods/extend_with_drain.rs b/clippy_lints/src/methods/extend_with_drain.rs index 495b26652..460ec7b36 100644 --- a/clippy_lints/src/methods/extend_with_drain.rs +++ b/clippy_lints/src/methods/extend_with_drain.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -11,35 +10,33 @@ use super::EXTEND_WITH_DRAIN; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if_chain! { - if is_type_diagnostic_item(cx, ty, sym::Vec); + if is_type_diagnostic_item(cx, ty, sym::Vec) //check source object - if let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind; - if src_method.ident.as_str() == "drain"; - let src_ty = cx.typeck_results().expr_ty(drain_vec); + && let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind + && src_method.ident.as_str() == "drain" + && let src_ty = cx.typeck_results().expr_ty(drain_vec) //check if actual src type is mutable for code suggestion - let immutable = src_ty.is_mutable_ptr(); - let src_ty = src_ty.peel_refs(); - if is_type_diagnostic_item(cx, src_ty, sym::Vec); + && let immutable = src_ty.is_mutable_ptr() + && let src_ty = src_ty.peel_refs() + && is_type_diagnostic_item(cx, src_ty, sym::Vec) //check drain range - if let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs(); - if is_type_lang_item(cx, src_ty_range, LangItem::RangeFull); - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - EXTEND_WITH_DRAIN, - expr.span, - "use of `extend` instead of `append` for adding the full range of a second vector", - "try", - format!( - "{}.append({}{})", - snippet_with_applicability(cx, recv.span, "..", &mut applicability), - if immutable { "" } else { "&mut " }, - snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability) - ), - applicability, - ); - } + && let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs() + && is_type_lang_item(cx, src_ty_range, LangItem::RangeFull) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + EXTEND_WITH_DRAIN, + expr.span, + "use of `extend` instead of `append` for adding the full range of a second vector", + "try", + format!( + "{}.append({}{})", + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + if immutable { "" } else { "&mut " }, + snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability) + ), + applicability, + ); } } diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 3fef53739..15f21fa6a 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::get_parent_expr; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; @@ -20,21 +19,19 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr let verb: &str; let lint_unary: &str; let help_unary: &str; - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let hir::ExprKind::Unary(op, _) = parent.kind; - if op == hir::UnOp::Not; - then { - lint_unary = "!"; - verb = "denies"; - help_unary = ""; - span = parent.span; - } else { - lint_unary = ""; - verb = "covers"; - help_unary = "!"; - span = expr.span; - } + if let Some(parent) = get_parent_expr(cx, expr) + && let hir::ExprKind::Unary(op, _) = parent.kind + && op == hir::UnOp::Not + { + lint_unary = "!"; + verb = "denies"; + help_unary = ""; + span = parent.span; + } else { + lint_unary = ""; + verb = "covers"; + help_unary = "!"; + span = expr.span; } let lint_msg = format!("`{lint_unary}FileType::is_file()` only {verb} regular files"); let help_msg = format!("use `{help_unary}FileType::is_dir()` instead"); diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 22b67923e..e52974103 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -4,7 +4,6 @@ use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq}; use hir::{Body, HirId, MatchSource, Pat}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; @@ -29,13 +28,11 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> let arg_id = body.params[0].pat.hir_id; match closure_expr.kind { hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => { - if_chain! { - if ident.name == method_name; - if let hir::ExprKind::Path(path) = &receiver.kind; - if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id); - then { - return arg_id == *local - } + if ident.name == method_name + && let hir::ExprKind::Path(path) = &receiver.kind + && let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id) + { + return arg_id == *local; } false }, @@ -139,11 +136,9 @@ impl<'tcx> OffendingFilterExpr<'tcx> { && path_to_local_id(map_arg_peeled, map_param_id)) && let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| { // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; + let a_path = if !is_filter_param_ref + && let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind + { expr_path } else { a }; // let the filter closure arg and the map closure arg be equal path_to_local_id(a_path, filter_param_id) && path_to_local_id(b, map_param_id) @@ -305,87 +300,98 @@ pub(super) fn check( return; } - if_chain! { - if is_trait_method(cx, map_recv, sym::Iterator); + if is_trait_method(cx, map_recv, sym::Iterator) // filter(|x| ...is_some())... - if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind; - let filter_body = cx.tcx.hir().body(filter_body_id); - if let [filter_param] = filter_body.params; + && let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind + && let filter_body = cx.tcx.hir().body(filter_body_id) + && let [filter_param] = filter_body.params // optional ref pattern: `filter(|&x| ..)` - let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + && let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { (ref_pat, true) } else { (filter_param.pat, false) - }; - - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id); - - if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; - let map_body = cx.tcx.hir().body(map_body_id); - if let [map_param] = map_body.params; - if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; - - if let Some(check_result) = - offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref); - - then { - let span = filter_span.with_hi(expr.span.hi()); - let (filter_name, lint) = if is_find { - ("find", MANUAL_FIND_MAP) - } else { - ("filter", MANUAL_FILTER_MAP) - }; - let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); - - let (sugg, note_and_span, applicability) = match check_result { - CheckResult::Method { map_arg, method, side_effect_expr_span } => { - let (to_opt, deref) = match method { - CalledMethod::ResultIsOk => (".ok()", String::new()), - CalledMethod::OptionIsSome => { - let derefs = cx.typeck_results() - .expr_adjustments(map_arg) - .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) - .count(); - - ("", "*".repeat(derefs)) - } - }; - - let sugg = format!( - "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", - snippet(cx, map_arg.span, ".."), - ); - let (note_and_span, applicability) = if let Some(span) = side_effect_expr_span { - let note = "the suggestion might change the behavior of the program when merging `filter` and `map`, \ - because this expression potentially contains side effects and will only execute once"; - - (Some((note, span)), Applicability::MaybeIncorrect) - } else { - (None, Applicability::MachineApplicable) - }; - - (sugg, note_and_span, applicability) - } - CheckResult::PatternMatching { variant_span, variant_ident } => { - let pat = snippet(cx, variant_span, ""); - - (format!("{filter_name}_map(|{map_param_ident}| match {map_param_ident} {{ \ - {pat} => Some({variant_ident}), \ - _ => None \ - }})"), None, Applicability::MachineApplicable) - } - }; - span_lint_and_then(cx, lint, span, &msg, |diag| { - diag.span_suggestion(span, "try", sugg, applicability); - - if let Some((note, span)) = note_and_span { - diag.span_note(span, note); - } - }); } + + && let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind + && let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id) + + && let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind + && let map_body = cx.tcx.hir().body(map_body_id) + && let [map_param] = map_body.params + && let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind + + && let Some(check_result) = + offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref) + { + let span = filter_span.with_hi(expr.span.hi()); + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) + } else { + ("filter", MANUAL_FILTER_MAP) + }; + let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); + + let (sugg, note_and_span, applicability) = match check_result { + CheckResult::Method { + map_arg, + method, + side_effect_expr_span, + } => { + let (to_opt, deref) = match method { + CalledMethod::ResultIsOk => (".ok()", String::new()), + CalledMethod::OptionIsSome => { + let derefs = cx + .typeck_results() + .expr_adjustments(map_arg) + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); + + ("", "*".repeat(derefs)) + }, + }; + + let sugg = format!( + "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", + snippet(cx, map_arg.span, ".."), + ); + let (note_and_span, applicability) = if let Some(span) = side_effect_expr_span { + let note = "the suggestion might change the behavior of the program when merging `filter` and `map`, \ + because this expression potentially contains side effects and will only execute once"; + + (Some((note, span)), Applicability::MaybeIncorrect) + } else { + (None, Applicability::MachineApplicable) + }; + + (sugg, note_and_span, applicability) + }, + CheckResult::PatternMatching { + variant_span, + variant_ident, + } => { + let pat = snippet(cx, variant_span, ""); + + ( + format!( + "{filter_name}_map(|{map_param_ident}| match {map_param_ident} {{ \ + {pat} => Some({variant_ident}), \ + _ => None \ + }})" + ), + None, + Applicability::MachineApplicable, + ) + }, + }; + span_lint_and_then(cx, lint, span, &msg, |diag| { + diag.span_suggestion(span, "try", sugg, applicability); + + if let Some((note, span)) = note_and_span { + diag.span_note(span, note); + } + }); } } diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 4040d3a5f..917a8e33e 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{is_path_diagnostic_item, sugg}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,28 +11,25 @@ use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) { - if_chain! { - if is_path_diagnostic_item(cx, func, sym::from_iter_fn); - let ty = cx.typeck_results().expr_ty(expr); - let arg_ty = cx.typeck_results().expr_ty(&args[0]); - if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - - if implements_trait(cx, arg_ty, iter_id, &[]); - then { - // `expr` implements `FromIterator` trait - let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); - let turbofish = extract_turbofish(cx, expr, ty); - let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); - span_lint_and_sugg( - cx, - FROM_ITER_INSTEAD_OF_COLLECT, - expr.span, - "usage of `FromIterator::from_iter`", - "use `.collect()` instead of `::from_iter()`", - sugg, - Applicability::MaybeIncorrect, - ); - } + if is_path_diagnostic_item(cx, func, sym::from_iter_fn) + && let ty = cx.typeck_results().expr_ty(expr) + && let arg_ty = cx.typeck_results().expr_ty(&args[0]) + && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && implements_trait(cx, arg_ty, iter_id, &[]) + { + // `expr` implements `FromIterator` trait + let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); + let turbofish = extract_turbofish(cx, expr, ty); + let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); + span_lint_and_sugg( + cx, + FROM_ITER_INSTEAD_OF_COLLECT, + expr.span, + "usage of `FromIterator::from_iter`", + "use `.collect()` instead of `::from_iter()`", + sugg, + Applicability::MaybeIncorrect, + ); } } @@ -43,41 +39,43 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> } let call_site = expr.span.source_callsite(); - if_chain! { - if let Some(snippet) = snippet_opt(cx, call_site); - let snippet_split = snippet.split("::").collect::>(); - if let Some((_, elements)) = snippet_split.split_last(); - - then { - if_chain! { - if let [type_specifier, _] = snippet_split.as_slice(); - if let Some(type_specifier) = strip_angle_brackets(type_specifier); - if let Some((type_specifier, ..)) = type_specifier.split_once(" as "); - then { - type_specifier.to_string() - } else { - // is there a type specifier? (i.e.: like `` in `collections::BTreeSet::::`) - if let Some(type_specifier) = snippet_split.iter().find(|e| strip_angle_brackets(e).is_some()) { - // remove the type specifier from the path elements - let without_ts = elements.iter().filter_map(|e| { - if e == type_specifier { None } else { Some((*e).to_string()) } - }).collect::>(); - // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) - format!("{}{type_specifier}", without_ts.join("::")) - } else { - // type is not explicitly specified so wildcards are needed - // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` - let ty_str = ty.to_string(); - let start = ty_str.find('<').unwrap_or(0); - let end = ty_str.find('>').unwrap_or(ty_str.len()); - let nb_wildcard = ty_str[start..end].split(',').count(); - let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{wildcards}>", elements.join("::")) - } - } - } + if let Some(snippet) = snippet_opt(cx, call_site) + && let snippet_split = snippet.split("::").collect::>() + && let Some((_, elements)) = snippet_split.split_last() + { + if let [type_specifier, _] = snippet_split.as_slice() + && let Some(type_specifier) = strip_angle_brackets(type_specifier) + && let Some((type_specifier, ..)) = type_specifier.split_once(" as ") + { + type_specifier.to_string() } else { - ty.to_string() + // is there a type specifier? (i.e.: like `` in `collections::BTreeSet::::`) + if let Some(type_specifier) = snippet_split.iter().find(|e| strip_angle_brackets(e).is_some()) { + // remove the type specifier from the path elements + let without_ts = elements + .iter() + .filter_map(|e| { + if e == type_specifier { + None + } else { + Some((*e).to_string()) + } + }) + .collect::>(); + // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) + format!("{}{type_specifier}", without_ts.join("::")) + } else { + // type is not explicitly specified so wildcards are needed + // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` + let ty_str = ty.to_string(); + let start = ty_str.find('<').unwrap_or(0); + let end = ty_str.find('>').unwrap_or(ty_str.len()); + let nb_wildcard = ty_str[start..end].split(',').count(); + let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); + format!("{}<{wildcards}>", elements.join("::")) + } } + } else { + ty.to_string() } } diff --git a/clippy_lints/src/methods/get_first.rs b/clippy_lints/src/methods/get_first.rs index 2e1dd3ec6..e1f1e4893 100644 --- a/clippy_lints/src/methods/get_first.rs +++ b/clippy_lints/src/methods/get_first.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir as hir; @@ -17,37 +16,38 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, ) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - let identity = cx.tcx.type_of(impl_id).instantiate_identity(); - if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind; - then { - if identity.is_slice() { - let mut app = Applicability::MachineApplicable; - let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); - span_lint_and_sugg( - cx, - GET_FIRST, - expr.span, - &format!("accessing first element with `{slice_name}.get(0)`"), - "try", - format!("{slice_name}.first()"), - app, - ); - } else if is_type_diagnostic_item(cx, identity, sym::VecDeque){ - let mut app = Applicability::MachineApplicable; - let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); - span_lint_and_sugg( - cx, - GET_FIRST, - expr.span, - &format!("accessing first element with `{slice_name}.get(0)`"), - "try", - format!("{slice_name}.front()"), - app, - ); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let identity = cx.tcx.type_of(impl_id).instantiate_identity() + && let hir::ExprKind::Lit(Spanned { + node: LitKind::Int(0, _), + .. + }) = arg.kind + { + if identity.is_slice() { + let mut app = Applicability::MachineApplicable; + let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); + span_lint_and_sugg( + cx, + GET_FIRST, + expr.span, + &format!("accessing first element with `{slice_name}.get(0)`"), + "try", + format!("{slice_name}.first()"), + app, + ); + } else if is_type_diagnostic_item(cx, identity, sym::VecDeque) { + let mut app = Applicability::MachineApplicable; + let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); + span_lint_and_sugg( + cx, + GET_FIRST, + expr.span, + &format!("accessing first element with `{slice_name}.get(0)`"), + "try", + format!("{slice_name}.front()"), + app, + ); } } } diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index e91ce64d8..78a553eb8 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; use clippy_utils::{is_diag_item_method, is_diag_trait_item}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -11,34 +10,32 @@ use rustc_span::sym; use super::IMPLICIT_CLONE; pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if_chain! { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_clone_like(cx, method_name, method_def_id); - let return_type = cx.typeck_results().expr_ty(expr); - let input_type = cx.typeck_results().expr_ty(recv); - let (input_type, ref_count) = peel_mid_ty_refs(input_type); - if !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)); - if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())); - if return_type == input_type; - if let Some(clone_trait) = cx.tcx.lang_items().clone_trait(); - if implements_trait(cx, return_type, clone_trait, &[]); - then { - let mut app = Applicability::MachineApplicable; - let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; - span_lint_and_sugg( - cx, - IMPLICIT_CLONE, - expr.span, - &format!("implicitly cloning a `{ty_name}` by calling `{method_name}` on its dereferenced type"), - "consider using", - if ref_count > 1 { - format!("({}{recv_snip}).clone()", "*".repeat(ref_count - 1)) - } else { - format!("{recv_snip}.clone()") - }, - app, - ); - } + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && is_clone_like(cx, method_name, method_def_id) + && let return_type = cx.typeck_results().expr_ty(expr) + && let input_type = cx.typeck_results().expr_ty(recv) + && let (input_type, ref_count) = peel_mid_ty_refs(input_type) + && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) + && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) + && return_type == input_type + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, return_type, clone_trait, &[]) + { + let mut app = Applicability::MachineApplicable; + let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; + span_lint_and_sugg( + cx, + IMPLICIT_CLONE, + expr.span, + &format!("implicitly cloning a `{ty_name}` by calling `{method_name}` on its dereferenced type"), + "consider using", + if ref_count > 1 { + format!("({}{recv_snip}).clone()", "*".repeat(ref_count - 1)) + } else { + format!("{recv_snip}.clone()") + }, + app, + ); } } diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 6686d42c9..efc3ddd20 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -18,37 +17,36 @@ pub fn check( receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>], ) { - if_chain! { - if args.is_empty() && method_name == sym::to_string; - if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if cx.tcx.is_diagnostic_item(sym::to_string_method, to_string_meth_did); - if let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id); - let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver); - let self_ty = args.type_at(0); - let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty); - if deref_count >= 1; - if specializes_tostring(cx, deref_self_ty); - then { - span_lint_and_then( - cx, - INEFFICIENT_TO_STRING, - expr.span, - &format!("calling `to_string` on `{arg_ty}`"), - |diag| { - diag.help(format!( - "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" - )); - let mut applicability = Applicability::MachineApplicable; - let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); - diag.span_suggestion( - expr.span, - "try dereferencing the receiver", - format!("({}{arg_snippet}).to_string()", "*".repeat(deref_count)), - applicability, - ); - }, - ); - } + if args.is_empty() + && method_name == sym::to_string + && let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && cx.tcx.is_diagnostic_item(sym::to_string_method, to_string_meth_did) + && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) + && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) + && let self_ty = args.type_at(0) + && let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty) + && deref_count >= 1 + && specializes_tostring(cx, deref_self_ty) + { + span_lint_and_then( + cx, + INEFFICIENT_TO_STRING, + expr.span, + &format!("calling `to_string` on `{arg_ty}`"), + |diag| { + diag.help(format!( + "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" + )); + let mut applicability = Applicability::MachineApplicable; + let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); + diag.span_suggestion( + expr.span, + "try dereferencing the receiver", + format!("({}{arg_snippet}).to_string()", "*".repeat(deref_count)), + applicability, + ); + }, + ); } } diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index 8adf9e370..5ca841df5 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; use clippy_utils::ty::has_iter_method; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -19,24 +18,20 @@ pub(super) fn check( receiver: &hir::Expr<'_>, ) { let self_ty = cx.typeck_results().expr_ty_adjusted(receiver); - if_chain! { - if let ty::Ref(..) = self_ty.kind(); - if method_name == sym::into_iter; - if is_trait_method(cx, expr, sym::IntoIterator); - if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty); - then { - span_lint_and_sugg( - cx, - INTO_ITER_ON_REF, - method_span, - &format!( - "this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`", - ), - "call directly", - method_name.to_string(), - Applicability::MachineApplicable, - ); - } + if let ty::Ref(..) = self_ty.kind() + && method_name == sym::into_iter + && is_trait_method(cx, expr, sym::IntoIterator) + && let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty) + { + span_lint_and_sugg( + cx, + INTO_ITER_ON_REF, + method_span, + &format!("this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`",), + "call directly", + method_name.to_string(), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index bde6f92b0..dd741cd43 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,7 +1,6 @@ use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,22 +9,21 @@ use rustc_span::sym; use super::ITER_CLONED_COLLECT; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) { - if_chain! { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec); - if let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)); - if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); - - then { - span_lint_and_sugg( - cx, - ITER_CLONED_COLLECT, - to_replace, - &format!("called `iter().{method_name}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ - more readable"), - "try", - ".to_vec()".to_string(), - Applicability::MachineApplicable, - ); - } + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) + && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) + && let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()) + { + span_lint_and_sugg( + cx, + ITER_CLONED_COLLECT, + to_replace, + &format!( + "called `iter().{method_name}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ + more readable" + ), + "try", + ".to_vec()".to_string(), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 625325d4c..c5dbb6ad9 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -22,64 +22,54 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'tcx>, // hashmap m_arg: &'tcx Expr<'tcx>, // |(_, v)| v ) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::Closure(c) = m_arg.kind; - if let Body {params: [p], value: body_expr, coroutine_kind: _ } = cx.tcx.hir().body(c.body); - if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind; - - let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) { + if !expr.span.from_expansion() + && let ExprKind::Closure(c) = m_arg.kind + && let Body { + params: [p], + value: body_expr, + coroutine_kind: _, + } = cx.tcx.hir().body(c.body) + && let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind + && let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) { (key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value), (PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key), _ => return, - }; + } + && let ty = cx.typeck_results().expr_ty(recv) + && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + { + let mut applicability = rustc_errors::Applicability::MachineApplicable; + let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); + let into_prefix = if map_type == "into_iter" { "into_" } else { "" }; - let ty = cx.typeck_results().expr_ty(recv); - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap); - - then { - let mut applicability = rustc_errors::Applicability::MachineApplicable; - let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); - let into_prefix = if map_type == "into_iter" {"into_"} else {""}; - - if_chain! { - if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind; - if let [local_ident] = path.segments; - if local_ident.ident.as_str() == bound_ident.as_str(); - - then { - span_lint_and_sugg( - cx, - ITER_KV_MAP, - expr.span, - &format!("iterating on a map's {replacement_kind}s"), - "try", - format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"), - applicability, - ); - } else { - let ref_annotation = if annotation.0 == ByRef::Yes { - "ref " - } else { - "" - }; - let mut_annotation = if annotation.1 == Mutability::Mut { - "mut " - } else { - "" - }; - span_lint_and_sugg( - cx, - ITER_KV_MAP, - expr.span, - &format!("iterating on a map's {replacement_kind}s"), - "try", - format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})", - snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), - applicability, - ); - } - } + if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind + && let [local_ident] = path.segments + && local_ident.ident.as_str() == bound_ident.as_str() + { + span_lint_and_sugg( + cx, + ITER_KV_MAP, + expr.span, + &format!("iterating on a map's {replacement_kind}s"), + "try", + format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"), + applicability, + ); + } else { + let ref_annotation = if annotation.0 == ByRef::Yes { "ref " } else { "" }; + let mut_annotation = if annotation.1 == Mutability::Mut { "mut " } else { "" }; + span_lint_and_sugg( + cx, + ITER_KV_MAP, + expr.span, + &format!("iterating on a map's {replacement_kind}s"), + "try", + format!( + "{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})", + snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability) + ), + applicability, + ); } } } diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index 8f885e9f7..fd4650e1e 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, higher}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -26,29 +25,36 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() { // caller is a Slice - 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::hir(index_expr); - if let hir::ExprKind::Lit(start_lit) = &start_expr.kind; - if let ast::LitKind::Int(start_idx, _) = start_lit.node; - then { - let mut applicability = Applicability::MachineApplicable; - let suggest = if start_idx == 0 { - format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) - } else { - format!("{}.get({start_idx})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) - }; - span_lint_and_sugg( - cx, - ITER_NEXT_SLICE, - expr.span, - "using `.iter().next()` on a Slice without end index", - "try calling", - suggest, - applicability, - ); - } + if let hir::ExprKind::Index(caller_var, index_expr, _) = &caller_expr.kind + && let Some(higher::Range { + start: Some(start_expr), + end: None, + limits: ast::RangeLimits::HalfOpen, + }) = higher::Range::hir(index_expr) + && let hir::ExprKind::Lit(start_lit) = &start_expr.kind + && let ast::LitKind::Int(start_idx, _) = start_lit.node + { + let mut applicability = Applicability::MachineApplicable; + let suggest = if start_idx == 0 { + format!( + "{}.first()", + snippet_with_applicability(cx, caller_var.span, "..", &mut applicability) + ) + } else { + format!( + "{}.get({start_idx})", + snippet_with_applicability(cx, caller_var.span, "..", &mut applicability) + ) + }; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "using `.iter().next()` on a Slice without end index", + "try calling", + suggest, + applicability, + ); } } else if is_vec_or_array(cx, caller_expr) { // caller is a Vec or an Array diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index 39af52141..fbe20dfe5 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -19,18 +19,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr expr.span.trim_start(recv.span).unwrap(), "called `skip(..).next()` on an iterator", |diag| { - if_chain! { - if let Some(id) = path_to_local(recv); - if let Node::Pat(pat) = cx.tcx.hir().get(id); - if let PatKind::Binding(ann, _, _, _) = pat.kind; - if ann != BindingAnnotation::MUT; - then { - application = Applicability::Unspecified; - diag.span_help( - pat.span, - format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), - ); - } + if let Some(id) = path_to_local(recv) + && let Node::Pat(pat) = cx.tcx.hir().get(id) + && let PatKind::Binding(ann, _, _, _) = pat.kind + && ann != BindingAnnotation::MUT + { + application = Applicability::Unspecified; + diag.span_help( + pat.span, + format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), + ); } diag.span_suggestion( diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index 3031193e5..b1af0083e 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -18,30 +17,26 @@ pub(super) fn check<'tcx>( or_expr: &'tcx Expr<'_>, map_expr: &'tcx Expr<'_>, ) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option); - if let ExprKind::Call(err_path, [err_arg]) = or_expr.kind; - if is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr); - if is_ok_wrapping(cx, map_expr); - if let Some(recv_snippet) = snippet_opt(cx, recv.span); - if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); - if let Some(indent) = indent_of(cx, expr.span); - then { - let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); - span_lint_and_sugg( - cx, - MANUAL_OK_OR, - expr.span, - "this pattern reimplements `Option::ok_or`", - "replace with", - format!( - "{recv_snippet}.ok_or({reindented_err_arg_snippet})" - ), - Applicability::MachineApplicable, - ); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option) + && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind + && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) + && is_ok_wrapping(cx, map_expr) + && let Some(recv_snippet) = snippet_opt(cx, recv.span) + && let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span) + && let Some(indent) = indent_of(cx, expr.span) + { + let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); + span_lint_and_sugg( + cx, + MANUAL_OK_OR, + expr.span, + "this pattern reimplements `Option::ok_or`", + "replace with", + format!("{recv_snippet}.ok_or({reindented_err_arg_snippet})"), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 540425eef..04bdbc1ea 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{match_def_path, path_def_id}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -69,16 +68,14 @@ enum MinMax { fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { // `T::max_value()` `T::min_value()` inherent methods - if_chain! { - if let hir::ExprKind::Call(func, args) = &expr.kind; - if args.is_empty(); - if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind; - then { - match segment.ident.as_str() { - "max_value" => return Some(MinMax::Max), - "min_value" => return Some(MinMax::Min), - _ => {} - } + if let hir::ExprKind::Call(func, args) = &expr.kind + && args.is_empty() + && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind + { + match segment.ident.as_str() { + "max_value" => return Some(MinMax::Max), + "min_value" => return Some(MinMax::Min), + _ => {}, } } diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index ab13d30d8..61e74369c 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -3,7 +3,6 @@ use clippy_utils::is_path_diagnostic_item; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -55,43 +54,42 @@ pub(super) fn check( take_self_arg: &Expr<'_>, take_arg: &Expr<'_>, ) { - if_chain! { - if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; - if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); - if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String); - if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); - if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id); - if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg); - let ctxt = collect_expr.span.ctxt(); - if ctxt == take_expr.span.ctxt(); - if ctxt == take_self_arg.span.ctxt(); - then { - let mut app = Applicability::MachineApplicable; - let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0; + if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind + && is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat) + && is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String) + && let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id) + && let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && cx.tcx.trait_of_item(take_id) == Some(iter_trait_id) + && let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg) + && let ctxt = collect_expr.span.ctxt() + && ctxt == take_expr.span.ctxt() + && ctxt == take_self_arg.span.ctxt() + { + let mut app = Applicability::MachineApplicable; + let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0; - let val_str = match repeat_kind { - RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return, - RepeatKind::Char('\'') => r#""'""#.into(), - RepeatKind::Char('"') => r#""\"""#.into(), - RepeatKind::Char(_) => - match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) { - Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])), - s @ Cow::Borrowed(_) => s, - }, - RepeatKind::String => - Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app).maybe_par().to_string().into(), - }; + let val_str = match repeat_kind { + RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return, + RepeatKind::Char('\'') => r#""'""#.into(), + RepeatKind::Char('"') => r#""\"""#.into(), + RepeatKind::Char(_) => match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) { + Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])), + s @ Cow::Borrowed(_) => s, + }, + RepeatKind::String => Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app) + .maybe_par() + .to_string() + .into(), + }; - span_lint_and_sugg( - cx, - MANUAL_STR_REPEAT, - collect_expr.span, - "manual implementation of `str::repeat` using iterators", - "try", - format!("{val_str}.repeat({count_snip})"), - app - ) - } + span_lint_and_sugg( + cx, + MANUAL_STR_REPEAT, + collect_expr.span, + "manual implementation of `str::repeat` using iterators", + "try", + format!("{val_str}.repeat({count_snip})"), + app, + ); } } diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index e0f8cb1b9..cc6eeaa86 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use clippy_utils::{is_diag_trait_item, peel_blocks}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,57 +15,55 @@ use rustc_span::{sym, Span}; use super::MAP_CLONE; pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id); - if cx.tcx.impl_of_method(method_id) - .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option)) - || is_diag_trait_item(cx, method_id, sym::Iterator); - if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind; - then { - let closure_body = cx.tcx.hir().body(body); - let closure_expr = peel_blocks(closure_body.value); - match closure_body.params[0].pat.kind { - hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding( - hir::BindingAnnotation::NONE, .., name, None - ) = inner.kind { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && (cx.tcx.impl_of_method(method_id).map_or(false, |id| { + is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option) + }) || is_diag_trait_item(cx, method_id, sym::Iterator)) + && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind + { + let closure_body = cx.tcx.hir().body(body); + let closure_expr = peel_blocks(closure_body.value); + match closure_body.params[0].pat.kind { + hir::PatKind::Ref(inner, hir::Mutability::Not) => { + if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind { if ident_eq(name, closure_expr) { lint_explicit_closure(cx, e.span, recv.span, true, msrv); } - }, - hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => { - match closure_expr.kind { - hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { - if ident_eq(name, inner) { - if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { - lint_explicit_closure(cx, e.span, recv.span, true, msrv); - } + } + }, + hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => { + match closure_expr.kind { + hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { + if ident_eq(name, inner) { + if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); } - }, - hir::ExprKind::MethodCall(method, obj, [], _) => if_chain! { - if ident_eq(name, obj) && method.ident.name == sym::clone; - if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id); - if let Some(trait_id) = cx.tcx.trait_of_item(fn_id); - if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id); - // no autoderefs - if !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); - then { - let obj_ty = cx.typeck_results().expr_ty(obj); - if let ty::Ref(_, ty, mutability) = obj_ty.kind() { - if matches!(mutability, Mutability::Not) { - let copy = is_copy(cx, *ty); - lint_explicit_closure(cx, e.span, recv.span, copy, msrv); - } - } else { - lint_needless_cloning(cx, e.span, recv.span); + } + }, + hir::ExprKind::MethodCall(method, obj, [], _) => { + if ident_eq(name, obj) && method.ident.name == sym::clone + && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) + && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id) + // no autoderefs + && !cx.typeck_results().expr_adjustments(obj).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + { + let obj_ty = cx.typeck_results().expr_ty(obj); + if let ty::Ref(_, ty, mutability) = obj_ty.kind() { + if matches!(mutability, Mutability::Not) { + let copy = is_copy(cx, *ty); + lint_explicit_closure(cx, e.span, recv.span, copy, msrv); } + } else { + lint_needless_cloning(cx, e.span, recv.span); } - }, - _ => {}, - } - }, - _ => {}, - } + } + }, + _ => {}, + } + }, + _ => {}, } } } diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index 01cdd02e6..e944eac91 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -13,26 +12,24 @@ use super::MAP_COLLECT_RESULT_UNIT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { // return of collect `Result<(),_>` let collect_ret_ty = cx.typeck_results().expr_ty(expr); - if_chain! { - if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result); - if let ty::Adt(_, args) = collect_ret_ty.kind(); - if let Some(result_t) = args.types().next(); - if result_t.is_unit(); - // get parts for snippet - then { - span_lint_and_sugg( - cx, - MAP_COLLECT_RESULT_UNIT, - expr.span, - "`.map().collect()` can be replaced with `.try_for_each()`", - "try", - format!( - "{}.try_for_each({})", - snippet(cx, iter.span, ".."), - snippet(cx, map_fn.span, "..") - ), - Applicability::MachineApplicable, - ); - } + if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result) + && let ty::Adt(_, args) = collect_ret_ty.kind() + && let Some(result_t) = args.types().next() + && result_t.is_unit() + // get parts for snippet + { + span_lint_and_sugg( + cx, + MAP_COLLECT_RESULT_UNIT, + expr.span, + "`.map().collect()` can be replaced with `.try_for_each()`", + "try", + format!( + "{}.try_for_each({})", + snippet(cx, iter.span, ".."), + snippet(cx, map_fn.span, "..") + ), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 57581363c..d10724758 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -19,22 +19,20 @@ pub(super) fn check( ) { let caller_ty = cx.typeck_results().expr_ty(caller); - if_chain! { - if is_trait_method(cx, expr, sym::Iterator) - || is_type_diagnostic_item(cx, caller_ty, sym::Result) - || is_type_diagnostic_item(cx, caller_ty, sym::Option); - if is_expr_untyped_identity_function(cx, map_arg); - if let Some(sugg_span) = expr.span.trim_start(caller.span); - then { - span_lint_and_sugg( - cx, - MAP_IDENTITY, - sugg_span, - "unnecessary map of the identity function", - &format!("remove the call to `{name}`"), - String::new(), - Applicability::MachineApplicable, - ) - } + if (is_trait_method(cx, expr, sym::Iterator) + || is_type_diagnostic_item(cx, caller_ty, sym::Result) + || is_type_diagnostic_item(cx, caller_ty, sym::Option)) + && is_expr_untyped_identity_function(cx, map_arg) + && let Some(sugg_span) = expr.span.trim_start(caller.span) + { + span_lint_and_sugg( + cx, + MAP_IDENTITY, + sugg_span, + "unnecessary map of the identity function", + &format!("remove the call to `{name}`"), + String::new(), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a71a136eb..ab376d669 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -123,7 +123,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; -use if_chain::if_chain; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -3977,44 +3976,41 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - if_chain! { - if let TraitItemKind::Fn(ref sig, _) = item.kind; - if sig.decl.implicit_self.has_implicit_self(); - if let Some(first_arg_hir_ty) = sig.decl.inputs.first(); - if let Some(&first_arg_ty) = cx.tcx.fn_sig(item.owner_id) + if let TraitItemKind::Fn(ref sig, _) = item.kind + && sig.decl.implicit_self.has_implicit_self() + && let Some(first_arg_hir_ty) = sig.decl.inputs.first() + && let Some(&first_arg_ty) = cx + .tcx + .fn_sig(item.owner_id) .instantiate_identity() .inputs() .skip_binder() - .first(); - then { - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); - wrong_self_convention::check( - cx, - item.ident.name.as_str(), - self_ty, - first_arg_ty, - first_arg_hir_ty.span, - false, - true, - ); - } + .first() + { + let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); + wrong_self_convention::check( + cx, + item.ident.name.as_str(), + self_ty, + first_arg_ty, + first_arg_hir_ty.span, + false, + true, + ); } - if_chain! { - if item.ident.name == sym::new; - if let TraitItemKind::Fn(_, _) = item.kind; - let ret_ty = return_ty(cx, item.owner_id); - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); - if !ret_ty.contains(self_ty); - - then { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); - } + if item.ident.name == sym::new + && let TraitItemKind::Fn(_, _) = item.kind + && let ret_ty = return_ty(cx, item.owner_id) + && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() + && !ret_ty.contains(self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); } } diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 2855e23bf..1a0fce287 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -11,22 +10,20 @@ use rustc_span::{sym, Span}; use super::MUT_MUTEX_LOCK; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { - if_chain! { - if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)); - if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind(); - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex); - then { - span_lint_and_sugg( - cx, - MUT_MUTEX_LOCK, - name_span, - "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", - "change this to", - "get_mut".to_owned(), - Applicability::MaybeIncorrect, - ); - } + if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)) + && let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind() + && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) + { + span_lint_and_sugg( + cx, + MUT_MUTEX_LOCK, + name_span, + "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", + "change this to", + "get_mut".to_owned(), + Applicability::MaybeIncorrect, + ); } } diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index 01655e860..81df32bde 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_lang_item; use clippy_utils::SpanlessEq; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; @@ -19,17 +18,13 @@ pub(super) fn check<'tcx>( return; } - if_chain! { - if let ExprKind::Lit(spanned) = &arg1.kind; - if let Some(param1) = lit_string_value(&spanned.node); - - if let ExprKind::Lit(spanned) = &arg2.kind; - if let LitKind::Str(param2, _) = &spanned.node; - if param1 == param2.as_str(); - - then { - span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); - } + if let ExprKind::Lit(spanned) = &arg1.kind + && let Some(param1) = lit_string_value(&spanned.node) + && let ExprKind::Lit(spanned) = &arg2.kind + && let LitKind::Str(param2, _) = &spanned.node + && param1 == param2.as_str() + { + span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); } if SpanlessEq::new(cx).eq_expr(arg1, arg2) { diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index f2ef42933..e10bc0216 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -10,23 +9,20 @@ use super::OK_EXPECT; /// lint use of `ok().expect()` for `Result`s pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if_chain! { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) // lint if the caller of `ok()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - let result_type = cx.typeck_results().expr_ty(recv); - if let Some(error_type) = get_error_type(cx, result_type); - if has_debug_impl(cx, error_type); - - then { - span_lint_and_help( - cx, - OK_EXPECT, - expr.span, - "called `ok().expect()` on a `Result` value", - None, - "you can call `expect()` directly on the `Result`", - ); - } + && let result_type = cx.typeck_results().expr_ty(recv) + && let Some(error_type) = get_error_type(cx, result_type) + && has_debug_impl(cx, error_type) + { + span_lint_and_help( + cx, + OK_EXPECT, + expr.span, + "called `ok().expect()` on a `Result` value", + None, + "you can call `expect()` directly on the `Result`", + ); } } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 7b81d4571..151110061 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -58,34 +57,30 @@ pub(super) fn check( match &closure_expr.kind { hir::ExprKind::MethodCall(_, receiver, [], _) => { - if_chain! { - if path_to_local_id(receiver, closure_body.params[0].pat.hir_id); - let adj = cx + if path_to_local_id(receiver, closure_body.params[0].pat.hir_id) + && let adj = cx .typeck_results() .expr_adjustments(receiver) .iter() .map(|x| &x.kind) - .collect::>(); - if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; - then { - let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - cx.tcx.is_diagnostic_item(sym::deref_method, method_did) - || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) - || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) - } else { - false - } + .collect::>() + && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj + { + let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); + cx.tcx.is_diagnostic_item(sym::deref_method, method_did) + || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) + || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + } else { + false } }, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => { - if_chain! { - if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind; - if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind; - then { - path_to_local_id(inner2, closure_body.params[0].pat.hir_id) - } else { - false - } + if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind + && let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind + { + path_to_local_id(inner2, closure_body.params[0].pat.hir_id) + } else { + false } }, _ => false, diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index cb6a23068..418e6a7d6 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -58,27 +58,25 @@ pub(super) fn check<'tcx>( if is_option { let self_snippet = snippet(cx, recv.span, ".."); - if_chain! { - if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind; - let arg_snippet = snippet(cx, fn_decl_span, ".."); - let body = cx.tcx.hir().body(body); - if let Some((func, [arg_char])) = reduce_unit_expression(body.value); - if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)); - if Some(id) == cx.tcx.lang_items().option_some_variant(); - then { - let func_snippet = snippet(cx, arg_char.span, ".."); - let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ - `map(..)` instead"; - return span_lint_and_sugg( - cx, - OPTION_MAP_OR_NONE, - expr.span, - msg, - "try using `map` instead", - format!("{self_snippet}.map({arg_snippet} {func_snippet})"), - Applicability::MachineApplicable, - ); - } + if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind + && let arg_snippet = snippet(cx, fn_decl_span, "..") + && let body = cx.tcx.hir().body(body) + && let Some((func, [arg_char])) = reduce_unit_expression(body.value) + && let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)) + && Some(id) == cx.tcx.lang_items().option_some_variant() + { + let func_snippet = snippet(cx, arg_char.span, ".."); + let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ + `map(..)` instead"; + return span_lint_and_sugg( + cx, + OPTION_MAP_OR_NONE, + expr.span, + msg, + "try using `map` instead", + format!("{self_snippet}.map({arg_snippet} {func_snippet})"), + Applicability::MachineApplicable, + ); } let func_snippet = snippet(cx, map_arg.span, ".."); diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index b942346d5..dd669e499 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -3,7 +3,6 @@ use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; @@ -131,54 +130,47 @@ pub(super) fn check<'tcx>( (sym::Result, true, &["or", "unwrap_or"], "else"), ]; - if_chain! { - if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); - - if switch_to_lazy_eval(cx, arg); - if !contains_return(arg); - - let self_ty = cx.typeck_results().expr_ty(self_expr); - - if let Some(&(_, fn_has_arguments, poss, suffix)) = - KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0)); - - if poss.contains(&name); - - then { - let ctxt = span.ctxt(); - let mut app = Applicability::HasPlaceholders; - let sugg = { - let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { - (false, Some(fun_span)) => (fun_span, false), - _ => (arg.span, true), - }; - - let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; - let snip = if use_lambda { - let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{l_arg}| {snip}") - } else { - snip.into_owned() - }; - - if let Some(f) = second_arg { - let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; - format!("{snip}, {f}") - } else { - snip - } + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) + && switch_to_lazy_eval(cx, arg) + && !contains_return(arg) + && let self_ty = cx.typeck_results().expr_ty(self_expr) + && let Some(&(_, fn_has_arguments, poss, suffix)) = + KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0)) + && poss.contains(&name) + { + let ctxt = span.ctxt(); + let mut app = Applicability::HasPlaceholders; + let sugg = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), }; - let span_replace_word = method_span.with_hi(span.hi()); - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span_replace_word, - &format!("use of `{name}` followed by a function call"), - "try", - format!("{name}_{suffix}({sugg})"), - app, - ); - } + + let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; + let snip = if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{l_arg}| {snip}") + } else { + snip.into_owned() + }; + + if let Some(f) = second_arg { + let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; + format!("{snip}, {f}") + } else { + snip + } + }; + let span_replace_word = method_span.with_hi(span.hi()); + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span_replace_word, + &format!("use of `{name}` followed by a function call"), + "try", + format!("{name}_{suffix}({sugg})"), + app, + ); } } diff --git a/clippy_lints/src/methods/path_buf_push_overwrite.rs b/clippy_lints/src/methods/path_buf_push_overwrite.rs index 1c07d2a3a..04a27cc98 100644 --- a/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -11,27 +10,25 @@ use std::path::{Component, Path}; use super::PATH_BUF_PUSH_OVERWRITE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf); - if let ExprKind::Lit(lit) = arg.kind; - if let LitKind::Str(ref path_lit, _) = lit.node; - if let pushed_path = Path::new(path_lit.as_str()); - if let Some(pushed_path_lit) = pushed_path.to_str(); - if pushed_path.has_root(); - if let Some(root) = pushed_path.components().next(); - if root == Component::RootDir; - then { - span_lint_and_sugg( - cx, - PATH_BUF_PUSH_OVERWRITE, - lit.span, - "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", - "try", - format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), - Applicability::MachineApplicable, - ); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf) + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Str(ref path_lit, _) = lit.node + && let pushed_path = Path::new(path_lit.as_str()) + && let Some(pushed_path_lit) = pushed_path.to_str() + && pushed_path.has_root() + && let Some(root) = pushed_path.components().next() + && root == Component::RootDir + { + span_lint_and_sugg( + cx, + PATH_BUF_PUSH_OVERWRITE, + lit.span, + "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", + "try", + format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index f253d8de9..1148628b0 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; use clippy_utils::{higher, is_integer_const, is_trait_method, SpanlessEq}; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,25 +8,26 @@ use rustc_span::sym; use super::RANGE_ZIP_WITH_LEN; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) { - if_chain! { - if is_trait_method(cx, expr, sym::Iterator); + if is_trait_method(cx, expr, sym::Iterator) // range expression in `.zip()` call: `0..x.len()` - if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg); - if is_integer_const(cx, start, 0); + && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg) + && is_integer_const(cx, start, 0) // `.len()` call - if let ExprKind::MethodCall(len_path, len_recv, [], _) = end.kind; - if len_path.ident.name == sym::len; + && let ExprKind::MethodCall(len_path, len_recv, [], _) = end.kind + && len_path.ident.name == sym::len // `.iter()` and `.len()` called on same `Path` - if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind; - if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind; - if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); - then { - span_lint(cx, - RANGE_ZIP_WITH_LEN, - expr.span, - &format!("it is more idiomatic to use `{}.iter().enumerate()`", - snippet(cx, recv.span, "_")) - ); - } + && let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind + && let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind + && SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments) + { + span_lint( + cx, + RANGE_ZIP_WITH_LEN, + expr.span, + &format!( + "it is more idiomatic to use `{}.iter().enumerate()`", + snippet(cx, recv.span, "_") + ), + ); } } diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 04ddaaa2f..9b90a3df7 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -3,7 +3,6 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_trait_method, strip_pat_refs}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::PatKind; @@ -35,29 +34,27 @@ pub(super) fn check<'tcx>( // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` let mut applicability = Applicability::MachineApplicable; - let any_search_snippet = if_chain! { - if search_method == "find"; - if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind; - let closure_body = cx.tcx.hir().body(body); - if let Some(closure_arg) = closure_body.params.first(); - then { - if let hir::PatKind::Ref(..) = closure_arg.pat.kind { - Some(search_snippet.replacen('&', "", 1)) - } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { - // `find()` provides a reference to the item, but `any` does not, - // so we should fix item usages for suggestion - if let Some(closure_sugg) = deref_closure_args(cx, search_arg) { - applicability = closure_sugg.applicability; - Some(closure_sugg.suggestion) - } else { - Some(search_snippet.to_string()) - } + let any_search_snippet = if search_method == "find" + && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind + && let closure_body = cx.tcx.hir().body(body) + && let Some(closure_arg) = closure_body.params.first() + { + if let hir::PatKind::Ref(..) = closure_arg.pat.kind { + Some(search_snippet.replacen('&', "", 1)) + } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { + // `find()` provides a reference to the item, but `any` does not, + // so we should fix item usages for suggestion + if let Some(closure_sugg) = deref_closure_args(cx, search_arg) { + applicability = closure_sugg.applicability; + Some(closure_sugg.suggestion) } else { - None + Some(search_snippet.to_string()) } } else { None } + } else { + None }; // add note if not multi-line if is_some { @@ -110,41 +107,37 @@ pub(super) fn check<'tcx>( self_ty.is_str() } }; - if_chain! { - if is_string_or_str_slice(search_recv); - if is_string_or_str_slice(search_arg); - then { - let msg = format!("called `{option_check_method}()` after calling `find()` on a string"); - match option_check_method { - "is_some" => { - let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - &msg, - "use `contains()` instead", - format!("contains({find_arg})"), - applicability, - ); - }, - "is_none" => { - let string = snippet(cx, search_recv.span, ".."); - let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - expr.span, - &msg, - "use `!_.contains()` instead", - format!("!{string}.contains({find_arg})"), - applicability, - ); - }, - _ => (), - } + if is_string_or_str_slice(search_recv) && is_string_or_str_slice(search_arg) { + let msg = format!("called `{option_check_method}()` after calling `find()` on a string"); + match option_check_method { + "is_some" => { + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `contains()` instead", + format!("contains({find_arg})"), + applicability, + ); + }, + "is_none" => { + let string = snippet(cx, search_recv.span, ".."); + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + &msg, + "use `!_.contains()` instead", + format!("!{string}.contains({find_arg})"), + applicability, + ); + }, + _ => (), } } } diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs index 4d704ec39..3983f0c0c 100644 --- a/clippy_lints/src/methods/single_char_pattern.rs +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -1,6 +1,5 @@ use super::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -45,24 +44,23 @@ pub(super) fn check( args: &[hir::Expr<'_>], ) { for &(method, pos) in &PATTERN_METHODS { - if_chain! { - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind(); - if ty.is_str(); - if method_name.as_str() == method && args.len() > pos; - let arg = &args[pos]; - let mut applicability = Applicability::MachineApplicable; - if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability); - then { - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - applicability, - ); - } + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() + && ty.is_str() + && method_name.as_str() == method + && args.len() > pos + && let arg = &args[pos] + && let mut applicability = Applicability::MachineApplicable + && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) + { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); } } } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 9da61bca5..0e7ad8fc9 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -6,7 +6,6 @@ use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, @@ -286,41 +285,35 @@ fn parse_iter_usage<'tcx>( match (name.ident.as_str(), args) { ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { - return if_chain! { - if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE); - if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind(); - if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()); - if let ty::Tuple(subs) = subs.type_at(0).kind(); - if subs.len() == 2; - then { - Some(IterUsage { - kind: IterUsageKind::NextTuple, - span: e.span, - unwrap_kind: None - }) - } else { - None - } + return if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE) + && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind() + && cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) + && let ty::Tuple(subs) = subs.type_at(0).kind() + && subs.len() == 2 + { + Some(IterUsage { + kind: IterUsageKind::NextTuple, + span: e.span, + unwrap_kind: None, + }) + } else { + None }; }, ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { if let Some(Constant::Int(idx)) = constant(cx, cx.typeck_results(), idx_expr) { let span = if name.ident.as_str() == "nth" { e.span + } else if let Some((_, Node::Expr(next_expr))) = iter.next() + && let ExprKind::MethodCall(next_name, _, [], _) = next_expr.kind + && next_name.ident.name == sym::next + && next_expr.span.ctxt() == ctxt + && let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id) + && cx.tcx.trait_of_item(next_id) == Some(iter_id) + { + next_expr.span } else { - if_chain! { - if let Some((_, Node::Expr(next_expr))) = iter.next(); - if let ExprKind::MethodCall(next_name, _, [], _) = next_expr.kind; - if next_name.ident.name == sym::next; - if next_expr.span.ctxt() == ctxt; - if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id); - if cx.tcx.trait_of_item(next_id) == Some(iter_id); - then { - next_expr.span - } else { - return None; - } - } + return None; }; (IterUsageKind::Nth(idx), span) } else { diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 0dc7fe2a2..ed49233ac 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::usage::mutated_variables; use clippy_utils::{expr_or_init, is_trait_method}; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,26 +8,24 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { - if_chain! { - if is_trait_method(cx, count_recv, sym::Iterator); - if let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind; - let closure_body = cx.tcx.hir().body(closure.body); - if !cx.typeck_results().expr_ty(closure_body.value).is_unit(); - then { - if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) { - // A variable is used mutably inside of the closure. Suppress the lint. - if !map_mutated_vars.is_empty() { - return; - } + if is_trait_method(cx, count_recv, sym::Iterator) + && let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind + && let closure_body = cx.tcx.hir().body(closure.body) + && !cx.typeck_results().expr_ty(closure_body.value).is_unit() + { + if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) { + // A variable is used mutably inside of the closure. Suppress the lint. + if !map_mutated_vars.is_empty() { + return; } - span_lint_and_help( - cx, - SUSPICIOUS_MAP, - expr.span, - "this call to `map()` won't have an effect on the call to `count()`", - None, - "make sure you did not confuse `map` with `filter`, `for_each` or `inspect`", - ); } + span_lint_and_help( + cx, + SUSPICIOUS_MAP, + expr.span, + "this call to `map()` won't have an effect on the call to `count()`", + None, + "make sure you did not confuse `map` with `filter`, `for_each` or `inspect`", + ); } } diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 3cb2719e4..c45212581 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_note; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -8,41 +7,36 @@ use rustc_span::source_map::Spanned; use super::SUSPICIOUS_SPLITN; pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { - if_chain! { - if count <= 1; - if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(call_id); - if cx.tcx.impl_trait_ref(impl_id).is_none(); - let self_ty = cx.tcx.type_of(impl_id).instantiate_identity(); - if self_ty.is_slice() || self_ty.is_str(); - then { - // Ignore empty slice and string literals when used with a literal count. - if matches!(self_arg.kind, ExprKind::Array([])) - || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()) - { - return; - } + if count <= 1 + && let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(call_id) + && cx.tcx.impl_trait_ref(impl_id).is_none() + && let self_ty = cx.tcx.type_of(impl_id).instantiate_identity() + && (self_ty.is_slice() || self_ty.is_str()) + { + // Ignore empty slice and string literals when used with a literal count. + if matches!(self_arg.kind, ExprKind::Array([])) + || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()) + { + return; + } - let (msg, note_msg) = if count == 0 { - (format!("`{method_name}` called with `0` splits"), - "the resulting iterator will always return `None`") - } else { - (format!("`{method_name}` called with `1` split"), + let (msg, note_msg) = if count == 0 { + ( + format!("`{method_name}` called with `0` splits"), + "the resulting iterator will always return `None`", + ) + } else { + ( + format!("`{method_name}` called with `1` split"), if self_ty.is_slice() { "the resulting iterator will always return the entire slice followed by `None`" } else { "the resulting iterator will always return the entire string followed by `None`" - }) - }; + }, + ) + }; - span_lint_and_note( - cx, - SUSPICIOUS_SPLITN, - expr.span, - &msg, - None, - note_msg, - ); - } + span_lint_and_note(cx, SUSPICIOUS_SPLITN, expr.span, &msg, None, note_msg); } } diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index 9eb8d6e6e..60864902a 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; use clippy_utils::source::snippet_with_context; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,40 +11,37 @@ use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool { - if_chain! { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diag_trait_item(cx, method_def_id, sym::ToOwned); - let input_type = cx.typeck_results().expr_ty(expr); - if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind(); - if cx.tcx.is_diagnostic_item(sym::Cow, adt.did()); - - then { - let mut app = Applicability::MaybeIncorrect; - let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; - span_lint_and_then( - cx, - SUSPICIOUS_TO_OWNED, - expr.span, - &with_forced_trimmed_paths!(format!( - "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" - )), - |diag| { - diag.span_suggestion( - expr.span, - "depending on intent, either make the Cow an Owned variant", - format!("{recv_snip}.into_owned()"), - app - ); - diag.span_suggestion( - expr.span, - "or clone the Cow itself", - format!("{recv_snip}.clone()"), - app - ); - } - ); - return true; - } + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && is_diag_trait_item(cx, method_def_id, sym::ToOwned) + && let input_type = cx.typeck_results().expr_ty(expr) + && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind() + && cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) + { + let mut app = Applicability::MaybeIncorrect; + let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; + span_lint_and_then( + cx, + SUSPICIOUS_TO_OWNED, + expr.span, + &with_forced_trimmed_paths!(format!( + "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" + )), + |diag| { + diag.span_suggestion( + expr.span, + "depending on intent, either make the Cow an Owned variant", + format!("{recv_snip}.into_owned()"), + app, + ); + diag.span_suggestion( + expr.span, + "or clone the Cow itself", + format!("{recv_snip}.clone()"), + app, + ); + }, + ); + return true; } false } diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index bc9c518db..1ee655d61 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_path_diagnostic_item; use clippy_utils::ty::is_uninit_value_valid_for_ty; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -10,18 +9,16 @@ use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::Call(callee, args) = recv.kind; - if args.is_empty(); - if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit); - if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)); - then { - span_lint( - cx, - UNINIT_ASSUMED_INIT, - expr.span, - "this call for this type may be undefined behavior" - ); - } + if let hir::ExprKind::Call(callee, args) = recv.kind + && args.is_empty() + && is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit) + && !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)) + { + span_lint( + cx, + UNINIT_ASSUMED_INIT, + expr.span, + "this call for this type may be undefined behavior", + ); } } diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 6e23754bf..7afd3802f 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -61,57 +60,51 @@ fn check_fold_with_op( op: hir::BinOpKind, replacement: Replacement, ) { - if_chain! { + if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind // Extract the body of the closure passed to fold - if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind; - let closure_body = cx.tcx.hir().body(body); - let closure_expr = peel_blocks(closure_body.value); + && let closure_body = cx.tcx.hir().body(body) + && let closure_expr = peel_blocks(closure_body.value) // Check if the closure body is of the form `acc some_expr(x)` - if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind; - if bin_op.node == op; + && let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind + && bin_op.node == op // Extract the names of the two arguments to the closure - if let [param_a, param_b] = closure_body.params; - if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind; - if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind; + && let [param_a, param_b] = closure_body.params + && let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind + && let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind - if path_to_local_id(left_expr, first_arg_id); - if replacement.has_args || path_to_local_id(right_expr, second_arg_id); + && path_to_local_id(left_expr, first_arg_id) + && (replacement.has_args || path_to_local_id(right_expr, second_arg_id)) + { + let mut applicability = Applicability::MachineApplicable; - then { - let mut applicability = Applicability::MachineApplicable; + let turbofish = if replacement.has_generic_return { + format!("::<{}>", cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs()) + } else { + String::new() + }; - let turbofish = if replacement.has_generic_return { - format!("::<{}>", cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs()) - } else { - String::new() - }; + let sugg = if replacement.has_args { + format!( + "{method}{turbofish}(|{second_arg_ident}| {r})", + method = replacement.method_name, + r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), + ) + } else { + format!("{method}{turbofish}()", method = replacement.method_name,) + }; - let sugg = if replacement.has_args { - format!( - "{method}{turbofish}(|{second_arg_ident}| {r})", - method = replacement.method_name, - r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), - ) - } else { - format!( - "{method}{turbofish}()", - method = replacement.method_name, - ) - }; - - span_lint_and_sugg( - cx, - UNNECESSARY_FOLD, - fold_span.with_hi(expr.span.hi()), - // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) - "this `.fold` can be written more succinctly using another method", - "try", - sugg, - applicability, - ); - } + span_lint_and_sugg( + cx, + UNNECESSARY_FOLD, + fold_span.with_hi(expr.span.hi()), + // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) + "this `.fold` can be written more succinctly using another method", + "try", + sugg, + applicability, + ); } } diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 0c72c13a3..36497d59a 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -13,15 +13,13 @@ use rustc_span::{sym, Symbol}; use super::UNNECESSARY_TO_OWNED; pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let Some(callee_def_id) = fn_def_id(cx, parent); - if is_into_iter(cx, callee_def_id); - then { - check_for_loop_iter(cx, parent, method_name, receiver, false) - } else { - false - } + if let Some(parent) = get_parent_expr(cx, expr) + && let Some(callee_def_id) = fn_def_id(cx, parent) + && is_into_iter(cx, callee_def_id) + { + check_for_loop_iter(cx, parent, method_name, receiver, false) + } else { + false } } @@ -36,65 +34,58 @@ pub fn check_for_loop_iter( receiver: &Expr<'_>, cloned_before_iter: bool, ) -> bool { - if_chain! { - if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)); - if let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent); - let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body); - if !clone_or_copy_needed; - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - let snippet = if_chain! { - if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind; - if maybe_iter_method_name.ident.name == sym::iter; - - if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - let receiver_ty = cx.typeck_results().expr_ty(receiver); - if implements_trait(cx, receiver_ty, iterator_trait_id, &[]); - if let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty); - - if let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator); - let collection_ty = cx.typeck_results().expr_ty(collection); - if implements_trait(cx, collection_ty, into_iterator_trait_id, &[]); - if let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item"); - - if iter_item_ty == into_iter_item_ty; - if let Some(collection_snippet) = snippet_opt(cx, collection.span); - then { - collection_snippet + if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)) + && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent) + && let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body) + && !clone_or_copy_needed + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind + && maybe_iter_method_name.ident.name == sym::iter + && let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let receiver_ty = cx.typeck_results().expr_ty(receiver) + && implements_trait(cx, receiver_ty, iterator_trait_id, &[]) + && let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty) + && let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && let collection_ty = cx.typeck_results().expr_ty(collection) + && implements_trait(cx, collection_ty, into_iterator_trait_id, &[]) + && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item") + && iter_item_ty == into_iter_item_ty + && let Some(collection_snippet) = snippet_opt(cx, collection.span) + { + collection_snippet + } else { + receiver_snippet + }; + span_lint_and_then( + cx, + UNNECESSARY_TO_OWNED, + expr.span, + &format!("unnecessary use of `{method_name}`"), + |diag| { + // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to + // a `to_owned`-like function was removed, then the next suggestion may be + // incorrect. This is because the iterator that results from the call's removal + // could hold a reference to a resource that is used mutably. See + // https://github.com/rust-lang/rust-clippy/issues/8148. + let applicability = if cloned_before_iter { + Applicability::MaybeIncorrect } else { - receiver_snippet - } - }; - span_lint_and_then( - cx, - UNNECESSARY_TO_OWNED, - expr.span, - &format!("unnecessary use of `{method_name}`"), - |diag| { - // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to - // a `to_owned`-like function was removed, then the next suggestion may be - // incorrect. This is because the iterator that results from the call's removal - // could hold a reference to a resource that is used mutably. See - // https://github.com/rust-lang/rust-clippy/issues/8148. - let applicability = if cloned_before_iter { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }; - diag.span_suggestion(expr.span, "use", snippet, applicability); - for addr_of_expr in addr_of_exprs { - match addr_of_expr.kind { - ExprKind::AddrOf(_, _, referent) => { - let span = addr_of_expr.span.with_hi(referent.span.lo()); - diag.span_suggestion(span, "remove this `&`", "", applicability); - } - _ => unreachable!(), - } + Applicability::MachineApplicable + }; + diag.span_suggestion(expr.span, "use", snippet, applicability); + for addr_of_expr in addr_of_exprs { + match addr_of_expr.kind { + ExprKind::AddrOf(_, _, referent) => { + let span = addr_of_expr.span.with_hi(referent.span.lo()); + diag.span_suggestion(span, "remove this `&`", "", applicability); + }, + _ => unreachable!(), } } - ); - return true; - } + }, + ); + return true; } false } diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index d0c62fb56..e2b389e96 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -18,25 +18,23 @@ pub(super) fn check<'tcx>( ) { let applicability = Applicability::MachineApplicable; let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg); - if_chain! { + if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind() // the turbofish for collect is ::> - if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind(); - if let Slice(slice) = ref_type.kind(); - if is_type_lang_item(cx, *slice, LangItem::String); + && let Slice(slice) = ref_type.kind() + && is_type_lang_item(cx, *slice, LangItem::String) // the argument for join is "" - if let ExprKind::Lit(spanned) = &join_arg.kind; - if let LitKind::Str(symbol, _) = spanned.node; - if symbol.is_empty(); - then { - span_lint_and_sugg( - cx, - UNNECESSARY_JOIN, - span.with_hi(expr.span.hi()), - r#"called `.collect::>().join("")` on an iterator"#, - "try using", - "collect::()".to_owned(), - applicability, - ); - } + && let ExprKind::Lit(spanned) = &join_arg.kind + && let LitKind::Str(symbol, _) = spanned.node + && symbol.is_empty() + { + span_lint_and_sugg( + cx, + UNNECESSARY_JOIN, + span.with_hi(expr.span.hi()), + r#"called `.collect::>().join("")` on an iterator"#, + "try using", + "collect::()".to_owned(), + applicability, + ); } } diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 79bff3937..4949ccd77 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; @@ -116,55 +115,72 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if cx.tcx.type_of(impl_id).instantiate_identity().is_slice(); - if let ExprKind::Closure(&Closure { body, .. }) = arg.kind; - if let closure_body = cx.tcx.hir().body(body); - if let &[ - Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, - Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } - ] = &closure_body.params; - if let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind; - if method_path.ident.name == sym::cmp; - if is_trait_method(cx, closure_body.value, sym::Ord); - then { - let (closure_body, closure_arg, reverse) = if mirrored_exprs( - left_expr, - left_ident, - right_expr, - right_ident - ) { - (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false) - } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) { - (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true) - } else { - return None; - }; - let vec_name = Sugg::hir(cx, recv, "..").to_string(); + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && cx.tcx.type_of(impl_id).instantiate_identity().is_slice() + && let ExprKind::Closure(&Closure { body, .. }) = arg.kind + && let closure_body = cx.tcx.hir().body(body) + && let &[ + Param { + pat: + Pat { + kind: PatKind::Binding(_, _, left_ident, _), + .. + }, + .. + }, + Param { + pat: + Pat { + kind: PatKind::Binding(_, _, right_ident, _), + .. + }, + .. + }, + ] = &closure_body.params + && let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind + && method_path.ident.name == sym::cmp + && is_trait_method(cx, closure_body.value, sym::Ord) + { + let (closure_body, closure_arg, reverse) = if mirrored_exprs(left_expr, left_ident, right_expr, right_ident) { + ( + Sugg::hir(cx, left_expr, "..").to_string(), + left_ident.name.to_string(), + false, + ) + } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) { + ( + Sugg::hir(cx, left_expr, "..").to_string(), + right_ident.name.to_string(), + true, + ) + } else { + return None; + }; + let vec_name = Sugg::hir(cx, recv, "..").to_string(); - if_chain! { - if let ExprKind::Path(QPath::Resolved(_, Path { - segments: [PathSegment { ident: left_name, .. }], .. - })) = &left_expr.kind; - if left_name == left_ident; - if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]) - }); - then { - return Some(LintTrigger::Sort(SortDetection { vec_name })); - } - } + if let ExprKind::Path(QPath::Resolved( + _, + Path { + segments: [PathSegment { ident: left_name, .. }], + .. + }, + )) = &left_expr.kind + && left_name == left_ident + && cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]) + }) + { + return Some(LintTrigger::Sort(SortDetection { vec_name })); + } - if !expr_borrows(cx, left_expr) { - return Some(LintTrigger::SortByKey(SortByKeyDetection { - vec_name, - closure_arg, - closure_body, - reverse, - })); - } + if !expr_borrows(cx, left_expr) { + return Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + closure_arg, + closure_body, + reverse, + })); } } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 772686d93..395b22ebc 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -32,25 +32,23 @@ pub fn check<'tcx>( args: &'tcx [Expr<'_>], msrv: &Msrv, ) { - if_chain! { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if args.is_empty(); - then { - if is_cloned_or_copied(cx, method_name, method_def_id) { - unnecessary_iter_cloned::check(cx, expr, method_name, receiver); - } else if is_to_owned_like(cx, expr, method_name, method_def_id) { - // At this point, we know the call is of a `to_owned`-like function. The functions - // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary - // based on its context, that is, whether it is a referent in an `AddrOf` expression, an - // argument in a `into_iter` call, or an argument in the call of some other function. - if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { - return; - } - if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { - return; - } - check_other_call_arg(cx, expr, method_name, receiver); + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && args.is_empty() + { + if is_cloned_or_copied(cx, method_name, method_def_id) { + unnecessary_iter_cloned::check(cx, expr, method_name, receiver); + } else if is_to_owned_like(cx, expr, method_name, method_def_id) { + // At this point, we know the call is of a `to_owned`-like function. The functions + // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary + // based on its context, that is, whether it is a referent in an `AddrOf` expression, an + // argument in a `into_iter` call, or an argument in the call of some other function. + if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { + return; } + if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { + return; + } + check_other_call_arg(cx, expr, method_name, receiver); } } } @@ -65,11 +63,10 @@ fn check_addr_of_expr( method_def_id: DefId, receiver: &Expr<'_>, ) -> bool { - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind; - let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::>(); - if let + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind + && let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::>() + && let // For matching uses of `Cow::from` [ Adjustment { @@ -110,10 +107,10 @@ fn check_addr_of_expr( kind: Adjust::Borrow(_), target: target_ty, }, - ] = adjustments[..]; - let receiver_ty = cx.typeck_results().expr_ty(receiver); - let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty); - let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty); + ] = adjustments[..] + && let receiver_ty = cx.typeck_results().expr_ty(receiver) + && let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty) + && let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty) // Only flag cases satisfying at least one of the following three conditions: // * the referent and receiver types are distinct // * the referent/receiver type is a copyable array @@ -123,77 +120,72 @@ fn check_addr_of_expr( // https://github.com/rust-lang/rust-clippy/issues/8759 // Arrays are a bit of a corner case. Non-copyable arrays are handled by // `redundant_clone`, but copyable arrays are not. - if *referent_ty != receiver_ty + && (*referent_ty != receiver_ty || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) - || is_cow_into_owned(cx, method_name, method_def_id); - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { + || is_cow_into_owned(cx, method_name, method_def_id)) + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{method_name}`"), + "use", + format!( + "{:&>width$}{receiver_snippet}", + "", + width = n_target_refs - n_receiver_refs + ), + Applicability::MachineApplicable, + ); + return true; + } + if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) + && implements_trait(cx, receiver_ty, deref_trait_id, &[]) + && cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty) + // Make sure that it's actually calling the right `.to_string()`, (#10033) + // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) + // but that's ok for Cow::into_owned specifically) + && (cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty + || is_cow_into_owned(cx, method_name, method_def_id)) + { + if n_receiver_refs > 0 { span_lint_and_sugg( cx, UNNECESSARY_TO_OWNED, parent.span, &format!("unnecessary use of `{method_name}`"), "use", - format!( - "{:&>width$}{receiver_snippet}", - "", - width = n_target_refs - n_receiver_refs - ), + receiver_snippet, + Applicability::MachineApplicable, + ); + } else { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + expr.span.with_lo(receiver.span.hi()), + &format!("unnecessary use of `{method_name}`"), + "remove this", + String::new(), Applicability::MachineApplicable, ); - return true; - } - if_chain! { - if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); - if implements_trait(cx, receiver_ty, deref_trait_id, &[]); - if cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty); - // Make sure that it's actually calling the right `.to_string()`, (#10033) - // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) - // but that's ok for Cow::into_owned specifically) - if cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty - || is_cow_into_owned(cx, method_name, method_def_id); - then { - if n_receiver_refs > 0 { - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - parent.span, - &format!("unnecessary use of `{method_name}`"), - "use", - receiver_snippet, - Applicability::MachineApplicable, - ); - } else { - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - expr.span.with_lo(receiver.span.hi()), - &format!("unnecessary use of `{method_name}`"), - "remove this", - String::new(), - Applicability::MachineApplicable, - ); - } - return true; - } - } - if_chain! { - if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); - if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]); - then { - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - parent.span, - &format!("unnecessary use of `{method_name}`"), - "use", - format!("{receiver_snippet}.as_ref()"), - Applicability::MachineApplicable, - ); - return true; - } } + return true; + } + if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) + && implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]) + { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{method_name}`"), + "use", + format!("{receiver_snippet}.as_ref()"), + Applicability::MachineApplicable, + ); + return true; } } false @@ -208,38 +200,36 @@ fn check_into_iter_call_arg( receiver: &Expr<'_>, msrv: &Msrv, ) -> bool { - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let Some(callee_def_id) = fn_def_id(cx, parent); - if is_into_iter(cx, callee_def_id); - if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - let parent_ty = cx.typeck_results().expr_ty(parent); - if implements_trait(cx, parent_ty, iterator_trait_id, &[]); - if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { - return true; - } - let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { - "copied" - } else { - "cloned" - }; - // The next suggestion may be incorrect because the removal of the `to_owned`-like - // function could cause the iterator to hold a reference to a resource that is used - // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - parent.span, - &format!("unnecessary use of `{method_name}`"), - "use", - format!("{receiver_snippet}.iter().{cloned_or_copied}()"), - Applicability::MaybeIncorrect, - ); + if let Some(parent) = get_parent_expr(cx, expr) + && let Some(callee_def_id) = fn_def_id(cx, parent) + && is_into_iter(cx, callee_def_id) + && let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let parent_ty = cx.typeck_results().expr_ty(parent) + && implements_trait(cx, parent_ty, iterator_trait_id, &[]) + && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } + let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { + "copied" + } else { + "cloned" + }; + // The next suggestion may be incorrect because the removal of the `to_owned`-like + // function could cause the iterator to hold a reference to a resource that is used + // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{method_name}`"), + "use", + format!("{receiver_snippet}.iter().{cloned_or_copied}()"), + Applicability::MaybeIncorrect, + ); + return true; } false } @@ -252,26 +242,25 @@ fn check_other_call_arg<'tcx>( method_name: Symbol, receiver: &'tcx Expr<'tcx>, ) -> bool { - if_chain! { - if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr); - if let Some((callee_def_id, _, recv, call_args)) = get_callee_generic_args_and_args(cx, maybe_call); - let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); - if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id); - if let Some(input) = fn_sig.inputs().get(i); - let (input, n_refs) = peel_mid_ty_refs(*input); - if let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input); - if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait(); - if let [trait_predicate] = trait_predicates + if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr) + && let Some((callee_def_id, _, recv, call_args)) = get_callee_generic_args_and_args(cx, maybe_call) + && let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder() + && let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id) + && let Some(input) = fn_sig.inputs().get(i) + && let (input, n_refs) = peel_mid_ty_refs(*input) + && let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input) + && let Some(sized_def_id) = cx.tcx.lang_items().sized_trait() + && let [trait_predicate] = trait_predicates .iter() .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id) - .collect::>()[..]; - if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); - if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); - if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id; - let receiver_ty = cx.typeck_results().expr_ty(receiver); + .collect::>()[..] + && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) + && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) + && (trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id) + && let receiver_ty = cx.typeck_results().expr_ty(receiver) // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match // `Target = T`. - if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { + && let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { Some((n_refs, receiver_ty)) } else if trait_predicate.def_id() != deref_trait_id { Some((1, Ty::new_ref(cx.tcx, @@ -283,21 +272,20 @@ fn check_other_call_arg<'tcx>( ))) } else { None - }; - if can_change_type(cx, maybe_arg, receiver_ty); - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - maybe_arg.span, - &format!("unnecessary use of `{method_name}`"), - "use", - format!("{:&>n_refs$}{receiver_snippet}", ""), - Applicability::MachineApplicable, - ); - return true; } + && can_change_type(cx, maybe_arg, receiver_ty) + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + maybe_arg.span, + &format!("unnecessary use of `{method_name}`"), + "use", + format!("{:&>n_refs$}{receiver_snippet}", ""), + Applicability::MachineApplicable, + ); + return true; } false } @@ -329,22 +317,18 @@ fn get_callee_generic_args_and_args<'tcx>( Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>], )> { - if_chain! { - if let ExprKind::Call(callee, args) = expr.kind; - let callee_ty = cx.typeck_results().expr_ty(callee); - if let ty::FnDef(callee_def_id, _) = callee_ty.kind(); - then { - let generic_args = cx.typeck_results().node_args(callee.hir_id); - return Some((*callee_def_id, generic_args, None, args)); - } + if let ExprKind::Call(callee, args) = expr.kind + && let callee_ty = cx.typeck_results().expr_ty(callee) + && let ty::FnDef(callee_def_id, _) = callee_ty.kind() + { + let generic_args = cx.typeck_results().node_args(callee.hir_id); + return Some((*callee_def_id, generic_args, None, args)); } - if_chain! { - if let ExprKind::MethodCall(_, recv, args, _) = expr.kind; - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - then { - let generic_args = cx.typeck_results().node_args(expr.hir_id); - return Some((method_def_id, generic_args, Some(recv), args)); - } + if let ExprKind::MethodCall(_, recv, args, _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + { + let generic_args = cx.typeck_results().node_args(expr.hir_id); + return Some((method_def_id, generic_args, Some(recv), args)); } None } diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index b5f810edd..84ee64e88 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; use clippy_utils::{get_parent_expr, is_trait_method}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -22,13 +21,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { // allow the `as_ref` or `as_mut` if it is followed by another method call - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let hir::ExprKind::MethodCall(segment, ..) = parent.kind; - if segment.ident.span != expr.span; - then { - return; - } + if let Some(parent) = get_parent_expr(cx, expr) + && let hir::ExprKind::MethodCall(segment, ..) = parent.kind + && segment.ident.span != expr.span + { + return; } let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 9f1f73e60..9ad4250a1 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -1,7 +1,6 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, path_to_local_id, usage}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -55,32 +54,33 @@ pub(super) fn get_hint_if_single_char_arg( arg: &hir::Expr<'_>, applicability: &mut Applicability, ) -> Option { - if_chain! { - if let hir::ExprKind::Lit(lit) = &arg.kind; - if let ast::LitKind::Str(r, style) = lit.node; - let string = r.as_str(); - if string.chars().count() == 1; - then { - let snip = snippet_with_applicability(cx, arg.span, string, applicability); - let ch = if let ast::StrStyle::Raw(nhash) = style { - let nhash = nhash as usize; - // for raw string: r##"a"## - &snip[(nhash + 2)..(snip.len() - 1 - nhash)] - } else { - // for regular string: "a" - &snip[1..(snip.len() - 1)] - }; + if let hir::ExprKind::Lit(lit) = &arg.kind + && let ast::LitKind::Str(r, style) = lit.node + && let string = r.as_str() + && string.chars().count() == 1 + { + let snip = snippet_with_applicability(cx, arg.span, string, applicability); + let ch = if let ast::StrStyle::Raw(nhash) = style { + let nhash = nhash as usize; + // for raw string: r##"a"## + &snip[(nhash + 2)..(snip.len() - 1 - nhash)] + } else { + // for regular string: "a" + &snip[1..(snip.len() - 1)] + }; - let hint = format!("'{}'", match ch { - "'" => "\\'" , + let hint = format!( + "'{}'", + match ch { + "'" => "\\'", r"\" => "\\\\", _ => ch, - }); + } + ); - Some(hint) - } else { - None - } + Some(hint) + } else { + None } } @@ -140,15 +140,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { return; }, ExprKind::MethodCall(.., args, _) => { - if_chain! { - if args.iter().all(|arg| !self.is_binding(arg)); - if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id); - let method_ty = self.cx.tcx.type_of(method_def_id).instantiate_identity(); - let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder(); - if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not)); - then { - return; - } + if args.iter().all(|arg| !self.is_binding(arg)) + && let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id) + && let method_ty = self.cx.tcx.type_of(method_def_id).instantiate_identity() + && let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder() + && matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not)) + { + return; } }, _ => {}, diff --git a/clippy_lints/src/methods/vec_resize_to_zero.rs b/clippy_lints/src/methods/vec_resize_to_zero.rs index 730727186..9e87fb45a 100644 --- a/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -17,29 +16,32 @@ pub(super) fn check<'tcx>( default_arg: &'tcx Expr<'_>, name_span: Span, ) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec); - if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind; - if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind; - then { - let method_call_span = expr.span.with_lo(name_span.lo()); - span_lint_and_then( - cx, - VEC_RESIZE_TO_ZERO, - expr.span, - "emptying a vector with `resize`", - |db| { - db.help("the arguments may be inverted..."); - db.span_suggestion( - method_call_span, - "...or you can empty the vector with", - "clear()".to_string(), - Applicability::MaybeIncorrect, - ); - }, - ); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec) + && let ExprKind::Lit(Spanned { + node: LitKind::Int(0, _), + .. + }) = count_arg.kind + && let ExprKind::Lit(Spanned { + node: LitKind::Int(..), .. + }) = default_arg.kind + { + let method_call_span = expr.span.with_lo(name_span.lo()); + span_lint_and_then( + cx, + VEC_RESIZE_TO_ZERO, + expr.span, + "emptying a vector with `resize`", + |db| { + db.help("the arguments may be inverted..."); + db.span_suggestion( + method_call_span, + "...or you can empty the vector with", + "clear()".to_string(), + Applicability::MaybeIncorrect, + ); + }, + ); } } diff --git a/clippy_lints/src/methods/zst_offset.rs b/clippy_lints/src/methods/zst_offset.rs index e9f268da6..0b829d99a 100644 --- a/clippy_lints/src/methods/zst_offset.rs +++ b/clippy_lints/src/methods/zst_offset.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; @@ -7,12 +6,10 @@ use rustc_middle::ty; use super::ZST_OFFSET; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if_chain! { - if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind(); - if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(*ty)); - if layout.is_zst(); - then { - span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); - } + if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind() + && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(*ty)) + && layout.is_zst() + { + span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); } } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 01eace6c4..a3d5a8a9d 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -5,7 +5,6 @@ use clippy_utils::{ any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, is_lint_allowed, iter_input_pats, last_path_segment, SpanlessEq, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::FnKind; @@ -143,73 +142,64 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if_chain! { - if !in_external_macro(cx.tcx.sess, stmt.span); - if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind; - if let Some(init) = local.init; + if !in_external_macro(cx.tcx.sess, stmt.span) + && let StmtKind::Local(local) = stmt.kind + && let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind + && let Some(init) = local.init // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. - if is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id); - then { - let ctxt = local.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); - let (mutopt, initref) = if mutabl == Mutability::Mut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) - }; - let tyopt = if let Some(ty) = local.ty { - let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; - format!(": &{mutopt}{ty_snip}") - } else { - String::new() - }; - span_lint_hir_and_then( - cx, - TOPLEVEL_REF_ARG, - init.hir_id, - local.pat.span, - "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", - |diag| { - diag.span_suggestion( - stmt.span, - "try", - format!( - "let {name}{tyopt} = {initref};", - name=snippet(cx, name.span, ".."), - ), - app, - ); - } - ); - } + && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) + { + let ctxt = local.span.ctxt(); + let mut app = Applicability::MachineApplicable; + let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); + let (mutopt, initref) = if mutabl == Mutability::Mut { + ("mut ", sugg_init.mut_addr()) + } else { + ("", sugg_init.addr()) + }; + let tyopt = if let Some(ty) = local.ty { + let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; + format!(": &{mutopt}{ty_snip}") + } else { + String::new() + }; + span_lint_hir_and_then( + cx, + TOPLEVEL_REF_ARG, + init.hir_id, + local.pat.span, + "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", + |diag| { + diag.span_suggestion( + stmt.span, + "try", + format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), + app, + ); + }, + ); }; - if_chain! { - if let StmtKind::Semi(expr) = stmt.kind; - if let ExprKind::Binary(ref binop, a, b) = expr.kind; - if binop.node == BinOpKind::And || binop.node == BinOpKind::Or; - if let Some(sugg) = Sugg::hir_opt(cx, a); - then { - span_lint_hir_and_then( - cx, - SHORT_CIRCUIT_STATEMENT, - expr.hir_id, - stmt.span, - "boolean short circuit operator in statement may be clearer using an explicit test", - |diag| { - let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg }; - diag.span_suggestion( - stmt.span, - "replace it with", - format!( - "if {sugg} {{ {}; }}", - &snippet(cx, b.span, ".."), - ), - Applicability::MachineApplicable, // snippet - ); - }); - } + if let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::Binary(ref binop, a, b) = expr.kind + && (binop.node == BinOpKind::And || binop.node == BinOpKind::Or) + && let Some(sugg) = Sugg::hir_opt(cx, a) + { + span_lint_hir_and_then( + cx, + SHORT_CIRCUIT_STATEMENT, + expr.hir_id, + stmt.span, + "boolean short circuit operator in statement may be clearer using an explicit test", + |diag| { + let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg }; + diag.span_suggestion( + stmt.span, + "replace it with", + format!("if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."),), + Applicability::MachineApplicable, // snippet + ); + }, + ); }; } diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 0d79ece08..c74d0d623 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -49,59 +49,59 @@ declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]); impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if_chain! { - if !item.span.from_expansion(); - if let ItemKind::Impl(imp) = &item.kind; - if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind; - if let Some(segment) = path.segments.iter().next(); - if let Some(generic_args) = segment.args; - if !generic_args.args.is_empty(); - then { - // get the name and span of the generic parameters in the Impl - let mut impl_params = Vec::new(); - for p in generic_args.args { - match p { - GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) => - impl_params.push((path.segments[0].ident.to_string(), path.span)), - GenericArg::Type(_) => return, - _ => (), - }; - } - - // find the type that the Impl is for - // only lint on struct/enum/union for now - let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) = path.res else { - return + if !item.span.from_expansion() + && let ItemKind::Impl(imp) = &item.kind + && let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind + && let Some(segment) = path.segments.iter().next() + && let Some(generic_args) = segment.args + && !generic_args.args.is_empty() + { + // get the name and span of the generic parameters in the Impl + let mut impl_params = Vec::new(); + for p in generic_args.args { + match p { + GenericArg::Type(Ty { + kind: TyKind::Path(QPath::Resolved(_, path)), + .. + }) => impl_params.push((path.segments[0].ident.to_string(), path.span)), + GenericArg::Type(_) => return, + _ => (), }; + } - // get the names of the generic parameters in the type - let type_params = &cx.tcx.generics_of(defid).params; - let type_param_names: Vec<_> = type_params.iter() - .filter_map(|p| - match p.kind { - GenericParamDefKind::Type {..} => Some(p.name.to_string()), - _ => None, - } - ).collect(); - // hashmap of name -> index for mismatch_param_name - let type_param_names_hashmap: FxHashMap<&String, usize> = - type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect(); + // find the type that the Impl is for + // only lint on struct/enum/union for now + let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) = path.res else { + return; + }; - let type_name = segment.ident; - for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() { - if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) { - let msg = format!("`{type_name}` has a similarly named generic type parameter `{impl_param_name}` in its declaration, but in a different order"); - let help = format!("try `{}`, or a name that does not conflict with `{type_name}`'s generic params", - type_param_names[i]); - span_lint_and_help( - cx, - MISMATCHING_TYPE_PARAM_ORDER, - *impl_param_span, - &msg, - None, - &help - ); - } + // get the names of the generic parameters in the type + let type_params = &cx.tcx.generics_of(defid).params; + let type_param_names: Vec<_> = type_params + .iter() + .filter_map(|p| match p.kind { + GenericParamDefKind::Type { .. } => Some(p.name.to_string()), + _ => None, + }) + .collect(); + // hashmap of name -> index for mismatch_param_name + let type_param_names_hashmap: FxHashMap<&String, usize> = type_param_names + .iter() + .enumerate() + .map(|(i, param)| (param, i)) + .collect(); + + let type_name = segment.ident; + for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() { + if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) { + let msg = format!( + "`{type_name}` has a similarly named generic type parameter `{impl_param_name}` in its declaration, but in a different order" + ); + let help = format!( + "try `{}`, or a name that does not conflict with `{type_name}`'s generic params", + type_param_names[i] + ); + span_lint_and_help(cx, MISMATCHING_TYPE_PARAM_ORDER, *impl_param_span, &msg, None, &help); } } } diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 0629dee4f..1dcaa60ac 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -8,7 +8,6 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; -use if_chain::if_chain; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; @@ -64,16 +63,14 @@ impl MissingDoc { } fn has_include(meta: Option) -> bool { - if_chain! { - if let Some(meta) = meta; - if let MetaItemKind::List(list) = meta.kind; - if let Some(meta) = list.first(); - if let Some(name) = meta.ident(); - then { - name.name == sym::include - } else { - false - } + if let Some(meta) = meta + && let MetaItemKind::List(list) = meta.kind + && let Some(meta) = list.first() + && let Some(name) = meta.ident() + { + name.name == sym::include + } else { + false } } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 16ff98a59..f7e428151 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -72,13 +72,12 @@ impl LateLintPass<'_> for ImportRename { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Use(path, UseKind::Single) = &item.kind { for &res in &path.res { - if_chain! { - if let Res::Def(_, id) = res; - if let Some(name) = self.renames.get(&id); + if let Res::Def(_, id) = res + && let Some(name) = self.renames.get(&id) // Remove semicolon since it is not present for nested imports - let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';'); - if let Some(snip) = snippet_opt(cx, span_without_semi); - if let Some(import) = match snip.split_once(" as ") { + && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';') + && let Some(snip) = snippet_opt(cx, span_without_semi) + && let Some(import) = match snip.split_once(" as ") { None => Some(snip.as_str()), Some((import, rename)) => { if rename.trim() == name.as_str() { @@ -87,20 +86,17 @@ impl LateLintPass<'_> for ImportRename { Some(import.trim()) } }, - }; - then { - span_lint_and_sugg( - cx, - MISSING_ENFORCED_IMPORT_RENAMES, - span_without_semi, - "this import should be renamed", - "try", - format!( - "{import} as {name}", - ), - Applicability::MachineApplicable, - ); } + { + span_lint_and_sugg( + cx, + MISSING_ENFORCED_IMPORT_RENAMES, + span_without_semi, + "this import should be renamed", + "try", + format!("{import} as {name}",), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 215161b04..b46c006cd 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; -use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -80,11 +79,13 @@ declare_lint_pass!(EvalOrderDependence => [MIXED_READ_WRITE_IN_EXPRESSION, DIVER impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. - let var = if_chain! { - if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind; - if let Some(var) = path_to_local(lhs); - if expr.span.desugaring_kind().is_none(); - then { var } else { return; } + let var = if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind + && let Some(var) = path_to_local(lhs) + && expr.span.desugaring_kind().is_none() + { + var + } else { + return; }; let mut visitor = ReadVisitor { cx, diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 97e8f1c03..1712262ff 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use if_chain::if_chain; use rustc_ast::ast::{BindingAnnotation, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -66,48 +65,46 @@ enum Mode { } fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) { - if_chain! { - if let [segment] = &path.segments[..]; - if segment.ident.name == kw::SelfUpper; - then { - // In case we have a named lifetime, we check if the name comes from expansion. - // If it does, at this point we know the rest of the parameter was written by the user, - // so let them decide what the name of the lifetime should be. - // See #6089 for more details. - let mut applicability = Applicability::MachineApplicable; - let self_param = match (binding_mode, mutbl) { - (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Mut) => { - if lifetime.ident.span.from_expansion() { - applicability = Applicability::HasPlaceholders; - "&'_ mut self".to_string() - } else { - format!("&{} mut self", &lifetime.ident.name) - } - }, - (Mode::Ref(None), Mutability::Not) => "&self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Not) => { - if lifetime.ident.span.from_expansion() { - applicability = Applicability::HasPlaceholders; - "&'_ self".to_string() - } else { - format!("&{} self", &lifetime.ident.name) - } - }, - (Mode::Value, Mutability::Mut) => "mut self".to_string(), - (Mode::Value, Mutability::Not) => "self".to_string(), - }; + if let [segment] = &path.segments[..] + && segment.ident.name == kw::SelfUpper + { + // In case we have a named lifetime, we check if the name comes from expansion. + // If it does, at this point we know the rest of the parameter was written by the user, + // so let them decide what the name of the lifetime should be. + // See #6089 for more details. + let mut applicability = Applicability::MachineApplicable; + let self_param = match (binding_mode, mutbl) { + (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => { + if lifetime.ident.span.from_expansion() { + applicability = Applicability::HasPlaceholders; + "&'_ mut self".to_string() + } else { + format!("&{} mut self", &lifetime.ident.name) + } + }, + (Mode::Ref(None), Mutability::Not) => "&self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Not) => { + if lifetime.ident.span.from_expansion() { + applicability = Applicability::HasPlaceholders; + "&'_ self".to_string() + } else { + format!("&{} self", &lifetime.ident.name) + } + }, + (Mode::Value, Mutability::Mut) => "mut self".to_string(), + (Mode::Value, Mutability::Not) => "self".to_string(), + }; - span_lint_and_sugg( - cx, - NEEDLESS_ARBITRARY_SELF_TYPE, - span, - "the type of the `self` parameter does not need to be arbitrary", - "consider to change this parameter to", - self_param, - applicability, - ) - } + span_lint_and_sugg( + cx, + NEEDLESS_ARBITRARY_SELF_TYPE, + span, + "the type of the `self` parameter does not need to be arbitrary", + "consider to change this parameter to", + self_param, + applicability, + ); } } @@ -125,12 +122,10 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { } }, TyKind::Ref(lifetime, mut_ty) => { - if_chain! { - if let TyKind::Path(None, path) = &mut_ty.ty.kind; - if let PatKind::Ident(BindingAnnotation::NONE, _, _) = p.pat.kind; - then { - check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl); - } + if let TyKind::Path(None, path) = &mut_ty.ty.kind + && let PatKind::Ident(BindingAnnotation::NONE, _, _) = p.pat.kind + { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl); } }, _ => {}, diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index cb2738947..6803034f4 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -362,21 +362,19 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin } fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if_chain! { - if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind; - if let Some(last_stmt) = loop_block.stmts.last(); - if let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind; - if let ast::ExprKind::Continue(_) = inner_expr.kind; - then { - span_lint_and_help( - cx, - NEEDLESS_CONTINUE, - last_stmt.span, - MSG_REDUNDANT_CONTINUE_EXPRESSION, - None, - DROP_CONTINUE_EXPRESSION_MSG, - ); - } + if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind + && let Some(last_stmt) = loop_block.stmts.last() + && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind + && let ast::ExprKind::Continue(_) = inner_expr.kind + { + span_lint_and_help( + cx, + NEEDLESS_CONTINUE, + last_stmt.span, + MSG_REDUNDANT_CONTINUE_EXPRESSION, + None, + DROP_CONTINUE_EXPRESSION_MSG, + ); } with_loop_block(expr, |loop_block, label| { for (i, stmt) in loop_block.stmts.iter().enumerate() { diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 086c8e6ff..0203e2ccd 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -6,8 +6,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::{sym, Symbol}; -use if_chain::if_chain; - use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; @@ -52,65 +50,63 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { return; }; - if_chain! { + if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind // Check the method name is `for_each`. - if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind; - if method_name.ident.name == Symbol::intern("for_each"); + && method_name.ident.name == Symbol::intern("for_each") // Check `for_each` is an associated function of `Iterator`. - if is_trait_method(cx, expr, sym::Iterator); + && is_trait_method(cx, expr, sym::Iterator) // Checks the receiver of `for_each` is also a method call. - if let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind; + && let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or // `v.foo().iter().for_each()` must be skipped. - if matches!( + && matches!( iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) - ); + ) // Checks the type of the `iter` method receiver is NOT a user defined type. - if has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some(); + && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. - if let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind; - let body = cx.tcx.hir().body(body); - if let ExprKind::Block(..) = body.value.kind; - then { - let mut ret_collector = RetCollector::default(); - ret_collector.visit_expr(body.value); + && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind + && let body = cx.tcx.hir().body(body) + && let ExprKind::Block(..) = body.value.kind + { + let mut ret_collector = RetCollector::default(); + ret_collector.visit_expr(body.value); - // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. - if ret_collector.ret_in_loop { - return; - } - - let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { - (Applicability::MachineApplicable, None) - } else { - ( - Applicability::MaybeIncorrect, - Some( - ret_collector - .spans - .into_iter() - .map(|span| (span, "continue".to_string())) - .collect(), - ), - ) - }; - - let sugg = format!( - "for {} in {} {}", - snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), - snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability), - snippet_with_applicability(cx, body.value.span, "..", &mut applicability), - ); - - span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| { - diag.span_suggestion(stmt.span, "try", sugg, applicability); - if let Some(ret_suggs) = ret_suggs { - diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability); - } - }) + // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. + if ret_collector.ret_in_loop { + return; } + + let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { + (Applicability::MachineApplicable, None) + } else { + ( + Applicability::MaybeIncorrect, + Some( + ret_collector + .spans + .into_iter() + .map(|span| (span, "continue".to_string())) + .collect(), + ), + ) + }; + + let sugg = format!( + "for {} in {} {}", + snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), + snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability), + snippet_with_applicability(cx, body.value.span, "..", &mut applicability), + ); + + span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| { + diag.span_suggestion(stmt.span, "try", sugg, applicability); + if let Some(ret_suggs) = ret_suggs { + diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability); + } + }); } } } diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index c8888c744..0a95678d3 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -128,21 +128,18 @@ impl LocalAssign { let assign = match expr.kind { ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span), ExprKind::Block(block, _) => { - if_chain! { - if let Some((last, other_stmts)) = block.stmts.split_last(); - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind; + if let Some((last, other_stmts)) = block.stmts.split_last() + && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind - let assign = Self::from_expr(expr, last.span)?; + && let assign = Self::from_expr(expr, last.span)? // avoid visiting if not needed - if assign.lhs_id == binding_id; - if other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt)); - - then { - Some(assign) - } else { - None - } + && assign.lhs_id == binding_id + && other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt)) + { + Some(assign) + } else { + None } }, ExprKind::Assign(..) => Self::from_expr(expr, expr.span), @@ -368,22 +365,20 @@ fn check<'tcx>( impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); - if_chain! { - if let Local { - init: None, - pat: &Pat { + if let Local { + init: None, + pat: + &Pat { kind: PatKind::Binding(BindingAnnotation::NONE, binding_id, _, None), .. }, - source: LocalSource::Normal, - .. - } = local; - if let Some((_, Node::Stmt(local_stmt))) = parents.next(); - if let Some((_, Node::Block(block))) = parents.next(); - - then { - check(cx, local, local_stmt, block, binding_id); - } + source: LocalSource::Normal, + .. + } = local + && let Some((_, Node::Stmt(local_stmt))) = parents.next() + && let Some((_, Node::Block(block))) = parents.next() + { + check(cx, local, local_stmt, block, binding_id); } } } diff --git a/clippy_lints/src/needless_parens_on_range_literals.rs b/clippy_lints/src/needless_parens_on_range_literals.rs index 7bbf1fb4c..490c3f9c1 100644 --- a/clippy_lints/src/needless_parens_on_range_literals.rs +++ b/clippy_lints/src/needless_parens_on_range_literals.rs @@ -53,21 +53,23 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { // don't check floating point literals on the start expression of a range return; } - if_chain! { - if let ExprKind::Lit(literal) = e.kind; + if let ExprKind::Lit(literal) = e.kind // the indicator that parenthesis surround the literal is that the span of the expression and the literal differ - if (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo); + && (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo) // inspect the source code of the expression for parenthesis - if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, "")); - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERALS, e.span, - "needless parenthesis on range literals can be removed", - |diag| { - let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability); - diag.span_suggestion(e.span, "try", suggestion, applicability); - }); - } + && snippet_enclosed_in_parenthesis(&snippet(cx, e.span, "")) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_then( + cx, + NEEDLESS_PARENS_ON_RANGE_LITERALS, + e.span, + "needless parenthesis on range literals can be removed", + |diag| { + let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability); + diag.span_suggestion(e.span, "try", suggestion, applicability); + }, + ); } } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 8fa461ac1..7c48b84e4 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -5,7 +5,6 @@ use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; -use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::FnKind; @@ -177,118 +176,119 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { ) }; - if_chain! { - if !is_self(arg); - if !ty.is_mutable_ptr(); - if !is_copy(cx, ty); - if ty.is_sized(cx.tcx, cx.param_env); - if !allowed_traits.iter().any(|&t| implements_trait_with_env_from_iter( - cx.tcx, - cx.param_env, - ty, - t, - [Option::>::None], - )); - if !implements_borrow_trait; - if !all_borrowable_trait; - - if let PatKind::Binding(BindingAnnotation(_, Mutability::Not), canonical_id, ..) = arg.pat.kind; - if !moved_vars.contains(&canonical_id); - then { - // Dereference suggestion - let sugg = |diag: &mut Diagnostic| { - if let ty::Adt(def, ..) = ty.kind() { - if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { - if type_allowed_to_implement_copy( - cx.tcx, - cx.param_env, - ty, - traits::ObligationCause::dummy_with_span(span), - ).is_ok() { - diag.span_help(span, "consider marking this type as `Copy`"); - } + if !is_self(arg) + && !ty.is_mutable_ptr() + && !is_copy(cx, ty) + && ty.is_sized(cx.tcx, cx.param_env) + && !allowed_traits.iter().any(|&t| { + implements_trait_with_env_from_iter( + cx.tcx, + cx.param_env, + ty, + t, + [Option::>::None], + ) + }) + && !implements_borrow_trait + && !all_borrowable_trait + && let PatKind::Binding(BindingAnnotation(_, Mutability::Not), canonical_id, ..) = arg.pat.kind + && !moved_vars.contains(&canonical_id) + { + // Dereference suggestion + let sugg = |diag: &mut Diagnostic| { + if let ty::Adt(def, ..) = ty.kind() { + if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { + if type_allowed_to_implement_copy( + cx.tcx, + cx.param_env, + ty, + traits::ObligationCause::dummy_with_span(span), + ) + .is_ok() + { + diag.span_help(span, "consider marking this type as `Copy`"); } } + } - if_chain! { - if is_type_diagnostic_item(cx, ty, sym::Vec); - if let Some(clone_spans) = - get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]); - if let TyKind::Path(QPath::Resolved(_, path)) = input.kind; - if let Some(elem_ty) = path.segments.iter() - .find(|seg| seg.ident.name == sym::Vec) - .and_then(|ps| ps.args.as_ref()) - .map(|params| params.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }).unwrap()); - then { - let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_")); + if is_type_diagnostic_item(cx, ty, sym::Vec) + && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]) + && let TyKind::Path(QPath::Resolved(_, path)) = input.kind + && let Some(elem_ty) = path + .segments + .iter() + .find(|seg| seg.ident.name == sym::Vec) + .and_then(|ps| ps.args.as_ref()) + .map(|params| { + params + .args + .iter() + .find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) + .unwrap() + }) + { + let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_")); + diag.span_suggestion( + input.span, + "consider changing the type to", + slice_ty, + Applicability::Unspecified, + ); + + for (span, suggestion) in clone_spans { + diag.span_suggestion( + span, + snippet_opt(cx, span) + .map_or("change the call to".into(), |x| format!("change `{x}` to")), + suggestion, + Applicability::Unspecified, + ); + } + + // cannot be destructured, no need for `*` suggestion + return; + } + + if is_type_lang_item(cx, ty, LangItem::String) { + if let Some(clone_spans) = + get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) + { + diag.span_suggestion( + input.span, + "consider changing the type to", + "&str", + Applicability::Unspecified, + ); + + for (span, suggestion) in clone_spans { diag.span_suggestion( - input.span, - "consider changing the type to", - slice_ty, + span, + snippet_opt(cx, span) + .map_or("change the call to".into(), |x| format!("change `{x}` to")), + suggestion, Applicability::Unspecified, ); - - for (span, suggestion) in clone_spans { - diag.span_suggestion( - span, - snippet_opt(cx, span) - .map_or( - "change the call to".into(), - |x| format!("change `{x}` to"), - ), - suggestion, - Applicability::Unspecified, - ); - } - - // cannot be destructured, no need for `*` suggestion - return; } + + return; } + } - if is_type_lang_item(cx, ty, LangItem::String) { - if let Some(clone_spans) = - get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) { - diag.span_suggestion( - input.span, - "consider changing the type to", - "&str", - Applicability::Unspecified, - ); + let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))]; - for (span, suggestion) in clone_spans { - diag.span_suggestion( - span, - snippet_opt(cx, span) - .map_or( - "change the call to".into(), - |x| format!("change `{x}` to") - ), - suggestion, - Applicability::Unspecified, - ); - } + multispan_sugg(diag, "consider taking a reference instead", spans); + }; - return; - } - } - - let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))]; - - multispan_sugg(diag, "consider taking a reference instead", spans); - }; - - span_lint_and_then( - cx, - NEEDLESS_PASS_BY_VALUE, - input.span, - "this argument is passed by value, but not consumed in the function body", - sugg, - ); - } + span_lint_and_then( + cx, + NEEDLESS_PASS_BY_VALUE, + input.span, + "this argument is passed by value, but not consumed in the function body", + sugg, + ); } } } diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 074c9fef1..7ec0879ba 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::path_res; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, CoroutineKind, CoroutineSource, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -111,34 +110,32 @@ impl LateLintPass<'_> for NeedlessQuestionMark { } fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Call(path, [arg]) = expr.kind; - if let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path); - if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); - let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { + if let ExprKind::Call(path, [arg]) = expr.kind + && let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + && let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { "Some()" } else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) { "Ok()" } else { return; - }; - if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind; - if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind; - if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind; - if expr.span.eq_ctxt(inner_expr.span); - let expr_ty = cx.typeck_results().expr_ty(expr); - let inner_ty = cx.typeck_results().expr_ty(inner_expr); - if expr_ty == inner_ty; - then { - span_lint_and_sugg( - cx, - NEEDLESS_QUESTION_MARK, - expr.span, - "question mark operator is useless here", - &format!("try removing question mark and `{sugg_remove}`"), - format!("{}", snippet(cx, inner_expr.span, r#""...""#)), - Applicability::MachineApplicable, - ); } + && let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind + && let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind + && expr.span.eq_ctxt(inner_expr.span) + && let expr_ty = cx.typeck_results().expr_ty(expr) + && let inner_ty = cx.typeck_results().expr_ty(inner_expr) + && expr_ty == inner_ty + { + span_lint_and_sugg( + cx, + NEEDLESS_QUESTION_MARK, + expr.span, + "question mark operator is useless here", + &format!("try removing question mark and `{sugg_remove}`"), + format!("{}", snippet(cx, inner_expr.span, r#""...""#)), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 56c67406d..30aed8cc0 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -46,42 +45,39 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Unary(UnOp::Not, inner) = expr.kind; - if let ExprKind::Binary(ref op, left, _) = inner.kind; - if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Unary(UnOp::Not, inner) = expr.kind + && let ExprKind::Binary(ref op, left, _) = inner.kind + && let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node + { + let ty = cx.typeck_results().expr_ty(left); - then { - let ty = cx.typeck_results().expr_ty(left); - - let implements_ord = { - if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) { - implements_trait(cx, ty, id, &[]) - } else { - return; - } - }; - - let implements_partial_ord = { - if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { - implements_trait(cx, ty, id, &[ty.into()]) - } else { - return; - } - }; - - if implements_partial_ord && !implements_ord { - span_lint( - cx, - NEG_CMP_OP_ON_PARTIAL_ORD, - expr.span, - "the use of negated comparison operators on partially ordered \ - types produces code that is hard to read and refactor, please \ - consider using the `partial_cmp` method instead, to make it \ - clear that the two values could be incomparable", - ); + let implements_ord = { + if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) { + implements_trait(cx, ty, id, &[]) + } else { + return; } + }; + + let implements_partial_ord = { + if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { + implements_trait(cx, ty, id, &[ty.into()]) + } else { + return; + } + }; + + if implements_partial_ord && !implements_ord { + span_lint( + cx, + NEG_CMP_OP_ON_PARTIAL_ORD, + expr.span, + "the use of negated comparison operators on partially ordered \ + types produces code that is hard to read and refactor, please \ + consider using the `partial_cmp` method instead, to make it \ + clear that the two values could be incomparable", + ); } } } diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index db0e22842..a6786e913 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -2,7 +2,6 @@ use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::has_enclosing_paren; -use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; @@ -53,28 +52,25 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply { } fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { - if_chain! { - if let ExprKind::Lit(l) = lit.kind; - if consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1); - if cx.typeck_results().expr_ty(exp).is_integral(); - - then { - let mut applicability = Applicability::MachineApplicable; - let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); - let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { - format!("-({snip})") - } else { - format!("-{snip}") - }; - span_lint_and_sugg( - cx, - NEG_MULTIPLY, - span, - "this multiplication by -1 can be written more succinctly", - "consider using", - suggestion, - applicability, - ); - } + if let ExprKind::Lit(l) = lit.kind + && consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1) + && cx.typeck_results().expr_ty(exp).is_integral() + { + let mut applicability = Applicability::MachineApplicable; + let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); + let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { + format!("-({snip})") + } else { + format!("-{snip}") + }; + span_lint_and_sugg( + cx, + NEG_MULTIPLY, + span, + "this multiplication by -1 can be written more succinctly", + "consider using", + suggestion, + applicability, + ); } } diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index f7f9dccfb..abba622a2 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::return_ty; use clippy_utils::source::snippet; use clippy_utils::sugg::DiagnosticExt; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::HirIdSet; @@ -93,73 +92,69 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // an impl of `Default` return; } - if_chain! { - if sig.decl.inputs.is_empty(); - if name == sym::new; - if cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id); - let self_def_id = cx.tcx.hir().get_parent_item(id.into()); - let self_ty = cx.tcx.type_of(self_def_id).instantiate_identity(); - if self_ty == return_ty(cx, id); - if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default); - then { - if self.impling_types.is_none() { - let mut impls = HirIdSet::default(); - cx.tcx.for_each_impl(default_trait_id, |d| { - let ty = cx.tcx.type_of(d).instantiate_identity(); - if let Some(ty_def) = ty.ty_adt_def() { - if let Some(local_def_id) = ty_def.did().as_local() { - impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); - } + if sig.decl.inputs.is_empty() + && name == sym::new + && cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id) + && let self_def_id = cx.tcx.hir().get_parent_item(id.into()) + && let self_ty = cx.tcx.type_of(self_def_id).instantiate_identity() + && self_ty == return_ty(cx, id) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + { + if self.impling_types.is_none() { + let mut impls = HirIdSet::default(); + cx.tcx.for_each_impl(default_trait_id, |d| { + let ty = cx.tcx.type_of(d).instantiate_identity(); + if let Some(ty_def) = ty.ty_adt_def() { + if let Some(local_def_id) = ty_def.did().as_local() { + impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); } - }); - self.impling_types = Some(impls); - } - - // Check if a Default implementation exists for the Self type, regardless of - // generics - if_chain! { - if let Some(ref impling_types) = self.impling_types; - let self_def = cx.tcx.type_of(self_def_id).instantiate_identity(); - if let Some(self_def) = self_def.ty_adt_def(); - if let Some(self_local_did) = self_def.did().as_local(); - let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); - if impling_types.contains(&self_id); - then { - return; } - } - - let generics_sugg = snippet(cx, generics.span, ""); - let where_clause_sugg = if generics.has_where_clause_predicates { - format!("\n{}\n", snippet(cx, generics.where_clause_span, "")) - } else { - String::new() - }; - let self_ty_fmt = self_ty.to_string(); - let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt); - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id.into(), - impl_item.span, - &format!( - "you should consider adding a `Default` implementation for `{self_type_snip}`" - ), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try adding this", - &create_new_without_default_suggest_msg( - &self_type_snip, - &generics_sugg, - &where_clause_sugg - ), - Applicability::MachineApplicable, - ); - }, - ); + }); + self.impling_types = Some(impls); } + + // Check if a Default implementation exists for the Self type, regardless of + // generics + if let Some(ref impling_types) = self.impling_types + && let self_def = cx.tcx.type_of(self_def_id).instantiate_identity() + && let Some(self_def) = self_def.ty_adt_def() + && let Some(self_local_did) = self_def.did().as_local() + && let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did) + && impling_types.contains(&self_id) + { + return; + } + + let generics_sugg = snippet(cx, generics.span, ""); + let where_clause_sugg = if generics.has_where_clause_predicates { + format!("\n{}\n", snippet(cx, generics.where_clause_span, "")) + } else { + String::new() + }; + let self_ty_fmt = self_ty.to_string(); + let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt); + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id.into(), + impl_item.span, + &format!( + "you should consider adding a `Default` implementation for `{self_type_snip}`" + ), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try adding this", + &create_new_without_default_suggest_msg( + &self_type_snip, + &generics_sugg, + &where_clause_sugg, + ), + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 3a28e511f..de8bb123e 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -132,24 +132,22 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { return true; } } else if let StmtKind::Local(local) = stmt.kind { - if_chain! { - if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id); - if let Some(init) = local.init; - if local.els.is_none(); - if !local.pat.span.from_expansion(); - if has_no_effect(cx, init); - if let PatKind::Binding(_, _, ident, _) = local.pat.kind; - if ident.name.to_ident_string().starts_with('_'); - then { - span_lint_hir( - cx, - NO_EFFECT_UNDERSCORE_BINDING, - init.hir_id, - stmt.span, - "binding to `_` prefixed variable with no side-effect" - ); - return true; - } + if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) + && let Some(init) = local.init + && local.els.is_none() + && !local.pat.span.from_expansion() + && has_no_effect(cx, init) + && let PatKind::Binding(_, _, ident, _) = local.pat.kind + && ident.name.to_ident_string().starts_with('_') + { + span_lint_hir( + cx, + NO_EFFECT_UNDERSCORE_BINDING, + init.hir_id, + stmt.span, + "binding to `_` prefixed variable with no side-effect", + ); + return true; } } false @@ -199,63 +197,60 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if_chain! { - if let StmtKind::Semi(expr) = stmt.kind; - let ctxt = stmt.span.ctxt(); - if expr.span.ctxt() == ctxt; - if let Some(reduced) = reduce_expression(cx, expr); - if !in_external_macro(cx.sess(), stmt.span); - if reduced.iter().all(|e| e.span.ctxt() == ctxt); - then { - if let ExprKind::Index(..) = &expr.kind { - let snippet = if let (Some(arr), Some(func)) = - (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) - { + if let StmtKind::Semi(expr) = stmt.kind + && let ctxt = stmt.span.ctxt() + && expr.span.ctxt() == ctxt + && let Some(reduced) = reduce_expression(cx, expr) + && !in_external_macro(cx.sess(), stmt.span) + && reduced.iter().all(|e| e.span.ctxt() == ctxt) + { + if let ExprKind::Index(..) = &expr.kind { + let snippet = + if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) { format!("assert!({}.len() > {});", &arr, &func) } else { return; }; - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "unnecessary operation", - |diag| { - diag.span_suggestion( - stmt.span, - "statement can be written as", - snippet, - Applicability::MaybeIncorrect, - ); - }, - ); - } else { - let mut snippet = String::new(); - for e in reduced { - if let Some(snip) = snippet_opt(cx, e.span) { - snippet.push_str(&snip); - snippet.push(';'); - } else { - return; - } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be written as", + snippet, + Applicability::MaybeIncorrect, + ); + }, + ); + } else { + let mut snippet = String::new(); + for e in reduced { + if let Some(snip) = snippet_opt(cx, e.span) { + snippet.push_str(&snip); + snippet.push(';'); + } else { + return; } - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "unnecessary operation", - |diag| { - diag.span_suggestion( - stmt.span, - "statement can be reduced to", - snippet, - Applicability::MachineApplicable, - ); - }, - ); } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be reduced to", + snippet, + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 54cec066b..3059eb25d 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -7,7 +7,6 @@ use std::ptr; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::macro_backtrace; use clippy_utils::{def_path_def_ids, in_constant}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -386,15 +385,14 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { of_trait: Some(of_trait_ref), .. }) => { - if_chain! { + if let Some(of_trait_def_id) = of_trait_ref.trait_def_id() // Lint a trait impl item only when the definition is a generic type, // assuming an assoc const is not meant to be an interior mutable type. - if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); - if let Some(of_assoc_item) = cx + && let Some(of_assoc_item) = cx .tcx .associated_item(impl_item.owner_id) - .trait_item_def_id; - if cx + .trait_item_def_id + && cx .tcx .layout_of(cx.tcx.param_env(of_trait_def_id).and( // Normalize assoc types because ones originated from generic params @@ -405,23 +403,17 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { cx.tcx.type_of(of_assoc_item).instantiate_identity(), ), )) - .is_err(); + .is_err() // If there were a function like `has_frozen_variant` described above, // we should use here as a frozen variant is a potential to be frozen // similar to unknown layouts. // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` - let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); - let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - if !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized); - if self.is_value_unfrozen_poly(cx, *body_id, normalized); - then { - lint( - cx, - Source::Assoc { - item: impl_item.span, - }, - ); - } + && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity() + && let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty) + && !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized) + && self.is_value_unfrozen_poly(cx, *body_id, normalized) + { + lint(cx, Source::Assoc { item: impl_item.span }); } }, ItemKind::Impl(Impl { of_trait: None, .. }) => { diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index e94e45899..6cfcc8102 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -44,38 +43,36 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { - if_chain! { - if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def(); - if (path.ident.name == sym!(mode) - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::FsOpenOptions | sym::DirBuilder))) + if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() + && ((path.ident.name == sym!(mode) + && matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::FsOpenOptions | sym::DirBuilder) + )) || (path.ident.name == sym!(set_mode) - && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())); - if let ExprKind::Lit(_) = param.kind; - if param.span.eq_ctxt(expr.span); + && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) + && let ExprKind::Lit(_) = param.kind + && param.span.eq_ctxt(expr.span) + { + let Some(snip) = snippet_opt(cx, param.span) else { + return; + }; - then { - let Some(snip) = snippet_opt(cx, param.span) else { - return - }; - - if !snip.starts_with("0o") { - show_error(cx, param); - } + if !snip.starts_with("0o") { + show_error(cx, param); } } }, ExprKind::Call(func, [param]) => { - if_chain! { - if let ExprKind::Path(ref path) = func.kind; - if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); - if let ExprKind::Lit(_) = param.kind; - if param.span.eq_ctxt(expr.span); - if let Some(snip) = snippet_opt(cx, param.span); - if !snip.starts_with("0o"); - then { - show_error(cx, param); - } + if let ExprKind::Path(ref path) = func.kind + && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE) + && let ExprKind::Lit(_) = param.kind + && param.span.eq_ctxt(expr.span) + && let Some(snip) = snippet_opt(cx, param.span) + && !snip.starts_with("0o") + { + show_error(cx, param); } }, _ => {}, diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index 62ef48c8a..df1476e68 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -81,73 +81,74 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { // We start from `Send` impl instead of `check_field_def()` because // single `AdtDef` may have multiple `Send` impls due to generic // parameters, and the lint is much easier to implement in this way. - if_chain! { - if !in_external_macro(cx.tcx.sess, item.span); - if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send); - if let ItemKind::Impl(hir_impl) = &item.kind; - if let Some(trait_ref) = &hir_impl.of_trait; - if let Some(trait_id) = trait_ref.trait_def_id(); - if send_trait == trait_id; - if hir_impl.polarity == ImplPolarity::Positive; - if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); - if let self_ty = ty_trait_ref.instantiate_identity().self_ty(); - if let ty::Adt(adt_def, impl_trait_args) = self_ty.kind(); - then { - let mut non_send_fields = Vec::new(); + if !in_external_macro(cx.tcx.sess, item.span) + && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) + && let ItemKind::Impl(hir_impl) = &item.kind + && let Some(trait_ref) = &hir_impl.of_trait + && let Some(trait_id) = trait_ref.trait_def_id() + && send_trait == trait_id + && hir_impl.polarity == ImplPolarity::Positive + && let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) + && let self_ty = ty_trait_ref.instantiate_identity().self_ty() + && let ty::Adt(adt_def, impl_trait_args) = self_ty.kind() + { + let mut non_send_fields = Vec::new(); - let hir_map = cx.tcx.hir(); - for variant in adt_def.variants() { - for field in &variant.fields { - if_chain! { - if let Some(field_hir_id) = field - .did - .as_local() - .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id)); - if !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id); - if let field_ty = field.ty(cx.tcx, impl_trait_args); - if !ty_allowed_in_send(cx, field_ty, send_trait); - if let Node::Field(field_def) = hir_map.get(field_hir_id); - then { - non_send_fields.push(NonSendField { - def: field_def, - ty: field_ty, - generic_params: collect_generic_params(field_ty), - }) - } - } + let hir_map = cx.tcx.hir(); + for variant in adt_def.variants() { + for field in &variant.fields { + if let Some(field_hir_id) = field + .did + .as_local() + .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id)) + && !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id) + && let field_ty = field.ty(cx.tcx, impl_trait_args) + && !ty_allowed_in_send(cx, field_ty, send_trait) + && let Node::Field(field_def) = hir_map.get(field_hir_id) + { + non_send_fields.push(NonSendField { + def: field_def, + ty: field_ty, + generic_params: collect_generic_params(field_ty), + }); } } + } - if !non_send_fields.is_empty() { - span_lint_and_then( - cx, - NON_SEND_FIELDS_IN_SEND_TY, - item.span, - &format!( - "some fields in `{}` are not safe to be sent to another thread", - snippet(cx, hir_impl.self_ty.span, "Unknown") - ), - |diag| { - for field in non_send_fields { - diag.span_note( - field.def.span, - format!("it is not safe to send field `{}` to another thread", field.def.ident.name), - ); + if !non_send_fields.is_empty() { + span_lint_and_then( + cx, + NON_SEND_FIELDS_IN_SEND_TY, + item.span, + &format!( + "some fields in `{}` are not safe to be sent to another thread", + snippet(cx, hir_impl.self_ty.span, "Unknown") + ), + |diag| { + for field in non_send_fields { + diag.span_note( + field.def.span, + format!( + "it is not safe to send field `{}` to another thread", + field.def.ident.name + ), + ); - match field.generic_params.len() { - 0 => diag.help("use a thread-safe type that implements `Send`"), - 1 if is_ty_param(field.ty) => diag.help(format!("add `{}: Send` bound in `Send` impl", field.ty)), - _ => diag.help(format!( - "add bounds on type parameter{} `{}` that satisfy `{}: Send`", - if field.generic_params.len() > 1 { "s" } else { "" }, - field.generic_params_string(), - snippet(cx, field.def.ty.span, "Unknown"), - )), - }; - } - }, - ); - } + match field.generic_params.len() { + 0 => diag.help("use a thread-safe type that implements `Send`"), + 1 if is_ty_param(field.ty) => { + diag.help(format!("add `{}: Send` bound in `Send` impl", field.ty)) + }, + _ => diag.help(format!( + "add bounds on type parameter{} `{}` that satisfy `{}: Send`", + if field.generic_params.len() > 1 { "s" } else { "" }, + field.generic_params_string(), + snippet(cx, field.def.ty.span, "Unknown"), + )), + }; + } + }, + ); } } } diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 11c3a5417..8604fe08e 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,7 +1,6 @@ use clippy_config::types::MacroMatcher; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -93,24 +92,22 @@ fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a Mac .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local)) }; let span_call_site = span.ctxt().outer_expn_data().call_site; - if_chain! { - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind; - let name = mac_name.as_str(); - if let Some(braces) = mac_braces.macro_braces.get(name); - if let Some(snip) = snippet_opt(cx, span_call_site); + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind + && let name = mac_name.as_str() + && let Some(braces) = mac_braces.macro_braces.get(name) + && let Some(snip) = snippet_opt(cx, span_call_site) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - if snip.starts_with(&format!("{name}!")); - if unnested_or_local(); + && snip.starts_with(&format!("{name}!")) + && unnested_or_local() // make formatting consistent - let c = snip.replace(' ', ""); - if !c.starts_with(&format!("{name}!{}", braces.0)); - if !mac_braces.done.contains(&span_call_site); - then { - Some((span_call_site, braces, snip)) - } else { - None - } + && let c = snip.replace(' ', "") + && !c.starts_with(&format!("{name}!{}", braces.0)) + && !mac_braces.done.contains(&span_call_site) + { + Some((span_call_site, braces, snip)) + } else { + None } } diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index c4572a09d..2f85130fb 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -4,7 +4,6 @@ use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr; use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -25,43 +24,40 @@ pub(super) fn check<'tcx>( let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| { let ty = cx.typeck_results().expr_ty(assignee); let rty = cx.typeck_results().expr_ty(rhs); - if_chain! { - if let Some((_, lang_item)) = binop_traits(op.node); - if let Some(trait_id) = cx.tcx.lang_items().get(lang_item); - let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id).def_id; - if trait_ref_of_method(cx, parent_fn) - .map_or(true, |t| t.path.res.def_id() != trait_id); - if implements_trait(cx, ty, trait_id, &[rty.into()]); - then { - // Primitive types execute assign-ops right-to-left. Every other type is left-to-right. - if !(ty.is_primitive() && rty.is_primitive()) { - // TODO: This will have false negatives as it doesn't check if the borrows are - // actually live at the end of their respective expressions. - let mut_borrows = mut_borrows_in_expr(cx, assignee); - let imm_borrows = imm_borrows_in_expr(cx, rhs); - if mut_borrows.iter().any(|id| imm_borrows.contains(id)) { - return; - } + if let Some((_, lang_item)) = binop_traits(op.node) + && let Some(trait_id) = cx.tcx.lang_items().get(lang_item) + && let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id).def_id + && trait_ref_of_method(cx, parent_fn).map_or(true, |t| t.path.res.def_id() != trait_id) + && implements_trait(cx, ty, trait_id, &[rty.into()]) + { + // Primitive types execute assign-ops right-to-left. Every other type is left-to-right. + if !(ty.is_primitive() && rty.is_primitive()) { + // TODO: This will have false negatives as it doesn't check if the borrows are + // actually live at the end of their respective expressions. + let mut_borrows = mut_borrows_in_expr(cx, assignee); + let imm_borrows = imm_borrows_in_expr(cx, rhs); + if mut_borrows.iter().any(|id| imm_borrows.contains(id)) { + return; } - span_lint_and_then( - cx, - ASSIGN_OP_PATTERN, - expr.span, - "manual implementation of an assign operation", - |diag| { - if let (Some(snip_a), Some(snip_r)) = - (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span)) - { - diag.span_suggestion( - expr.span, - "replace it with", - format!("{snip_a} {}= {snip_r}", op.node.as_str()), - Applicability::MachineApplicable, - ); - } - }, - ); } + span_lint_and_then( + cx, + ASSIGN_OP_PATTERN, + expr.span, + "manual implementation of an assign operation", + |diag| { + if let (Some(snip_a), Some(snip_r)) = + (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span)) + { + diag.span_suggestion( + expr.span, + "replace it with", + format!("{snip_a} {}= {snip_r}", op.node.as_str()), + Applicability::MachineApplicable, + ); + } + }, + ); } }; diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index abe8df195..52ef95461 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -3,7 +3,6 @@ use std::cmp::Ordering; use clippy_utils::consts::{constant, Constant}; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::layout::HasTyCtxt; @@ -23,19 +22,17 @@ fn comparison_to_const<'tcx>( typeck: &TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>, ) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { - if_chain! { - if let ExprKind::Binary(operator, left, right) = expr.kind; - if let Ok(cmp_op) = CmpOp::try_from(operator.node); - then { - match (constant(cx, typeck, left), constant(cx, typeck, right)) { - (Some(_), Some(_)) => None, - (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))), - (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))), - _ => None, - } - } else { - None + if let ExprKind::Binary(operator, left, right) = expr.kind + && let Ok(cmp_op) = CmpOp::try_from(operator.node) + { + match (constant(cx, typeck, left), constant(cx, typeck, right)) { + (Some(_), Some(_)) => None, + (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))), + (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))), + _ => None, } + } else { + None } } @@ -46,87 +43,89 @@ pub(super) fn check<'tcx>( right_cond: &'tcx Expr<'tcx>, span: Span, ) { - if_chain! { + if and_op.node == BinOpKind::And // Ensure that the binary operator is && - if and_op.node == BinOpKind::And; // Check that both operands to '&&' are themselves a binary operation // The `comparison_to_const` step also checks this, so this step is just an optimization - if let ExprKind::Binary(_, _, _) = left_cond.kind; - if let ExprKind::Binary(_, _, _) = right_cond.kind; + && let ExprKind::Binary(_, _, _) = left_cond.kind + && let ExprKind::Binary(_, _, _) = right_cond.kind - let typeck = cx.typeck_results(); + && let typeck = cx.typeck_results() // Check that both operands to '&&' compare a non-literal to a literal - if let Some((left_cmp_op, left_expr, left_const_expr, left_const, left_type)) = - comparison_to_const(cx, typeck, left_cond); - if let Some((right_cmp_op, right_expr, right_const_expr, right_const, right_type)) = - comparison_to_const(cx, typeck, right_cond); + && let Some((left_cmp_op, left_expr, left_const_expr, left_const, left_type)) = + comparison_to_const(cx, typeck, left_cond) + && let Some((right_cmp_op, right_expr, right_const_expr, right_const, right_type)) = + comparison_to_const(cx, typeck, right_cond) - if left_type == right_type; + && left_type == right_type // Check that the same expression is compared in both comparisons - if SpanlessEq::new(cx).eq_expr(left_expr, right_expr); + && SpanlessEq::new(cx).eq_expr(left_expr, right_expr) - if !left_expr.can_have_side_effects(); + && !left_expr.can_have_side_effects() // Compare the two constant expressions - if let Some(ordering) = Constant::partial_cmp(cx.tcx(), left_type, &left_const, &right_const); + && let Some(ordering) = Constant::partial_cmp(cx.tcx(), left_type, &left_const, &right_const) // Rule out the `x >= 42 && x <= 42` corner case immediately // Mostly to simplify the implementation, but it is also covered by `clippy::double_comparisons` - if !matches!( + && !matches!( (&left_cmp_op, &right_cmp_op, ordering), (CmpOp::Le | CmpOp::Ge, CmpOp::Le | CmpOp::Ge, Ordering::Equal) - ); - - then { - if left_cmp_op.direction() == right_cmp_op.direction() { - let lhs_str = snippet(cx, left_cond.span, ""); - let rhs_str = snippet(cx, right_cond.span, ""); - // We already know that either side of `&&` has no effect, - // but emit a different error message depending on which side it is - if left_side_is_useless(left_cmp_op, ordering) { - span_lint_and_note( - cx, - REDUNDANT_COMPARISONS, - span, - "left-hand side of `&&` operator has no effect", - Some(left_cond.span.until(right_cond.span)), - &format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"), - ); - } else { - span_lint_and_note( - cx, - REDUNDANT_COMPARISONS, - span, - "right-hand side of `&&` operator has no effect", - Some(and_op.span.to(right_cond.span)), - &format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"), - ); - } - // We could autofix this error but choose not to, - // because code triggering this lint probably not behaving correctly in the first place - } - else if !comparison_is_possible(left_cmp_op.direction(), ordering) { - let expr_str = snippet(cx, left_expr.span, ".."); - let lhs_str = snippet(cx, left_const_expr.span, ""); - let rhs_str = snippet(cx, right_const_expr.span, ""); - let note = match ordering { - Ordering::Less => format!("since `{lhs_str}` < `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"), - Ordering::Equal => format!("`{expr_str}` cannot simultaneously be greater than and less than `{lhs_str}`"), - Ordering::Greater => format!("since `{lhs_str}` > `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"), - }; + ) + { + if left_cmp_op.direction() == right_cmp_op.direction() { + let lhs_str = snippet(cx, left_cond.span, ""); + let rhs_str = snippet(cx, right_cond.span, ""); + // We already know that either side of `&&` has no effect, + // but emit a different error message depending on which side it is + if left_side_is_useless(left_cmp_op, ordering) { span_lint_and_note( cx, - IMPOSSIBLE_COMPARISONS, + REDUNDANT_COMPARISONS, span, - "boolean expression will never evaluate to 'true'", - None, - ¬e, + "left-hand side of `&&` operator has no effect", + Some(left_cond.span.until(right_cond.span)), + &format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"), ); + } else { + span_lint_and_note( + cx, + REDUNDANT_COMPARISONS, + span, + "right-hand side of `&&` operator has no effect", + Some(and_op.span.to(right_cond.span)), + &format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"), + ); + } + // We could autofix this error but choose not to, + // because code triggering this lint probably not behaving correctly in the first place + } else if !comparison_is_possible(left_cmp_op.direction(), ordering) { + let expr_str = snippet(cx, left_expr.span, ".."); + let lhs_str = snippet(cx, left_const_expr.span, ""); + let rhs_str = snippet(cx, right_const_expr.span, ""); + let note = match ordering { + Ordering::Less => format!( + "since `{lhs_str}` < `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`" + ), + Ordering::Equal => { + format!("`{expr_str}` cannot simultaneously be greater than and less than `{lhs_str}`") + }, + Ordering::Greater => format!( + "since `{lhs_str}` > `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`" + ), }; - } + span_lint_and_note( + cx, + IMPOSSIBLE_COMPARISONS, + span, + "boolean expression will never evaluate to 'true'", + None, + ¬e, + ); + }; } } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index bce6bdcaf..0561739d1 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -2,7 +2,6 @@ use clippy_utils::consts::{constant_with_source, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -105,14 +104,12 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { return is_signum(cx, child_expr); } - if_chain! { - if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind; - if sym!(signum) == method_name.ident.name; - // Check that the receiver of the signum() is a float (expressions[0] is the receiver of - // the method call) - then { - return is_float(cx, self_arg); - } + if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind + && sym!(signum) == method_name.ident.name + // Check that the receiver of the signum() is a float (expressions[0] is the receiver of + // the method call) + { + return is_float(cx, self_arg); } false } diff --git a/clippy_lints/src/operators/float_equality_without_abs.rs b/clippy_lints/src/operators/float_equality_without_abs.rs index a0a8b6aab..cace85a24 100644 --- a/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/clippy_lints/src/operators/float_equality_without_abs.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths, sugg}; -use if_chain::if_chain; use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -24,48 +23,43 @@ pub(crate) fn check<'tcx>( _ => return, }; - if_chain! { + if let ExprKind::Binary( // left hand side is a subtraction - if let ExprKind::Binary( Spanned { node: BinOpKind::Sub, .. }, val_l, val_r, - ) = lhs.kind; + ) = lhs.kind // right hand side matches either f32::EPSILON or f64::EPSILON - if let ExprKind::Path(ref epsilon_path) = rhs.kind; - if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id); - if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON); + && let ExprKind::Path(ref epsilon_path) = rhs.kind + && let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id) + && (match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON)) // values of the subtractions on the left hand side are of the type float - let t_val_l = cx.typeck_results().expr_ty(val_l); - let t_val_r = cx.typeck_results().expr_ty(val_r); - if let ty::Float(_) = t_val_l.kind(); - if let ty::Float(_) = t_val_r.kind(); - - then { - let sug_l = sugg::Sugg::hir(cx, val_l, ".."); - let sug_r = sugg::Sugg::hir(cx, val_r, ".."); - // format the suggestion - let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); - // spans the lint - span_lint_and_then( - cx, - FLOAT_EQUALITY_WITHOUT_ABS, - expr.span, - "float equality check without `.abs()`", - | diag | { - diag.span_suggestion( - lhs.span, - "add `.abs()`", - suggestion, - Applicability::MaybeIncorrect, - ); - } - ); - } + && let t_val_l = cx.typeck_results().expr_ty(val_l) + && let t_val_r = cx.typeck_results().expr_ty(val_r) + && let ty::Float(_) = t_val_l.kind() + && let ty::Float(_) = t_val_r.kind() + { + let sug_l = sugg::Sugg::hir(cx, val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, val_r, ".."); + // format the suggestion + let suggestion = format!( + "{}.abs()", + sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par() + ); + // spans the lint + span_lint_and_then( + cx, + FLOAT_EQUALITY_WITHOUT_ABS, + expr.span, + "float equality check without `.abs()`", + |diag| { + diag.span_suggestion(lhs.span, "add `.abs()`", suggestion, Applicability::MaybeIncorrect); + }, + ); } } diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index a2c3a4d8b..cb3916484 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -1,7 +1,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sext; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -19,15 +18,12 @@ pub(super) fn check<'tcx>( if op == BinOpKind::Rem { let lhs_operand = analyze_operand(lhs, cx, e); let rhs_operand = analyze_operand(rhs, cx, e); - if_chain! { - if let Some(lhs_operand) = lhs_operand; - if let Some(rhs_operand) = rhs_operand; - then { - check_const_operands(cx, e, &lhs_operand, &rhs_operand); - } - else { - check_non_const_operands(cx, e, lhs); - } + if let Some(lhs_operand) = lhs_operand + && let Some(rhs_operand) = rhs_operand + { + check_const_operands(cx, e, &lhs_operand, &rhs_operand); + } else { + check_non_const_operands(cx, e, lhs); } }; } diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index 932dd470f..7d8aa3f56 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::get_enclosing_block; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; @@ -180,41 +179,33 @@ fn in_impl<'tcx>( e: &'tcx Expr<'_>, bin_op: DefId, ) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> { - if_chain! { - if let Some(block) = get_enclosing_block(cx, e.hir_id); - if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id()); - let item = cx.tcx.hir().expect_item(impl_def_id.expect_local()); - if let ItemKind::Impl(item) = &item.kind; - if let Some(of_trait) = &item.of_trait; - if let Some(seg) = of_trait.path.segments.last(); - if let Res::Def(_, trait_id) = seg.res; - if trait_id == bin_op; - if let Some(generic_args) = seg.args; - if let Some(GenericArg::Type(other_ty)) = generic_args.args.last(); - - then { - Some((item.self_ty, other_ty)) - } - else { - None - } + if let Some(block) = get_enclosing_block(cx, e.hir_id) + && let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id()) + && let item = cx.tcx.hir().expect_item(impl_def_id.expect_local()) + && let ItemKind::Impl(item) = &item.kind + && let Some(of_trait) = &item.of_trait + && let Some(seg) = of_trait.path.segments.last() + && let Res::Def(_, trait_id) = seg.res + && trait_id == bin_op + && let Some(generic_args) = seg.args + && let Some(GenericArg::Type(other_ty)) = generic_args.args.last() + { + Some((item.self_ty, other_ty)) + } else { + None } } fn are_equal(cx: &LateContext<'_>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { - if_chain! { - if let ty::Adt(adt_def, _) = middle_ty.kind(); - if let Some(local_did) = adt_def.did().as_local(); - let item = cx.tcx.hir().expect_item(local_did); - let middle_ty_id = item.owner_id.to_def_id(); - if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; - if let Res::Def(_, hir_ty_id) = path.res; - - then { - hir_ty_id == middle_ty_id - } - else { - false - } + if let ty::Adt(adt_def, _) = middle_ty.kind() + && let Some(local_did) = adt_def.did().as_local() + && let item = cx.tcx.hir().expect_item(local_did) + && let middle_ty_id = item.owner_id.to_def_id() + && let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind + && let Res::Def(_, hir_ty_id) = path.res + { + hir_ty_id == middle_ty_id + } else { + false } } diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs index 1229c202f..9db2e2463 100644 --- a/clippy_lints/src/operators/ptr_eq.rs +++ b/clippy_lints/src/operators/ptr_eq.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -22,22 +21,20 @@ pub(super) fn check<'tcx>( _ => (left, right), }; - if_chain! { - if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left); - if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right); - if let Some(left_snip) = snippet_opt(cx, left_var.span); - if let Some(right_snip) = snippet_opt(cx, right_var.span); - then { - span_lint_and_sugg( - cx, - PTR_EQ, - expr.span, - LINT_MSG, - "try", - format!("std::ptr::eq({left_snip}, {right_snip})"), - Applicability::MachineApplicable, - ); - } + if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left) + && let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right) + && let Some(left_snip) = snippet_opt(cx, left_var.span) + && let Some(right_snip) = snippet_opt(cx, right_var.span) + { + span_lint_and_sugg( + cx, + PTR_EQ, + expr.span, + LINT_MSG, + "try", + format!("std::ptr::eq({left_snip}, {right_snip})"), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index d7cbbe13a..6e4e0c98d 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -4,7 +4,6 @@ use clippy_utils::{ can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, CaptureKind, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; @@ -122,73 +121,97 @@ fn try_get_option_occurrence<'tcx>( _ => expr, }; let (inner_pat, is_result) = try_get_inner_pat_and_is_result(cx, pat)?; - if_chain! { - if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind; - if let Some(some_captures) = can_move_expr_to_closure(cx, if_then); - if let Some(none_captures) = can_move_expr_to_closure(cx, if_else); - if some_captures + if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind + && let Some(some_captures) = can_move_expr_to_closure(cx, if_then) + && let Some(none_captures) = can_move_expr_to_closure(cx, if_else) + && some_captures .iter() .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2))) - .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref()); - then { - let capture_mut = if bind_annotation == BindingAnnotation::MUT { "mut " } else { "" }; - let some_body = peel_blocks(if_then); - let none_body = peel_blocks(if_else); - let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" }; - let capture_name = id.name.to_ident_string(); - let (as_ref, as_mut) = match &expr.kind { - ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), - ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), - _ if let Some(mutb) = cx.typeck_results().expr_ty(expr).ref_mutability() => { - (mutb == Mutability::Not, mutb == Mutability::Mut) - } - _ => (bind_annotation == BindingAnnotation::REF, bind_annotation == BindingAnnotation::REF_MUT), - }; + .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref()) + { + let capture_mut = if bind_annotation == BindingAnnotation::MUT { + "mut " + } else { + "" + }; + let some_body = peel_blocks(if_then); + let none_body = peel_blocks(if_else); + let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { + "map_or" + } else { + "map_or_else" + }; + let capture_name = id.name.to_ident_string(); + let (as_ref, as_mut) = match &expr.kind { + ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), + ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), + _ if let Some(mutb) = cx.typeck_results().expr_ty(expr).ref_mutability() => { + (mutb == Mutability::Not, mutb == Mutability::Mut) + }, + _ => ( + bind_annotation == BindingAnnotation::REF, + bind_annotation == BindingAnnotation::REF_MUT, + ), + }; - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if as_ref || as_mut { - let e = peel_hir_expr_while(cond_expr, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind { - match some_captures.get(local_id) - .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|()| none_captures.get(local_id))) - { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, - Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if as_ref || as_mut { + let e = peel_hir_expr_while(cond_expr, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved( + None, + Path { + res: Res::Local(local_id), + .. + }, + )) = e.kind + { + match some_captures.get(local_id).or_else(|| { + (method_sugg == "map_or_else") + .then_some(()) + .and_then(|()| none_captures.get(local_id)) + }) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), } } - - let mut app = Applicability::Unspecified; - - let (none_body, is_argless_call) = match none_body.kind { - ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() => (call_expr, true), - _ => (none_body, false), - }; - - return Some(OptionOccurrence { - option: format_option_in_sugg( - Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app), - as_ref, - as_mut, - ), - method_sugg: method_sugg.to_string(), - some_expr: format!( - "|{capture_mut}{capture_name}| {}", - Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app), - ), - none_expr: format!( - "{}{}", - if method_sugg == "map_or" || is_argless_call { "" } else if is_result { "|_| " } else { "|| "}, - Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app), - ), - }); } + + let mut app = Applicability::Unspecified; + + let (none_body, is_argless_call) = match none_body.kind { + ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() => (call_expr, true), + _ => (none_body, false), + }; + + return Some(OptionOccurrence { + option: format_option_in_sugg( + Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app), + as_ref, + as_mut, + ), + method_sugg: method_sugg.to_string(), + some_expr: format!( + "|{capture_mut}{capture_name}| {}", + Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app), + ), + none_expr: format!( + "{}{}", + if method_sugg == "map_or" || is_argless_call { + "" + } else if is_result { + "|_| " + } else { + "|| " + }, + Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app), + ), + }); } None diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index 38cd5043a..e661bfbb9 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::SpanlessEq; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -34,41 +33,37 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { // a + b < a, a > a + b, a < a - b, a - b > a fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r); - if_chain! { - if let ExprKind::Binary(ref op, first, second) = expr.kind; - if let ExprKind::Binary(ref op2, ident1, ident2) = first.kind; - if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind; - if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); - if cx.typeck_results().expr_ty(ident1).is_integral(); - if cx.typeck_results().expr_ty(ident2).is_integral(); - then { - if op.node == BinOpKind::Lt && op2.node == BinOpKind::Add { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); - } - if op.node == BinOpKind::Gt && op2.node == BinOpKind::Sub { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); - } + if let ExprKind::Binary(ref op, first, second) = expr.kind + && let ExprKind::Binary(ref op2, ident1, ident2) = first.kind + && let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind + && let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind + && let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind + && (eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0])) + && cx.typeck_results().expr_ty(ident1).is_integral() + && cx.typeck_results().expr_ty(ident2).is_integral() + { + if op.node == BinOpKind::Lt && op2.node == BinOpKind::Add { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); + } + if op.node == BinOpKind::Gt && op2.node == BinOpKind::Sub { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); } } - if_chain! { - if let ExprKind::Binary(ref op, first, second) = expr.kind; - if let ExprKind::Binary(ref op2, ident1, ident2) = second.kind; - if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind; - if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); - if cx.typeck_results().expr_ty(ident1).is_integral(); - if cx.typeck_results().expr_ty(ident2).is_integral(); - then { - if op.node == BinOpKind::Gt && op2.node == BinOpKind::Add { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); - } - if op.node == BinOpKind::Lt && op2.node == BinOpKind::Sub { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); - } + if let ExprKind::Binary(ref op, first, second) = expr.kind + && let ExprKind::Binary(ref op2, ident1, ident2) = second.kind + && let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind + && let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind + && let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind + && (eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0])) + && cx.typeck_results().expr_ty(ident1).is_integral() + && cx.typeck_results().expr_ty(ident2).is_integral() + { + if op.node == BinOpKind::Gt && op2.node == BinOpKind::Add { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); + } + if op.node == BinOpKind::Lt && op2.node == BinOpKind::Sub { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); } } } diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 68d3d00ac..1b0676241 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_hir; -use if_chain::if_chain; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -34,22 +33,24 @@ declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]); impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if_chain! { - if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind; - if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived); - if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); - if trait_ref.path.res.def_id() == eq_trait; - then { - for impl_item in *impl_items { - if impl_item.ident.name == sym::ne { - span_lint_hir( - cx, - PARTIALEQ_NE_IMPL, - impl_item.id.hir_id(), - impl_item.span, - "re-implementing `PartialEq::ne` is unnecessary", - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: impl_items, + .. + }) = item.kind + && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && let Some(eq_trait) = cx.tcx.lang_items().eq_trait() + && trait_ref.path.res.def_id() == eq_trait + { + for impl_item in *impl_items { + if impl_item.ident.name == sym::ne { + span_lint_hir( + cx, + PARTIALEQ_NE_IMPL, + impl_item.id.hir_id(), + impl_item.span, + "re-implementing `PartialEq::ne` is unnecessary", + ); } } }; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 4d7a055da..0f025c16a 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -5,7 +5,6 @@ use clippy_utils::source::snippet; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{is_self, is_self_ty}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -229,22 +228,23 @@ impl<'tcx> PassByRefOrValue { } let ty = cx.tcx.erase_late_bound_regions(ty); - if_chain! { - if is_copy(cx, ty); - if !is_self_ty(input); - if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); - if size > self.value_max_size; - then { - span_lint_and_sugg( - cx, - LARGE_TYPES_PASSED_BY_VALUE, - input.span, - &format!("this argument ({size} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", self.value_max_size), - "consider passing by reference instead", - format!("&{}", snippet(cx, input.span, "_")), - Applicability::MaybeIncorrect, - ); - } + if is_copy(cx, ty) + && !is_self_ty(input) + && let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) + && size > self.value_max_size + { + span_lint_and_sugg( + cx, + LARGE_TYPES_PASSED_BY_VALUE, + input.span, + &format!( + "this argument ({size} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", + self.value_max_size + ), + "consider passing by reference instead", + format!("&{}", snippet(cx, input.span, "_")), + Applicability::MaybeIncorrect, + ); } }, diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index 057b7e306..b638e8399 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, MethodCall, UnOp}; use rustc_ast::token; use rustc_errors::Applicability; @@ -118,25 +117,23 @@ impl EarlyLintPass for Precedence { arg = receiver; } - if_chain! { - if !all_odd; - if let ExprKind::Lit(lit) = &arg.kind; - if let token::LitKind::Integer | token::LitKind::Float = &lit.kind; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - PRECEDENCE, - expr.span, - "unary minus has lower precedence than method call", - "consider adding parentheses to clarify your intent", - format!( - "-({})", - snippet_with_applicability(cx, operand.span, "..", &mut applicability) - ), - applicability, - ); - } + if !all_odd + && let ExprKind::Lit(lit) = &arg.kind + && let token::LitKind::Integer | token::LitKind::Float = &lit.kind + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + PRECEDENCE, + expr.span, + "unary minus has lower precedence than method call", + "consider adding parentheses to clarify your intent", + format!( + "-({})", + snippet_with_applicability(cx, operand.span, "..", &mut applicability) + ), + applicability, + ); } } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index b133635e8..5c395143b 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -10,7 +10,6 @@ use clippy_utils::{ is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -179,17 +178,15 @@ fn expr_return_none_or_err( _ => false, }, ExprKind::Call(call_expr, args_expr) => { - if_chain! { - if smbl == sym::Result; - if let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind; - if let Some(segment) = path.segments.first(); - if let Some(err_sym) = err_sym; - if let Some(arg) = args_expr.first(); - if let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind; - if let Some(PathSegment { ident, .. }) = arg_path.segments.first(); - then { - return segment.ident.name == sym::Err && err_sym == ident.name; - } + if smbl == sym::Result + && let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind + && let Some(segment) = path.segments.first() + && let Some(err_sym) = err_sym + && let Some(arg) = args_expr.first() + && let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind + && let Some(PathSegment { ident, .. }) = arg_path.segments.first() + { + return segment.ident.name == sym::Err && err_sym == ident.name; } false }, @@ -218,81 +215,85 @@ impl QuestionMark { /// /// If it matches, it will suggest to use the question mark operator instead fn check_is_none_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if_chain! { - if !self.inside_try_block(); - if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr); - if !is_else_clause(cx.tcx, expr); - if let ExprKind::MethodCall(segment, caller, ..) = &cond.kind; - let caller_ty = cx.typeck_results().expr_ty(caller); - let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else); - if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block); - then { - let mut applicability = Applicability::MachineApplicable; - let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); - let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) && - !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); - let sugg = if let Some(else_inner) = r#else { - if eq_expr_value(cx, caller, peel_blocks(else_inner)) { - format!("Some({receiver_str}?)") - } else { - return; - } + if !self.inside_try_block() + && let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) + && !is_else_clause(cx.tcx, expr) + && let ExprKind::MethodCall(segment, caller, ..) = &cond.kind + && let caller_ty = cx.typeck_results().expr_ty(caller) + && let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else) + && (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block)) + { + let mut applicability = Applicability::MachineApplicable; + let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); + let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) + && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); + let sugg = if let Some(else_inner) = r#else { + if eq_expr_value(cx, caller, peel_blocks(else_inner)) { + format!("Some({receiver_str}?)") } else { - format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" }) - }; + return; + } + } else { + format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" }) + }; - span_lint_and_sugg( - cx, - QUESTION_MARK, - expr.span, - "this block may be rewritten with the `?` operator", - "replace it with", - sugg, - applicability, - ); - } + span_lint_and_sugg( + cx, + QUESTION_MARK, + expr.span, + "this block may be rewritten with the `?` operator", + "replace it with", + sugg, + applicability, + ); } } fn check_if_let_some_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if_chain! { - if !self.inside_try_block(); - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr); - if !is_else_clause(cx.tcx, expr); - if let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind; - if ddpos.as_opt_usize().is_none(); - if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind; - let caller_ty = cx.typeck_results().expr_ty(let_expr); - let if_block = IfBlockType::IfLet( + if !self.inside_try_block() + && let Some(higher::IfLet { + let_pat, + let_expr, + if_then, + if_else, + }) = higher::IfLet::hir(cx, expr) + && !is_else_clause(cx.tcx, expr) + && let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind + && ddpos.as_opt_usize().is_none() + && let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind + && let caller_ty = cx.typeck_results().expr_ty(let_expr) + && let if_block = IfBlockType::IfLet( cx.qpath_res(path1, let_pat.hir_id), caller_ty, ident.name, let_expr, if_then, - if_else + if_else, + ) + && ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) + || is_early_return(sym::Result, cx, &if_block)) + && if_else + .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) + .filter(|e| *e) + .is_none() + { + let mut applicability = Applicability::MachineApplicable; + let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); + let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_))); + let sugg = format!( + "{receiver_str}{}?{}", + if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, + if requires_semi { ";" } else { "" } + ); + span_lint_and_sugg( + cx, + QUESTION_MARK, + expr.span, + "this block may be rewritten with the `?` operator", + "replace it with", + sugg, + applicability, ); - if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) - || is_early_return(sym::Result, cx, &if_block); - if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none(); - then { - let mut applicability = Applicability::MachineApplicable; - let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); - let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_))); - let sugg = format!( - "{receiver_str}{}?{}", - if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, - if requires_semi { ";" } else { "" } - ); - span_lint_and_sugg( - cx, - QUESTION_MARK, - expr.span, - "this block may be rewritten with the `?` operator", - "replace it with", - sugg, - applicability, - ); - } } } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index ffd49de38..9bdba9940 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local}; -use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; @@ -282,16 +281,14 @@ fn check_possible_range_contains( // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have // the same operator precedence - if_chain! { - if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind; - if op == lhs_op.node; - let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()); - if let Some(snip) = &snippet_opt(cx, new_span); + if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind + && op == lhs_op.node + && let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()) + && let Some(snip) = &snippet_opt(cx, new_span) // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong - if snip.matches('(').count() == snip.matches(')').count(); - then { - check_possible_range_contains(cx, op, new_lhs, right, expr, new_span); - } + && snip.matches('(').count() == snip.matches(')').count() + { + check_possible_range_contains(cx, op, new_lhs, right, expr, new_span); } } @@ -348,71 +345,66 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> // exclusive range plus one: `x..(y+1)` fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if expr.span.can_be_used_for_suggestions(); - if let Some(higher::Range { + if expr.span.can_be_used_for_suggestions() + && let Some(higher::Range { start, end: Some(end), - limits: RangeLimits::HalfOpen - }) = higher::Range::hir(expr); - if let Some(y) = y_plus_one(cx, end); - then { - let span = expr.span; - span_lint_and_then( - cx, - RANGE_PLUS_ONE, - span, - "an inclusive range would be more readable", - |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); - let end = Sugg::hir(cx, y, "y").maybe_par(); - if let Some(is_wrapped) = &snippet_opt(cx, span) { - if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') { - diag.span_suggestion( - span, - "use", - format!("({start}..={end})"), - Applicability::MaybeIncorrect, - ); - } else { - diag.span_suggestion( - span, - "use", - format!("{start}..={end}"), - Applicability::MachineApplicable, // snippet - ); - } + limits: RangeLimits::HalfOpen, + }) = higher::Range::hir(expr) + && let Some(y) = y_plus_one(cx, end) + { + let span = expr.span; + span_lint_and_then( + cx, + RANGE_PLUS_ONE, + span, + "an inclusive range would be more readable", + |diag| { + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_par(); + if let Some(is_wrapped) = &snippet_opt(cx, span) { + if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') { + diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect); + } else { + diag.span_suggestion( + span, + "use", + format!("{start}..={end}"), + Applicability::MachineApplicable, // snippet + ); } - }, - ); - } + } + }, + ); } } // inclusive range minus one: `x..=(y-1)` fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if expr.span.can_be_used_for_suggestions(); - 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( - cx, - RANGE_MINUS_ONE, - expr.span, - "an exclusive range would be more readable", - |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); - let end = Sugg::hir(cx, y, "y").maybe_par(); - diag.span_suggestion( - expr.span, - "use", - format!("{start}..{end}"), - Applicability::MachineApplicable, // snippet - ); - }, - ); - } + if expr.span.can_be_used_for_suggestions() + && let Some(higher::Range { + start, + end: Some(end), + limits: RangeLimits::Closed, + }) = higher::Range::hir(expr) + && let Some(y) = y_minus_one(cx, end) + { + span_lint_and_then( + cx, + RANGE_MINUS_ONE, + expr.span, + "an exclusive range would be more readable", + |diag| { + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_par(); + diag.span_suggestion( + expr.span, + "use", + format!("{start}..{end}"), + Applicability::MachineApplicable, // snippet + ); + }, + ); } } @@ -446,52 +438,54 @@ 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::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); - if let Some(end_idx) = constant(cx, cx.typeck_results(), end); - if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); - if is_empty_range(limits, ordering); - then { - if inside_indexing_expr(cx, expr) { - // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ... - if ordering != Ordering::Equal { - span_lint( - cx, - REVERSED_EMPTY_RANGES, - expr.span, - "this range is reversed and using it to index a slice will panic at run-time", - ); - } - // ... except in for loop arguments for backwards compatibility with `reverse_range_loop` - } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) { - span_lint_and_then( + if let Some(higher::Range { + start: Some(start), + end: Some(end), + limits, + }) = higher::Range::hir(expr) + && let ty = cx.typeck_results().expr_ty(start) + && let ty::Int(_) | ty::Uint(_) = ty.kind() + && let Some(start_idx) = constant(cx, cx.typeck_results(), start) + && let Some(end_idx) = constant(cx, cx.typeck_results(), end) + && let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx) + && is_empty_range(limits, ordering) + { + if inside_indexing_expr(cx, expr) { + // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ... + if ordering != Ordering::Equal { + span_lint( cx, REVERSED_EMPTY_RANGES, expr.span, - "this range is empty so it will yield no values", - |diag| { - if ordering != Ordering::Equal { - let start_snippet = snippet(cx, start.span, "_"); - let end_snippet = snippet(cx, end.span, "_"); - let dots = match limits { - RangeLimits::HalfOpen => "..", - RangeLimits::Closed => "..=" - }; - - diag.span_suggestion( - expr.span, - "consider using the following if you are attempting to iterate over this \ - range in reverse", - format!("({end_snippet}{dots}{start_snippet}).rev()"), - Applicability::MaybeIncorrect, - ); - } - }, + "this range is reversed and using it to index a slice will panic at run-time", ); } + // ... except in for loop arguments for backwards compatibility with `reverse_range_loop` + } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) { + span_lint_and_then( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is empty so it will yield no values", + |diag| { + if ordering != Ordering::Equal { + let start_snippet = snippet(cx, start.span, "_"); + let end_snippet = snippet(cx, end.span, "_"); + let dots = match limits { + RangeLimits::HalfOpen => "..", + RangeLimits::Closed => "..=", + }; + + diag.span_suggestion( + expr.span, + "consider using the following if you are attempting to iterate over this \ + range in reverse", + format!("({end_snippet}{dots}{start_snippet}).rev()"), + Applicability::MaybeIncorrect, + ); + } + }, + ); } } } diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index 59ce289e7..b99af4465 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -118,26 +118,24 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< /// Checks whether the given `expr` is a call to `Arc::new`, `Rc::new`, or evaluates to a `Weak` fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> { - if_chain! { - if let ExprKind::Call(func, _args) = expr.kind; - if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind; - if let TyKind::Path(ref ty_path) = ty.kind; - if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id(); + if let ExprKind::Call(func, _args) = expr.kind + && let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind + && let TyKind::Path(ref ty_path) = ty.kind + && let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id() + { + if last_path_segment(func_path).ident.name == sym::new + && let Some(symbol) = cx + .tcx + .get_diagnostic_name(def_id) + .filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) + { + return Some((symbol, func.span)); + } - then { - if last_path_segment(func_path).ident.name == sym::new - && let Some(symbol) = cx - .tcx - .get_diagnostic_name(def_id) - .filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) { - return Some((symbol, func.span)); - } - - if let ty::Adt(adt, _) = *cx.typeck_results().expr_ty(expr).kind() - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::RcWeak | sym::ArcWeak)) - { - return Some((Symbol::intern("Weak"), func.span)); - } + if let ty::Adt(adt, _) = *cx.typeck_results().expr_ty(expr).kind() + && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::RcWeak | sym::ArcWeak)) + { + return Some((Symbol::intern("Weak"), func.span)); } } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index ef14e6896..88dbf9ef5 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -3,7 +3,6 @@ use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{def_id, Body, FnDecl, LangItem}; @@ -146,18 +145,16 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let pred_terminator = mir[ps[0]].terminator(); // receiver of the `deref()` call - let (pred_arg, deref_clone_ret) = if_chain! { - if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) = - is_call_with_ref_arg(cx, mir, &pred_terminator.kind); - if res == cloned; - if cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id); - if is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf) - || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString); - then { - (pred_arg, res) - } else { - continue; - } + let (pred_arg, deref_clone_ret) = if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) = + is_call_with_ref_arg(cx, mir, &pred_terminator.kind) + && res == cloned + && cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id) + && (is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf) + || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString)) + { + (pred_arg, res) + } else { + continue; }; let (local, cannot_move_out) = @@ -212,45 +209,37 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .assert_crate_local() .lint_root; - if_chain! { - if let Some(snip) = snippet_opt(cx, span); - if let Some(dot) = snip.rfind('.'); - then { - let sugg_span = span.with_lo( - span.lo() + BytePos(u32::try_from(dot).unwrap()) - ); - let mut app = Applicability::MaybeIncorrect; + if let Some(snip) = snippet_opt(cx, span) + && let Some(dot) = snip.rfind('.') + { + let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap())); + let mut app = Applicability::MaybeIncorrect; - let call_snip = &snip[dot + 1..]; - // Machine applicable when `call_snip` looks like `foobar()` - if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { - if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { - app = Applicability::MachineApplicable; - } + let call_snip = &snip[dot + 1..]; + // Machine applicable when `call_snip` looks like `foobar()` + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { + if call_snip + .as_bytes() + .iter() + .all(|b| b.is_ascii_alphabetic() || *b == b'_') + { + app = Applicability::MachineApplicable; } - - span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { - diag.span_suggestion( - sugg_span, - "remove this", - "", - app, - ); - if clone_usage.cloned_used { - diag.span_note( - span, - "cloned value is neither consumed nor mutated", - ); - } else { - diag.span_note( - span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), - "this value is dropped without further use", - ); - } - }); - } else { - span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); } + + span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { + diag.span_suggestion(sugg_span, "remove this", "", app); + if clone_usage.cloned_used { + diag.span_note(span, "cloned value is neither consumed nor mutated"); + } else { + diag.span_note( + span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), + "this value is dropped without further use", + ); + } + }); + } else { + span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); } } } @@ -262,18 +251,21 @@ fn is_call_with_ref_arg<'tcx>( mir: &'tcx mir::Body<'tcx>, kind: &'tcx mir::TerminatorKind<'tcx>, ) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, mir::Local)> { - if_chain! { - if let mir::TerminatorKind::Call { func, args, destination, .. } = kind; - if args.len() == 1; - if let mir::Operand::Move(mir::Place { local, .. }) = &args[0]; - if let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind(); - if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx)); - if !is_copy(cx, inner_ty); - then { - Some((def_id, *local, inner_ty, destination.as_local()?)) - } else { - None - } + if let mir::TerminatorKind::Call { + func, + args, + destination, + .. + } = kind + && args.len() == 1 + && let mir::Operand::Move(mir::Place { local, .. }) = &args[0] + && let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind() + && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx)) + && !is_copy(cx, inner_ty) + { + Some((def_id, *local, inner_ty, destination.as_local()?)) + } else { + None } } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index e679fab53..f2a006ebd 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -2,7 +2,6 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::get_parent_expr; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit as hir_visit; @@ -141,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } if let hir::ExprKind::Call(recv, _) = expr.kind - // don't lint if the receiver is a call, too. + // don't lint if the receiver is a call, too. // we do this in order to prevent linting multiple times; consider: // `(|| || 1)()()` // ^^ we only want to lint for this call (but we walk up the calls to consider both calls). @@ -201,14 +200,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if_chain! { - if let hir::ExprKind::Call(closure, _) = expr.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; - if self.path.segments[0].ident == path.segments[0].ident; - if self.path.res == path.res; - then { - self.count += 1; - } + if let hir::ExprKind::Call(closure, _) = expr.kind + && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind + && self.path.segments[0].ident == path.segments[0].ident + && self.path.res == path.res + { + self.count += 1; } hir_visit::walk_expr(self, expr); } @@ -223,25 +220,23 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } for w in block.stmts.windows(2) { - if_chain! { - if let hir::StmtKind::Local(local) = w[0].kind; - if let Option::Some(t) = local.init; - if let hir::ExprKind::Closure { .. } = t.kind; - if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind; - if let hir::StmtKind::Semi(second) = w[1].kind; - if let hir::ExprKind::Assign(_, call, _) = second.kind; - if let hir::ExprKind::Call(closure, _) = call.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; - if ident == path.segments[0].ident; - if count_closure_usage(cx, block, path) == 1; - then { - span_lint( - cx, - REDUNDANT_CLOSURE_CALL, - second.span, - "closure called just once immediately after it was declared", - ); - } + if let hir::StmtKind::Local(local) = w[0].kind + && let Option::Some(t) = local.init + && let hir::ExprKind::Closure { .. } = t.kind + && let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind + && let hir::StmtKind::Semi(second) = w[1].kind + && let hir::ExprKind::Assign(_, call, _) = second.kind + && let hir::ExprKind::Call(closure, _) = call.kind + && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind + && ident == path.segments[0].ident + && count_closure_usage(cx, block, path) == 1 + { + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "closure called just once immediately after it was declared", + ); } } } diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index 6bc0d0618..15b784039 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -46,40 +46,38 @@ declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); impl<'tcx> LateLintPass<'tcx> for RedundantLocals { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { - if_chain! { - if !local.span.is_desugaring(DesugaringKind::Async); + if !local.span.is_desugaring(DesugaringKind::Async) // the pattern is a single by-value binding - if let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind; + && let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind // the binding is not type-ascribed - if local.ty.is_none(); + && local.ty.is_none() // the expression is a resolved path - if let Some(expr) = local.init; - if let ExprKind::Path(qpath @ QPath::Resolved(None, path)) = expr.kind; + && let Some(expr) = local.init + && let ExprKind::Path(qpath @ QPath::Resolved(None, path)) = expr.kind // the path is a single segment equal to the local's name - if let [last_segment] = path.segments; - if last_segment.ident == ident; + && let [last_segment] = path.segments + && last_segment.ident == ident // resolve the path to its defining binding pattern - if let Res::Local(binding_id) = cx.qpath_res(&qpath, expr.hir_id); - if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id); + && let Res::Local(binding_id) = cx.qpath_res(&qpath, expr.hir_id) + && let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id) // the previous binding has the same mutability - if find_binding(binding_pat, ident).is_some_and(|bind| bind.1 == mutability); + && find_binding(binding_pat, ident).is_some_and(|bind| bind.1 == mutability) // the local does not change the effect of assignments to the binding. see #11290 - if !affects_assignments(cx, mutability, binding_id, local.hir_id); + && !affects_assignments(cx, mutability, binding_id, local.hir_id) // the local does not affect the code's drop behavior - if !needs_ordered_drop(cx, cx.typeck_results().expr_ty(expr)); + && !needs_ordered_drop(cx, cx.typeck_results().expr_ty(expr)) // the local is user-controlled - if !in_external_macro(cx.sess(), local.span); - if !is_from_proc_macro(cx, expr); - then { - span_lint_and_help( - cx, - REDUNDANT_LOCALS, - local.span, - &format!("redundant redefinition of a binding `{ident}`"), - Some(binding_pat.span), - &format!("`{ident}` is initially defined here"), - ); - } + && !in_external_macro(cx.sess(), local.span) + && !is_from_proc_macro(cx, expr) + { + span_lint_and_help( + cx, + REDUNDANT_LOCALS, + local.span, + &format!("redundant redefinition of a binding `{ident}`"), + Some(binding_pat.span), + &format!("`{ident}` is initially defined here"), + ); } } } diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 03673eb27..32e0c3749 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -45,28 +45,27 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]); impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if_chain! { - if cx.tcx.visibility(item.owner_id.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()); - if !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false); - if is_not_macro_export(item); - then { - let span = item.span.with_hi(item.ident.span.hi()); - let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); - span_lint_and_then( - cx, - REDUNDANT_PUB_CRATE, - span, - &format!("pub(crate) {descr} inside private module"), - |diag| { - diag.span_suggestion( - item.vis_span, - "consider using", - "pub".to_string(), - Applicability::MachineApplicable, - ); - }, - ); - } + if cx.tcx.visibility(item.owner_id.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) + && !cx.effective_visibilities.is_exported(item.owner_id.def_id) + && self.is_exported.last() == Some(&false) + && is_not_macro_export(item) + { + let span = item.span.with_hi(item.ident.span.hi()); + let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); + span_lint_and_then( + cx, + REDUNDANT_PUB_CRATE, + span, + &format!("pub(crate) {descr} inside private module"), + |diag| { + diag.span_suggestion( + item.vis_span, + "consider using", + "pub".to_string(), + Applicability::MachineApplicable, + ); + }, + ); } if let ItemKind::Mod { .. } = item.kind { diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 7adbd6791..c9fc65653 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs}; -use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -78,92 +77,83 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { } let ctxt = expr.span.ctxt(); - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; - if addressee.span.ctxt() == ctxt; - if let ExprKind::Index(indexed, range, _) = addressee.kind; - if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull); - then { - let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr)); - let (indexed_ty, indexed_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(indexed)); - let parent_expr = get_parent_expr(cx, expr); - let needs_parens_for_prefix = parent_expr.map_or(false, |parent| { - parent.precedence().order() > PREC_PREFIX - }); - let mut app = Applicability::MachineApplicable; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind + && addressee.span.ctxt() == ctxt + && let ExprKind::Index(indexed, range, _) = addressee.kind + && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) + { + let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr)); + let (indexed_ty, indexed_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(indexed)); + let parent_expr = get_parent_expr(cx, expr); + let needs_parens_for_prefix = parent_expr.map_or(false, |parent| parent.precedence().order() > PREC_PREFIX); + let mut app = Applicability::MachineApplicable; - let ((lint, msg), help, sugg) = if expr_ty == indexed_ty { - if expr_ref_count > indexed_ref_count { - // Indexing takes self by reference and can't return a reference to that - // reference as it's a local variable. The only way this could happen is if - // `self` contains a reference to the `Self` type. If this occurs then the - // lint no longer applies as it's essentially a field access, which is not - // redundant. - return; - } - let deref_count = indexed_ref_count - expr_ref_count; + let ((lint, msg), help, sugg) = if expr_ty == indexed_ty { + if expr_ref_count > indexed_ref_count { + // Indexing takes self by reference and can't return a reference to that + // reference as it's a local variable. The only way this could happen is if + // `self` contains a reference to the `Self` type. If this occurs then the + // lint no longer applies as it's essentially a field access, which is not + // redundant. + return; + } + let deref_count = indexed_ref_count - expr_ref_count; - let (lint, reborrow_str, help_str) = if mutability == Mutability::Mut { - // The slice was used to reborrow the mutable reference. - (DEREF_BY_SLICING_LINT, "&mut *", "reborrow the original value instead") - } else if matches!( - parent_expr, - Some(Expr { - kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _), - .. - }) - ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| { - matches!(a.kind, Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. }))) - }) { - // The slice was used to make a temporary reference. - (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead") - } else if deref_count != 0 { - (DEREF_BY_SLICING_LINT, "", "dereference the original value instead") - } else { - (REDUNDANT_SLICING_LINT, "", "use the original value instead") - }; + let (lint, reborrow_str, help_str) = if mutability == Mutability::Mut { + // The slice was used to reborrow the mutable reference. + (DEREF_BY_SLICING_LINT, "&mut *", "reborrow the original value instead") + } else if matches!( + parent_expr, + Some(Expr { + kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _), + .. + }) + ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| { + matches!( + a.kind, + Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })) + ) + }) { + // The slice was used to make a temporary reference. + (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead") + } else if deref_count != 0 { + (DEREF_BY_SLICING_LINT, "", "dereference the original value instead") + } else { + (REDUNDANT_SLICING_LINT, "", "use the original value instead") + }; - let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; - let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix { - format!("({reborrow_str}{}{snip})", "*".repeat(deref_count)) - } else { - format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) - }; + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix { + format!("({reborrow_str}{}{snip})", "*".repeat(deref_count)) + } else { + format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) + }; - (lint, help_str, sugg) - } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { - if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( - cx.param_env, - Ty::new_projection(cx.tcx,target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), - ) { - if deref_ty == expr_ty { - let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; - let sugg = if needs_parens_for_prefix { - format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - } else { - format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - }; - (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg) + (lint, help_str, sugg) + } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { + if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( + cx.param_env, + Ty::new_projection(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), + ) { + if deref_ty == expr_ty { + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + let sugg = if needs_parens_for_prefix { + format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) } else { - return; - } + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + }; + (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg) } else { return; } } else { return; - }; + } + } else { + return; + }; - span_lint_and_sugg( - cx, - lint, - expr.span, - msg, - help, - sugg, - app, - ); - } + span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg, app); } } } diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index c984a8286..0ba898e75 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::last_path_segment; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{GenericArg, GenericArgsParentheses, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -38,34 +37,30 @@ declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { - if_chain! { - if let TyKind::Ref(_, ref mut_ty) = ty.kind; - if mut_ty.mutbl == Mutability::Not; - if let TyKind::Path(ref qpath) = &mut_ty.ty.kind; - let last = last_path_segment(qpath); - if let Some(def_id) = last.res.opt_def_id(); - - if cx.tcx.is_diagnostic_item(sym::Option, def_id); - if let Some(params) = last_path_segment(qpath).args ; - if params.parenthesized == GenericArgsParentheses::No; - if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { + if let TyKind::Ref(_, ref mut_ty) = ty.kind + && mut_ty.mutbl == Mutability::Not + && let TyKind::Path(ref qpath) = &mut_ty.ty.kind + && let last = last_path_segment(qpath) + && let Some(def_id) = last.res.opt_def_id() + && cx.tcx.is_diagnostic_item(sym::Option, def_id) + && let Some(params) = last_path_segment(qpath).args + && params.parenthesized == GenericArgsParentheses::No + && let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(inner_ty) => Some(inner_ty), _ => None, - }); - if let TyKind::Ref(_, ref inner_mut_ty) = inner_ty.kind; - if inner_mut_ty.mutbl == Mutability::Not; - - then { - span_lint_and_sugg( - cx, - REF_OPTION_REF, - ty.span, - "since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`", - "try", - format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), - Applicability::MaybeIncorrect, - ); - } + }) + && let TyKind::Ref(_, ref inner_mut_ty) = inner_ty.kind + && inner_mut_ty.mutbl == Mutability::Not + { + span_lint_and_sugg( + cx, + REF_OPTION_REF, + ty.span, + "since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`", + "try", + format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), + Applicability::MaybeIncorrect, + ); } } } diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 12da29f11..69818db7c 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -47,58 +46,62 @@ fn without_parens(mut e: &Expr) -> &Expr { impl EarlyLintPass for DerefAddrOf { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { - if_chain! { - if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; - if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; - if deref_target.span.eq_ctxt(e.span); - if !addrof_target.span.from_expansion(); - then { - let mut applicability = Applicability::MachineApplicable; - let sugg = if e.span.from_expansion() { - if let Some(macro_source) = snippet_opt(cx, e.span) { - // Remove leading whitespace from the given span - // e.g: ` $visitor` turns into `$visitor` - let trim_leading_whitespaces = |span| { - snippet_opt(cx, span).and_then(|snip| { + if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind + && let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind + && deref_target.span.eq_ctxt(e.span) + && !addrof_target.span.from_expansion() + { + let mut applicability = Applicability::MachineApplicable; + let sugg = if e.span.from_expansion() { + if let Some(macro_source) = snippet_opt(cx, e.span) { + // Remove leading whitespace from the given span + // e.g: ` $visitor` turns into `$visitor` + let trim_leading_whitespaces = |span| { + snippet_opt(cx, span) + .and_then(|snip| { #[expect(clippy::cast_possible_truncation)] - snip.find(|c: char| !c.is_whitespace()).map(|pos| { - span.lo() + BytePos(pos as u32) - }) - }).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) - }; - - let mut generate_snippet = |pattern: &str| { - #[expect(clippy::cast_possible_truncation)] - macro_source.rfind(pattern).map(|pattern_pos| { - let rpos = pattern_pos + pattern.len(); - let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); - let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability) + snip.find(|c: char| !c.is_whitespace()) + .map(|pos| span.lo() + BytePos(pos as u32)) }) - }; + .map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) + }; - if *mutability == Mutability::Mut { - generate_snippet("mut") - } else { - generate_snippet("&") - } + let mut generate_snippet = |pattern: &str| { + #[expect(clippy::cast_possible_truncation)] + macro_source.rfind(pattern).map(|pattern_pos| { + let rpos = pattern_pos + pattern.len(); + let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); + let span = trim_leading_whitespaces(span_after_ref); + snippet_with_applicability(cx, span, "_", &mut applicability) + }) + }; + + if *mutability == Mutability::Mut { + generate_snippet("mut") } else { - Some(snippet_with_applicability(cx, e.span, "_", &mut applicability)) + generate_snippet("&") } } else { - Some(snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)) - }; - if let Some(sugg) = sugg { - span_lint_and_sugg( - cx, - DEREF_ADDROF, - e.span, - "immediately dereferencing a reference", - "try", - sugg.to_string(), - applicability, - ); + Some(snippet_with_applicability(cx, e.span, "_", &mut applicability)) } + } else { + Some(snippet_with_applicability( + cx, + addrof_target.span, + "_", + &mut applicability, + )) + }; + if let Some(sugg) = sugg { + span_lint_and_sugg( + cx, + DEREF_ADDROF, + e.span, + "immediately dereferencing a reference", + "try", + sugg.to_string(), + applicability, + ); } } } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index b795e4b15..3571b9bfb 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -191,13 +191,11 @@ fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { } fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; - if let ExprKind::Array(exprs) = expr.kind; - then { - for expr in exprs { - check_regex(cx, expr, utf8); - } + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind + && let ExprKind::Array(exprs) = expr.kind + { + for expr in exprs { + check_regex(cx, expr, utf8); } } } diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 245029a06..ad22b751b 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -69,35 +69,32 @@ declare_clippy_lint! { declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]); fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, owner_id: OwnerId) { - if_chain! { + if !in_external_macro(cx.sess(), span) // If it comes from an external macro, better ignore it. - if !in_external_macro(cx.sess(), span); - if decl.implicit_self.has_implicit_self(); + && decl.implicit_self.has_implicit_self() // We only show this warning for public exported methods. - if cx.effective_visibilities.is_exported(fn_def); + && cx.effective_visibilities.is_exported(fn_def) // We don't want to emit this lint if the `#[must_use]` attribute is already there. - if !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)); - if cx.tcx.visibility(fn_def.to_def_id()).is_public(); - let ret_ty = return_ty(cx, owner_id); - let self_arg = nth_arg(cx, owner_id, 0); + && !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)) + && cx.tcx.visibility(fn_def.to_def_id()).is_public() + && let ret_ty = return_ty(cx, owner_id) + && let self_arg = nth_arg(cx, owner_id, 0) // If `Self` has the same type as the returned type, then we want to warn. // // For this check, we don't want to remove the reference on the returned type because if // there is one, we shouldn't emit a warning! - if self_arg.peel_refs() == ret_ty; + && self_arg.peel_refs() == ret_ty // If `Self` is already marked as `#[must_use]`, no need for the attribute here. - if !is_must_use_ty(cx, ret_ty); - - then { - span_lint_and_help( - cx, - RETURN_SELF_NOT_MUST_USE, - span, - "missing `#[must_use]` attribute on a method returning `Self`", - None, - "consider adding the `#[must_use]` attribute to the method or directly to the `Self` type" - ); - } + && !is_must_use_ty(cx, ret_ty) + { + span_lint_and_help( + cx, + RETURN_SELF_NOT_MUST_USE, + span, + "missing `#[must_use]` attribute on a method returning `Self`", + None, + "consider adding the `#[must_use]` attribute to the method or directly to the `Self` type", + ); } } @@ -111,18 +108,15 @@ impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse { span: Span, fn_def: LocalDefId, ) { - if_chain! { + if matches!(kind, FnKind::Method(_, _)) // We are only interested in methods, not in functions or associated functions. - if matches!(kind, FnKind::Method(_, _)); - if let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id()); + && let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id()) // We don't want this method to be te implementation of a trait because the // `#[must_use]` should be put on the trait definition directly. - if cx.tcx.trait_id_of_impl(impl_def).is_none(); - - then { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def); - check_method(cx, decl, fn_def, span, hir_id.expect_owner()); - } + && cx.tcx.trait_id_of_impl(impl_def).is_none() + { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def); + check_method(cx, decl, fn_def, span, hir_id.expect_owner()); } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 72235a954..7468bda02 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -4,7 +4,6 @@ use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{fn_def_id, is_from_proc_macro, path_to_local_id, span_find_starting_semi}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -189,50 +188,45 @@ impl<'tcx> LateLintPass<'tcx> for Return { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { // we need both a let-binding stmt and an expr - if_chain! { - if let Some(retexpr) = block.expr; - if let Some(stmt) = block.stmts.iter().last(); - if let StmtKind::Local(local) = &stmt.kind; - if local.ty.is_none(); - if cx.tcx.hir().attrs(local.hir_id).is_empty(); - if let Some(initexpr) = &local.init; - if let PatKind::Binding(_, local_id, _, _) = local.pat.kind; - if path_to_local_id(retexpr, local_id); - if !last_statement_borrows(cx, initexpr); - if !in_external_macro(cx.sess(), initexpr.span); - if !in_external_macro(cx.sess(), retexpr.span); - if !local.span.from_expansion(); - then { - span_lint_hir_and_then( - cx, - LET_AND_RETURN, - retexpr.hir_id, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); + if let Some(retexpr) = block.expr + && let Some(stmt) = block.stmts.iter().last() + && let StmtKind::Local(local) = &stmt.kind + && local.ty.is_none() + && cx.tcx.hir().attrs(local.hir_id).is_empty() + && let Some(initexpr) = &local.init + && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && path_to_local_id(retexpr, local_id) + && !last_statement_borrows(cx, initexpr) + && !in_external_macro(cx.sess(), initexpr.span) + && !in_external_macro(cx.sess(), retexpr.span) + && !local.span.from_expansion() + { + span_lint_hir_and_then( + cx, + LET_AND_RETURN, + retexpr.hir_id, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); - if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { - if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { - if !has_enclosing_paren(&snippet) { - snippet = format!("({snippet})"); - } - snippet.push_str(" as _"); + if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { + if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { + if !has_enclosing_paren(&snippet) { + snippet = format!("({snippet})"); } - err.multipart_suggestion( - "return the expression directly", - vec![ - (local.span, String::new()), - (retexpr.span, snippet), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); + snippet.push_str(" as _"); } - }, - ); - } + err.multipart_suggestion( + "return the expression directly", + vec![(local.span, String::new()), (retexpr.span, snippet)], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); } } diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 9587326ca..52475ff9e 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -75,26 +75,23 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { match of_trait { Some(trait_ref) => { - let mut methods_in_trait: BTreeSet = if_chain! { - if let Some(Node::TraitRef(TraitRef { path, .. })) = - cx.tcx.hir().find(trait_ref.hir_ref_id); - if let Res::Def(DefKind::Trait, did) = path.res; - then{ - // FIXME: if - // `rustc_middle::ty::assoc::AssocItems::items` is public, - // we can iterate its keys instead of `in_definition_order`, - // which's more efficient - cx.tcx - .associated_items(did) - .in_definition_order() - .filter(|assoc_item| { - matches!(assoc_item.kind, AssocKind::Fn) - }) - .map(|assoc_item| assoc_item.name) - .collect() - }else{ - BTreeSet::new() - } + let mut methods_in_trait: BTreeSet = if let Some(Node::TraitRef(TraitRef { + path, .. + })) = cx.tcx.hir().find(trait_ref.hir_ref_id) + && let Res::Def(DefKind::Trait, did) = path.res + { + // FIXME: if + // `rustc_middle::ty::assoc::AssocItems::items` is public, + // we can iterate its keys instead of `in_definition_order`, + // which's more efficient + cx.tcx + .associated_items(did) + .in_definition_order() + .filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn)) + .map(|assoc_item| assoc_item.name) + .collect() + } else { + BTreeSet::new() }; let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { diff --git a/clippy_lints/src/self_named_constructors.rs b/clippy_lints/src/self_named_constructors.rs index b92014f68..c1edcf509 100644 --- a/clippy_lints/src/self_named_constructors.rs +++ b/clippy_lints/src/self_named_constructors.rs @@ -70,22 +70,20 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { return; } - if_chain! { - if let Some(self_def) = self_ty.ty_adt_def(); - if let Some(self_local_did) = self_def.did().as_local(); - let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); - if let Some(Node::Item(x)) = cx.tcx.hir().find(self_id); - let type_name = x.ident.name.as_str().to_lowercase(); - if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name; - - then { - span_lint( - cx, - SELF_NAMED_CONSTRUCTORS, - impl_item.span, - &format!("constructor `{}` has the same name as the type", impl_item.ident.name), - ); - } + if let Some(self_def) = self_ty.ty_adt_def() + && let Some(self_local_did) = self_def.did().as_local() + && let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did) + && let Some(Node::Item(x)) = cx.tcx.hir().find(self_id) + && let type_name = x.ident.name.as_str().to_lowercase() + && (impl_item.ident.name.as_str() == type_name + || impl_item.ident.name.as_str().replace('_', "") == type_name) + { + span_lint( + cx, + SELF_NAMED_CONSTRUCTORS, + impl_item.span, + &format!("constructor `{}` has the same name as the type", impl_item.ident.name), + ); } } } diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index ccf8b9977..3aabcadaa 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,7 +1,6 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -38,30 +37,29 @@ declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED] impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if_chain! { - if !block.span.from_expansion(); - if let Some(expr) = block.expr; - let t_expr = cx.typeck_results().expr_ty(expr); - if t_expr.is_unit(); - let mut app = Applicability::MachineApplicable; - if let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0; - if !snippet.ends_with('}') && !snippet.ends_with(';'); - if cx.sess().source_map().is_multiline(block.span); - then { - // filter out the desugared `for` loop - if let ExprKind::DropTemps(..) = &expr.kind { - return; - } - span_lint_and_sugg( - cx, - SEMICOLON_IF_NOTHING_RETURNED, - expr.span.source_callsite(), - "consider adding a `;` to the last statement for consistent formatting", - "add a `;` here", - format!("{snippet};"), - app, - ); + if !block.span.from_expansion() + && let Some(expr) = block.expr + && let t_expr = cx.typeck_results().expr_ty(expr) + && t_expr.is_unit() + && let mut app = Applicability::MachineApplicable + && let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0 + && !snippet.ends_with('}') + && !snippet.ends_with(';') + && cx.sess().source_map().is_multiline(block.span) + { + // filter out the desugared `for` loop + if let ExprKind::DropTemps(..) = &expr.kind { + return; } + span_lint_and_sugg( + cx, + SEMICOLON_IF_NOTHING_RETURNED, + expr.span.source_callsite(), + "consider adding a `;` to the last statement for consistent formatting", + "add a `;` here", + format!("{snippet};"), + app, + ); } } } diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index b940cac60..0385f1a98 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -2,7 +2,6 @@ //! expecting a count of T use clippy_utils::diagnostics::span_lint_and_help; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty, TypeAndMut}; @@ -39,16 +38,17 @@ declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option> { match expr.kind { ExprKind::Call(count_func, _func_args) => { - if_chain! { - if !inverted; - if let ExprKind::Path(ref count_func_qpath) = count_func.kind; - if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); - if matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::mem_size_of | sym::mem_size_of_val)); - then { - cx.typeck_results().node_args(count_func.hir_id).types().next() - } else { - None - } + if !inverted + && let ExprKind::Path(ref count_func_qpath) = count_func.kind + && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id() + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::mem_size_of | sym::mem_size_of_val) + ) + { + cx.typeck_results().node_args(count_func.hir_id).types().next() + } else { + None } }, ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node => { @@ -80,13 +80,12 @@ fn get_pointee_ty_and_count_expr<'tcx>( "wrapping_offset", ]; - if_chain! { + if let ExprKind::Call(func, [.., count]) = expr.kind // Find calls to ptr::{copy, copy_nonoverlapping} // and ptr::{swap_nonoverlapping, write_bytes}, - if let ExprKind::Call(func, [.., count]) = expr.kind; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if matches!(cx.tcx.get_diagnostic_name(def_id), Some( + && let ExprKind::Path(ref func_qpath) = func.kind + && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() + && matches!(cx.tcx.get_diagnostic_name(def_id), Some( sym::ptr_copy | sym::ptr_copy_nonoverlapping | sym::ptr_slice_from_raw_parts @@ -95,26 +94,23 @@ fn get_pointee_ty_and_count_expr<'tcx>( | sym::ptr_write_bytes | sym::slice_from_raw_parts | sym::slice_from_raw_parts_mut - )); + )) // Get the pointee type - if let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next(); - then { - return Some((pointee_ty, count)); - } + && let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next() + { + return Some((pointee_ty, count)); }; - if_chain! { + if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods - if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind; - let method_ident = method_path.ident.as_str(); - if METHODS.iter().any(|m| *m == method_ident); + && let method_ident = method_path.ident.as_str() + && METHODS.iter().any(|m| *m == method_ident) // Get the pointee type - if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = - cx.typeck_results().expr_ty(ptr_self).kind(); - then { - return Some((*pointee_ty, count)); - } + && let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = + cx.typeck_results().expr_ty(ptr_self).kind() + { + return Some((*pointee_ty, count)); }; None } @@ -127,25 +123,16 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { const LINT_MSG: &str = "found a count of bytes \ instead of a count of elements of `T`"; - if_chain! { + if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr) // Find calls to functions with an element count parameter and get // the pointee type and count parameter expression - if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); // Find a size_of call in the count parameter expression and // check that it's the same type - if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false); - if pointee_ty == ty_used_for_size_of; - then { - span_lint_and_help( - cx, - SIZE_OF_IN_ELEMENT_COUNT, - count_expr.span, - LINT_MSG, - None, - HELP_MSG - ); - } + && let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false) + && pointee_ty == ty_used_for_size_of + { + span_lint_and_help(cx, SIZE_OF_IN_ELEMENT_COUNT, count_expr.span, LINT_MSG, None, HELP_MSG); }; } } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 2244eab96..733da7904 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -5,7 +5,6 @@ use clippy_utils::{ get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, paths, SpanlessEq, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; @@ -103,41 +102,35 @@ enum InitializationType<'tcx> { impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)` - if_chain! { - if let ExprKind::Assign(left, right, _) = expr.kind; - if let Some(local_id) = path_to_local(left); - if let Some(size_expr) = Self::as_vec_initializer(cx, right); + if let ExprKind::Assign(left, right, _) = expr.kind + && let Some(local_id) = path_to_local(left) + && let Some(size_expr) = Self::as_vec_initializer(cx, right) + { + let vi = VecAllocation { + local_id, + allocation_expr: right, + size_expr, + }; - then { - let vi = VecAllocation { - local_id, - allocation_expr: right, - size_expr, - }; - - Self::search_initialization(cx, vi, expr.hir_id); - } + Self::search_initialization(cx, vi, expr.hir_id); } } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` // or `Vec::new()` - if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind; - if let Some(init) = local.init; - if let Some(size_expr) = Self::as_vec_initializer(cx, init); + if let StmtKind::Local(local) = stmt.kind + && let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind + && let Some(init) = local.init + && let Some(size_expr) = Self::as_vec_initializer(cx, init) + { + let vi = VecAllocation { + local_id, + allocation_expr: init, + size_expr, + }; - then { - let vi = VecAllocation { - local_id, - allocation_expr: init, - size_expr, - }; - - Self::search_initialization(cx, vi, stmt.hir_id); - } + Self::search_initialization(cx, vi, stmt.hir_id); } } } @@ -242,16 +235,13 @@ struct VectorInitializationVisitor<'a, 'tcx> { impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Checks if the given expression is extending a vector with `repeat(0).take(..)` fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { - if self.initialization_found; - if let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind; - if path_to_local_id(self_arg, self.vec_alloc.local_id); - if path.ident.name == sym!(extend); - if self.is_repeat_take(extend_arg); - - then { - self.slow_expression = Some(InitializationType::Extend(expr)); - } + if self.initialization_found + && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind + && path_to_local_id(self_arg, self.vec_alloc.local_id) + && path.ident.name == sym!(extend) + && self.is_repeat_take(extend_arg) + { + self.slow_expression = Some(InitializationType::Extend(expr)); } } @@ -281,21 +271,19 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool { - if_chain! { - if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind; - if take_path.ident.name == sym!(take); + if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind + && take_path.ident.name == sym!(take) // Check that take is applied to `repeat(0)` - if self.is_repeat_zero(recv); - then { - if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { - // Check that len expression is equals to `with_capacity` expression - return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) - || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity") - } - - self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); - return true; + && self.is_repeat_zero(recv) + { + if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { + // Check that len expression is equals to `with_capacity` expression + return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity"); } + + self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); + return true; } false @@ -303,15 +291,13 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if given expression is `repeat(0)` fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind; - if is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat); - if is_integer_literal(repeat_arg, 0); - then { - true - } else { - false - } + if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind + && is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat) + && is_integer_literal(repeat_arg, 0) + { + true + } else { + false } } } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index a44adc938..baa9750cc 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -5,7 +5,6 @@ use clippy_utils::{ get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls, peel_blocks, SpanlessEq, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath}; @@ -251,129 +250,116 @@ const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { - #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use rustc_ast::LitKind; - if_chain! { + if let ExprKind::Call(fun, args) = e.kind // Find std::str::converts::from_utf8 - if let ExprKind::Call(fun, args) = e.kind; - if is_path_diagnostic_item(cx, fun, sym::str_from_utf8); + && is_path_diagnostic_item(cx, fun, sym::str_from_utf8) // Find string::as_bytes - if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind; - if let ExprKind::Index(left, right, _) = args.kind; - let (method_names, expressions, _) = method_calls(left, 1); - if method_names.len() == 1; - if expressions.len() == 1; - if expressions[0].1.is_empty(); - if method_names[0] == sym!(as_bytes); + && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind + && let ExprKind::Index(left, right, _) = args.kind + && let (method_names, expressions, _) = method_calls(left, 1) + && method_names.len() == 1 + && expressions.len() == 1 + && expressions[0].1.is_empty() + && method_names[0] == sym!(as_bytes) // Check for slicer - if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind; + && let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind + { + let mut applicability = Applicability::MachineApplicable; + let string_expression = &expressions[0].0; - then { - let mut applicability = Applicability::MachineApplicable; - let string_expression = &expressions[0].0; + let snippet_app = snippet_with_applicability(cx, string_expression.span, "..", &mut applicability); - let snippet_app = snippet_with_applicability( - cx, - string_expression.span, "..", - &mut applicability, - ); - - span_lint_and_sugg( - cx, - STRING_FROM_UTF8_AS_BYTES, - e.span, - "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", - "try", - format!("Some(&{snippet_app}[{}])", snippet(cx, right.span, "..")), - applicability - ) - } + span_lint_and_sugg( + cx, + STRING_FROM_UTF8_AS_BYTES, + e.span, + "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", + "try", + format!("Some(&{snippet_app}[{}])", snippet(cx, right.span, "..")), + applicability, + ); } - if_chain! { - if !in_external_macro(cx.sess(), e.span); - if let ExprKind::MethodCall(path, receiver, ..) = &e.kind; - if path.ident.name == sym!(as_bytes); - if let ExprKind::Lit(lit) = &receiver.kind; - if let LitKind::Str(lit_content, _) = &lit.node; - then { - let callsite = snippet(cx, receiver.span.source_callsite(), r#""foo""#); - let mut applicability = Applicability::MachineApplicable; - if callsite.starts_with("include_str!") { - span_lint_and_sugg( - cx, - STRING_LIT_AS_BYTES, - e.span, - "calling `as_bytes()` on `include_str!(..)`", - "consider using `include_bytes!(..)` instead", - snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability).replacen( - "include_str", - "include_bytes", - 1, - ), - applicability, - ); - } else if lit_content.as_str().is_ascii() - && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT - && !receiver.span.from_expansion() - { - if let Some((parent, id)) = get_expr_use_or_unification_node(cx.tcx, e) - && let Node::Expr(parent) = parent - && let ExprKind::Match(scrutinee, ..) = parent.kind - && scrutinee.hir_id == id - { - // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces - // `&[u8]`. This change would prevent matching with different sized slices. - } else if !callsite.starts_with("env!") { - span_lint_and_sugg( - cx, - STRING_LIT_AS_BYTES, - e.span, - "calling `as_bytes()` on a string literal", - "consider using a byte string literal instead", - format!( - "b{}", - snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) - ), - applicability, - ); - } - } - } - } - - if_chain! { - if let ExprKind::MethodCall(path, recv, [], _) = &e.kind; - if path.ident.name == sym!(into_bytes); - if let ExprKind::MethodCall(path, recv, [], _) = &recv.kind; - if matches!(path.ident.name.as_str(), "to_owned" | "to_string"); - if let ExprKind::Lit(lit) = &recv.kind; - if let LitKind::Str(lit_content, _) = &lit.node; - - if lit_content.as_str().is_ascii(); - if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT; - if !recv.span.from_expansion(); - then { - let mut applicability = Applicability::MachineApplicable; - + if !in_external_macro(cx.sess(), e.span) + && let ExprKind::MethodCall(path, receiver, ..) = &e.kind + && path.ident.name == sym!(as_bytes) + && let ExprKind::Lit(lit) = &receiver.kind + && let LitKind::Str(lit_content, _) = &lit.node + { + let callsite = snippet(cx, receiver.span.source_callsite(), r#""foo""#); + let mut applicability = Applicability::MachineApplicable; + if callsite.starts_with("include_str!") { span_lint_and_sugg( cx, STRING_LIT_AS_BYTES, e.span, - "calling `into_bytes()` on a string literal", - "consider using a byte string literal instead", - format!( - "b{}.to_vec()", - snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability) + "calling `as_bytes()` on `include_str!(..)`", + "consider using `include_bytes!(..)` instead", + snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability).replacen( + "include_str", + "include_bytes", + 1, ), applicability, ); + } else if lit_content.as_str().is_ascii() + && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT + && !receiver.span.from_expansion() + { + if let Some((parent, id)) = get_expr_use_or_unification_node(cx.tcx, e) + && let Node::Expr(parent) = parent + && let ExprKind::Match(scrutinee, ..) = parent.kind + && scrutinee.hir_id == id + { + // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces + // `&[u8]`. This change would prevent matching with different sized slices. + } else if !callsite.starts_with("env!") { + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `as_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}", + snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) + ), + applicability, + ); + } } } + + if let ExprKind::MethodCall(path, recv, [], _) = &e.kind + && path.ident.name == sym!(into_bytes) + && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind + && matches!(path.ident.name.as_str(), "to_owned" | "to_string") + && let ExprKind::Lit(lit) = &recv.kind + && let LitKind::Str(lit_content, _) = &lit.node + && lit_content.as_str().is_ascii() + && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT + && !recv.span.from_expansion() + { + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `into_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}.to_vec()", + snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability) + ), + applicability, + ); + } } } @@ -406,22 +392,20 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; - if path.ident.name == sym::to_string; - let ty = cx.typeck_results().expr_ty(self_arg); - if let ty::Ref(_, ty, ..) = ty.kind(); - if ty.is_str(); - then { - span_lint_and_help( - cx, - STR_TO_STRING, - expr.span, - "`to_string()` called on a `&str`", - None, - "consider using `.to_owned()`", - ); - } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind + && path.ident.name == sym::to_string + && let ty = cx.typeck_results().expr_ty(self_arg) + && let ty::Ref(_, ty, ..) = ty.kind() + && ty.is_str() + { + span_lint_and_help( + cx, + STR_TO_STRING, + expr.span, + "`to_string()` called on a `&str`", + None, + "consider using `.to_owned()`", + ); } } } @@ -456,21 +440,19 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; - if path.ident.name == sym::to_string; - let ty = cx.typeck_results().expr_ty(self_arg); - if is_type_lang_item(cx, ty, LangItem::String); - then { - span_lint_and_help( - cx, - STRING_TO_STRING, - expr.span, - "`to_string()` called on a `String`", - None, - "consider using `.clone()`", - ); - } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind + && path.ident.name == sym::to_string + && let ty = cx.typeck_results().expr_ty(self_arg) + && is_type_lang_item(cx, ty, LangItem::String) + { + span_lint_and_help( + cx, + STRING_TO_STRING, + expr.span, + "`to_string()` called on a `String`", + None, + "consider using `.clone()`", + ); } } } @@ -500,26 +482,24 @@ declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]); impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { let tyckres = cx.typeck_results(); - if_chain! { - if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind; - if path.ident.name == sym!(split_whitespace); - if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id); - if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id); - if let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind; - if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str(); - if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id); - if is_one_of_trim_diagnostic_items(cx, trim_def_id); - then { - span_lint_and_sugg( - cx, - TRIM_SPLIT_WHITESPACE, - trim_span.with_hi(split_ws_span.lo()), - &format!("found call to `str::{trim_fn_name}` before `str::split_whitespace`"), - &format!("remove `{trim_fn_name}()`"), - String::new(), - Applicability::MachineApplicable, - ); - } + if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind + && path.ident.name == sym!(split_whitespace) + && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id) + && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id) + && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind + && let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str() + && let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id) + && is_one_of_trim_diagnostic_items(cx, trim_def_id) + { + span_lint_and_sugg( + cx, + TRIM_SPLIT_WHITESPACE, + trim_span.with_hi(split_ws_span.lo()), + &format!("found call to `str::{trim_fn_name}` before `str::split_whitespace`"), + &format!("remove `{trim_fn_name}()`"), + String::new(), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index b3db5e9a4..644664b10 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -3,7 +3,6 @@ use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::is_expr_unsafe; use clippy_utils::{get_parent_node, match_libc_symbol}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; @@ -41,48 +40,45 @@ declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]); impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::Call(func, [recv]) = expr.kind; - if let ExprKind::Path(path) = &func.kind; - if let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if match_libc_symbol(cx, did, "strlen"); - if let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind; - if !recv.span.from_expansion(); - if path.ident.name == sym::as_ptr; - then { - let ctxt = expr.span.ctxt(); - let span = match get_parent_node(cx.tcx, expr.hir_id) { - Some(Node::Block(&Block { - rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), span, .. - })) - if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => { - span - } - _ => expr.span, - }; - - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - let mut app = Applicability::MachineApplicable; - let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; - let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) { - "as_bytes" - } else if is_type_lang_item(cx, ty, LangItem::CStr) { - "to_bytes" - } else { - return; - }; - - span_lint_and_sugg( - cx, - STRLEN_ON_C_STRINGS, + if !expr.span.from_expansion() + && let ExprKind::Call(func, [recv]) = expr.kind + && let ExprKind::Path(path) = &func.kind + && let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id() + && match_libc_symbol(cx, did, "strlen") + && let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind + && !recv.span.from_expansion() + && path.ident.name == sym::as_ptr + { + let ctxt = expr.span.ctxt(); + let span = match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Block(&Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), span, - "using `libc::strlen` on a `CString` or `CStr` value", - "try", - format!("{val_name}.{method_name}().len()"), - app, - ); - } + .. + })) if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => span, + _ => expr.span, + }; + + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + let mut app = Applicability::MachineApplicable; + let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; + let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) { + "as_bytes" + } else if is_type_lang_item(cx, ty, LangItem::CStr) { + "to_bytes" + } else { + return; + }; + + span_lint_and_sugg( + cx, + STRLEN_ON_C_STRINGS, + span, + "using `libc::strlen` on a `CString` or `CStr` value", + "try", + format!("{val_name}.{method_name}().len()"), + app, + ); } } } diff --git a/clippy_lints/src/suspicious_doc_comments.rs b/clippy_lints/src/suspicious_doc_comments.rs index 0abc199da..cbd6e7458 100644 --- a/clippy_lints/src/suspicious_doc_comments.rs +++ b/clippy_lints/src/suspicious_doc_comments.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; -use if_chain::if_chain; use rustc_ast::token::CommentKind; use rustc_ast::{AttrKind, AttrStyle, Attribute, Item}; use rustc_errors::Applicability; @@ -76,19 +75,17 @@ fn collect_doc_comment_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> attrs .iter() .filter_map(|attr| { - if_chain! { - if let AttrKind::DocComment(com_kind, sym) = attr.kind; - if let AttrStyle::Outer = attr.style; - if let Some(com) = sym.as_str().strip_prefix('!'); - then { - let sugg = match com_kind { - CommentKind::Line => format!("//!{com}"), - CommentKind::Block => format!("/*!{com}*/") - }; - Some((attr.span, sugg)) - } else { - None - } + if let AttrKind::DocComment(com_kind, sym) = attr.kind + && let AttrStyle::Outer = attr.style + && let Some(com) = sym.as_str().strip_prefix('!') + { + let sugg = match com_kind { + CommentKind::Line => format!("//!{com}"), + CommentKind::Block => format!("/*!{com}*/"), + }; + Some((attr.span, sugg)) + } else { + None } }) .collect() diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index bb8cde5b9..92de79178 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -2,7 +2,6 @@ use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use core::ops::{Add, AddAssign}; -use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -155,34 +154,22 @@ fn check_binops(cx: &EarlyContext<'_>, binops: &[&BinaryOp<'_>]) { match (no_difference_info, double_difference_info) { (Some(i), None) => attempt_to_emit_no_difference_lint(cx, binops, i, expected_loc), (None, Some((double_difference_index, ident_loc1, ident_loc2))) => { - if_chain! { - if one_ident_difference_count == binop_count - 1; - if let Some(binop) = binops.get(double_difference_index); - then { - let changed_loc = if ident_loc1 == expected_loc { - ident_loc2 - } else if ident_loc2 == expected_loc { - ident_loc1 - } else { - // This expression doesn't match the form we're - // looking for. - return; - }; + if one_ident_difference_count == binop_count - 1 + && let Some(binop) = binops.get(double_difference_index) + { + let changed_loc = if ident_loc1 == expected_loc { + ident_loc2 + } else if ident_loc2 == expected_loc { + ident_loc1 + } else { + // This expression doesn't match the form we're + // looking for. + return; + }; - if let Some(sugg) = ident_swap_sugg( - cx, - &paired_identifiers, - binop, - changed_loc, - &mut applicability, - ) { - emit_suggestion( - cx, - binop.span, - sugg, - applicability, - ); - } + if let Some(sugg) = ident_swap_sugg(cx, &paired_identifiers, binop, changed_loc, &mut applicability) + { + emit_suggestion(cx, binop.span, sugg, applicability); } } }, @@ -212,48 +199,32 @@ fn attempt_to_emit_no_difference_lint( let old_right_ident = get_ident(binop.right, expected_loc); for b in skip_index(binops.iter(), i) { - if_chain! { - if let (Some(old_ident), Some(new_ident)) = - (old_left_ident, get_ident(b.left, expected_loc)); - if old_ident != new_ident; - if let Some(sugg) = suggestion_with_swapped_ident( + if let (Some(old_ident), Some(new_ident)) = (old_left_ident, get_ident(b.left, expected_loc)) + && old_ident != new_ident + && let Some(sugg) = + suggestion_with_swapped_ident(cx, binop.left, expected_loc, new_ident, &mut applicability) + { + emit_suggestion( cx, - binop.left, - expected_loc, - new_ident, - &mut applicability, + binop.span, + replace_left_sugg(cx, binop, &sugg, &mut applicability), + applicability, ); - then { - emit_suggestion( - cx, - binop.span, - replace_left_sugg(cx, binop, &sugg, &mut applicability), - applicability, - ); - return; - } + return; } - if_chain! { - if let (Some(old_ident), Some(new_ident)) = - (old_right_ident, get_ident(b.right, expected_loc)); - if old_ident != new_ident; - if let Some(sugg) = suggestion_with_swapped_ident( + if let (Some(old_ident), Some(new_ident)) = (old_right_ident, get_ident(b.right, expected_loc)) + && old_ident != new_ident + && let Some(sugg) = + suggestion_with_swapped_ident(cx, binop.right, expected_loc, new_ident, &mut applicability) + { + emit_suggestion( cx, - binop.right, - expected_loc, - new_ident, - &mut applicability, + binop.span, + replace_right_sugg(cx, binop, &sugg, &mut applicability), + applicability, ); - then { - emit_suggestion( - cx, - binop.span, - replace_right_sugg(cx, binop, &sugg, &mut applicability), - applicability, - ); - return; - } + return; } } } diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 6271ea027..3244933a1 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::for_each_expr; use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -57,37 +56,39 @@ declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind; - if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node); - if let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang); - if let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang); + if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind + && let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node) + && let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang) + && let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang) // Check for more than one binary operation in the implemented function // Linting when multiple operations are involved can result in false positives - let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id; - if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get_by_def_id(parent_fn); - if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind; - let body = cx.tcx.hir().body(body_id); - let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id; - if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn); - let trait_id = trait_ref.path.res.def_id(); - if ![binop_trait_id, op_assign_trait_id].contains(&trait_id); - if let Some(&(_, lint)) = [ + && let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id + && let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get_by_def_id(parent_fn) + && let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind + && let body = cx.tcx.hir().body(body_id) + && let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id + && let Some(trait_ref) = trait_ref_of_method(cx, parent_fn) + && let trait_id = trait_ref.path.res.def_id() + && ![binop_trait_id, op_assign_trait_id].contains(&trait_id) + && let Some(&(_, lint)) = [ (&BINOP_TRAITS, SUSPICIOUS_ARITHMETIC_IMPL), (&OP_ASSIGN_TRAITS, SUSPICIOUS_OP_ASSIGN_IMPL), ] .iter() - .find(|&(ts, _)| ts.iter().any(|&t| Some(trait_id) == cx.tcx.lang_items().get(t))); - if count_binops(body.value) == 1; - then { - span_lint( - cx, - lint, - binop.span, - &format!("suspicious use of `{}` in `{}` impl", binop.node.as_str(), cx.tcx.item_name(trait_id)), - ); - } + .find(|&(ts, _)| ts.iter().any(|&t| Some(trait_id) == cx.tcx.lang_items().get(t))) + && count_binops(body.value) == 1 + { + span_lint( + cx, + lint, + binop.span, + &format!( + "suspicious use of `{}` in `{}` impl", + binop.node.as_str(), + cx.tcx.item_name(trait_id) + ), + ); } } } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 660e6835e..285f2f4f6 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -3,7 +3,6 @@ use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -149,36 +148,33 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { } for [s1, s2, s3] in block.stmts.array_windows::<3>() { - if_chain! { + if let StmtKind::Local(tmp) = s1.kind // let t = foo(); - if let StmtKind::Local(tmp) = s1.kind; - if let Some(tmp_init) = tmp.init; - if let PatKind::Binding(.., ident, None) = tmp.pat.kind; + && let Some(tmp_init) = tmp.init + && let PatKind::Binding(.., ident, None) = tmp.pat.kind // foo() = bar(); - if let StmtKind::Semi(first) = s2.kind; - if let ExprKind::Assign(lhs1, rhs1, _) = first.kind; + && let StmtKind::Semi(first) = s2.kind + && let ExprKind::Assign(lhs1, rhs1, _) = first.kind // bar() = t; - if let StmtKind::Semi(second) = s3.kind; - if let ExprKind::Assign(lhs2, rhs2, _) = second.kind; - if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind; - if rhs2.segments.len() == 1; + && let StmtKind::Semi(second) = s3.kind + && let ExprKind::Assign(lhs2, rhs2, _) = second.kind + && let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind + && rhs2.segments.len() == 1 - if ident.name == rhs2.segments[0].ident.name; - if eq_expr_value(cx, tmp_init, lhs1); - if eq_expr_value(cx, rhs1, lhs2); + && ident.name == rhs2.segments[0].ident.name + && eq_expr_value(cx, tmp_init, lhs1) + && eq_expr_value(cx, rhs1, lhs2) - let ctxt = s1.span.ctxt(); - if s2.span.ctxt() == ctxt; - if s3.span.ctxt() == ctxt; - if first.span.ctxt() == ctxt; - if second.span.ctxt() == ctxt; - - then { - let span = s1.span.to(s3.span); - generate_swap_warning(cx, lhs1, lhs2, span, false); - } + && let ctxt = s1.span.ctxt() + && s2.span.ctxt() == ctxt + && s3.span.ctxt() == ctxt + && first.span.ctxt() == ctxt + && second.span.ctxt() == ctxt + { + let span = s1.span.to(s3.span); + generate_swap_warning(cx, lhs1, lhs2, span, false); } } } @@ -261,20 +257,18 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr< fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { for [s1, s2, s3] in block.stmts.array_windows::<3>() { let ctxt = s1.span.ctxt(); - if_chain! { - if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt); - if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt); - if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt); - if eq_expr_value(cx, lhs0, rhs1); - if eq_expr_value(cx, lhs2, rhs1); - if eq_expr_value(cx, lhs1, rhs0); - if eq_expr_value(cx, lhs1, rhs2); - if s2.span.ctxt() == ctxt; - if s3.span.ctxt() == ctxt; - then { - let span = s1.span.to(s3.span); - generate_swap_warning(cx, lhs0, rhs0, span, true); - } + if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt) + && let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt) + && let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt) + && eq_expr_value(cx, lhs0, rhs1) + && eq_expr_value(cx, lhs2, rhs1) + && eq_expr_value(cx, lhs1, rhs0) + && eq_expr_value(cx, lhs1, rhs2) + && s2.span.ctxt() == ctxt + && s3.span.ctxt() == ctxt + { + let span = s1.span.to(s3.span); + generate_swap_warning(cx, lhs0, rhs0, span, true); }; } } diff --git a/clippy_lints/src/tests_outside_test_module.rs b/clippy_lints/src/tests_outside_test_module.rs index 0cfb1c125..9481c78a5 100644 --- a/clippy_lints/src/tests_outside_test_module.rs +++ b/clippy_lints/src/tests_outside_test_module.rs @@ -55,20 +55,18 @@ impl LateLintPass<'_> for TestsOutsideTestModule { sp: Span, _: LocalDefId, ) { - if_chain! { - if !matches!(kind, FnKind::Closure); - if is_in_test_function(cx.tcx, body.id().hir_id); - if !is_in_cfg_test(cx.tcx, body.id().hir_id); - then { - span_lint_and_note( - cx, - TESTS_OUTSIDE_TEST_MODULE, - sp, - "this function marked with #[test] is outside a #[cfg(test)] module", - None, - "move it to a testing module marked with #[cfg(test)]", - ); - } + if !matches!(kind, FnKind::Closure) + && is_in_test_function(cx.tcx, body.id().hir_id) + && !is_in_cfg_test(cx.tcx, body.id().hir_id) + { + span_lint_and_note( + cx, + TESTS_OUTSIDE_TEST_MODULE, + sp, + "this function marked with #[test] is outside a #[cfg(test)] module", + None, + "move it to a testing module marked with #[cfg(test)]", + ); } } } diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index a171d225f..1dca523a9 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::match_def_path; use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -38,59 +37,57 @@ declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind; - if is_some_path.ident.name.as_str() == "is_some"; - then { - let match_result = match &to_digit_expr.kind { - hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { - if_chain! { - if to_digits_path.ident.name.as_str() == "to_digit"; - let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg); - if *char_arg_ty.kind() == ty::Char; - then { - Some((true, *char_arg, radix_arg)) - } else { - None - } - } + if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind + && is_some_path.ident.name.as_str() == "is_some" + { + let match_result = match &to_digit_expr.kind { + hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { + if to_digits_path.ident.name.as_str() == "to_digit" + && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg) + && *char_arg_ty.kind() == ty::Char + { + Some((true, *char_arg, radix_arg)) + } else { + None } - hir::ExprKind::Call(to_digits_call, to_digit_args) => { - if_chain! { - if let [char_arg, radix_arg] = *to_digit_args; - if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind; - if let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id); - if let Some(to_digits_def_id) = to_digits_call_res.opt_def_id(); - if match_def_path(cx, to_digits_def_id, &["core", "char", "methods", "", "to_digit"]); - then { - Some((false, char_arg, radix_arg)) - } else { - None - } - } + }, + hir::ExprKind::Call(to_digits_call, to_digit_args) => { + if let [char_arg, radix_arg] = *to_digit_args + && let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind + && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) + && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() + && match_def_path( + cx, + to_digits_def_id, + &["core", "char", "methods", "", "to_digit"], + ) + { + Some((false, char_arg, radix_arg)) + } else { + None } - _ => None - }; + }, + _ => None, + }; - if let Some((is_method_call, char_arg, radix_arg)) = match_result { - let mut applicability = Applicability::MachineApplicable; - let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability); - let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability); + if let Some((is_method_call, char_arg, radix_arg)) = match_result { + let mut applicability = Applicability::MachineApplicable; + let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability); + let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - TO_DIGIT_IS_SOME, - expr.span, - "use of `.to_digit(..).is_some()`", - "try", - if is_method_call { - format!("{char_arg_snip}.is_digit({radix_snip})") - } else { - format!("char::is_digit({char_arg_snip}, {radix_snip})") - }, - applicability, - ); - } + span_lint_and_sugg( + cx, + TO_DIGIT_IS_SOME, + expr.span, + "use of `.to_digit(..).is_some()`", + "try", + if is_method_call { + format!("{char_arg_snip}.is_digit({radix_snip})") + } else { + format!("char::is_digit({char_arg_snip}, {radix_snip})") + }, + applicability, + ); } } } diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index 87181adc2..7eef02b3c 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -54,20 +54,18 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { } fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if_chain! { + if let ItemKind::Struct(data, _) = &item.kind // First check if last field is an array - if let ItemKind::Struct(data, _) = &item.kind; - if let Some(last_field) = data.fields().last(); - if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind; + && let Some(last_field) = data.fields().last() + && let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind // Then check if that array is zero-sized - let length = Const::from_anon_const(cx.tcx, length.def_id); - let length = length.try_eval_target_usize(cx.tcx, cx.param_env); - if let Some(length) = length; - then { - length == 0 - } else { - false - } + && let length = Const::from_anon_const(cx.tcx, length.def_id) + && let length = length.try_eval_target_usize(cx.tcx, cx.param_env) + && let Some(length) = length + { + length == 0 + } else { + false } } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index f065d215e..e624bbe5f 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash}; use core::hash::{Hash, Hasher}; -use if_chain::if_chain; use itertools::Itertools; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unhash::UnhashMap; @@ -124,103 +123,100 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { let mut self_bounds_map = FxHashMap::default(); for predicate in item.generics.predicates { - if_chain! { - if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; - if bound_predicate.origin != PredicateOrigin::ImplTrait; - if !bound_predicate.span.from_expansion(); - if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; - if let Some(PathSegment { - res: Res::SelfTyParam { trait_: def_id }, .. - }) = segments.first(); - if let Some( - Node::Item( - Item { - kind: ItemKind::Trait(_, _, _, self_bounds, _), - .. } - ) - ) = cx.tcx.hir().get_if_local(*def_id); - then { - if self_bounds_map.is_empty() { - for bound in *self_bounds { - let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { continue }; - self_bounds_map.insert(self_res, self_segments); - } + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate + && bound_predicate.origin != PredicateOrigin::ImplTrait + && !bound_predicate.span.from_expansion() + && let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind + && let Some(PathSegment { + res: Res::SelfTyParam { trait_: def_id }, + .. + }) = segments.first() + && let Some(Node::Item(Item { + kind: ItemKind::Trait(_, _, _, self_bounds, _), + .. + })) = cx.tcx.hir().get_if_local(*def_id) + { + if self_bounds_map.is_empty() { + for bound in *self_bounds { + let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { + continue; + }; + self_bounds_map.insert(self_res, self_segments); } - - bound_predicate - .bounds - .iter() - .filter_map(get_trait_info_from_bound) - .for_each(|(trait_item_res, trait_item_segments, span)| { - if let Some(self_segments) = self_bounds_map.get(&trait_item_res) { - if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - span, - "this trait bound is already specified in trait declaration", - None, - "consider removing this trait bound", - ); - } - } - }); } + + bound_predicate + .bounds + .iter() + .filter_map(get_trait_info_from_bound) + .for_each(|(trait_item_res, trait_item_segments, span)| { + if let Some(self_segments) = self_bounds_map.get(&trait_item_res) { + if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in trait declaration", + None, + "consider removing this trait bound", + ); + } + } + }); } } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { - if_chain! { - if let TyKind::Ref(.., mut_ty) = &ty.kind; - if let TyKind::TraitObject(bounds, ..) = mut_ty.ty.kind; - if bounds.len() > 2; - then { + if let TyKind::Ref(.., mut_ty) = &ty.kind + && let TyKind::TraitObject(bounds, ..) = mut_ty.ty.kind + && bounds.len() > 2 + { + // Build up a hash of every trait we've seen + // When we see a trait for the first time, add it to unique_traits + // so we can later use it to build a string of all traits exactly once, without duplicates - // Build up a hash of every trait we've seen - // When we see a trait for the first time, add it to unique_traits - // so we can later use it to build a string of all traits exactly once, without duplicates + let mut seen_def_ids = FxHashSet::default(); + let mut unique_traits = Vec::new(); - let mut seen_def_ids = FxHashSet::default(); - let mut unique_traits = Vec::new(); + // Iterate the bounds and add them to our seen hash + // If we haven't yet seen it, add it to the fixed traits + for bound in bounds { + let Some(def_id) = bound.trait_ref.trait_def_id() else { + continue; + }; - // Iterate the bounds and add them to our seen hash - // If we haven't yet seen it, add it to the fixed traits - for bound in bounds { - let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; }; + let new_trait = seen_def_ids.insert(def_id); - let new_trait = seen_def_ids.insert(def_id); + if new_trait { + unique_traits.push(bound); + } + } - if new_trait { - unique_traits.push(bound); - } + // If the number of unique traits isn't the same as the number of traits in the bounds, + // there must be 1 or more duplicates + if bounds.len() != unique_traits.len() { + let mut bounds_span = bounds[0].span; + + for bound in bounds.iter().skip(1) { + bounds_span = bounds_span.to(bound.span); } - // If the number of unique traits isn't the same as the number of traits in the bounds, - // there must be 1 or more duplicates - if bounds.len() != unique_traits.len() { - let mut bounds_span = bounds[0].span; + let fixed_trait_snippet = unique_traits + .iter() + .filter_map(|b| snippet_opt(cx, b.span)) + .collect::>() + .join(" + "); - for bound in bounds.iter().skip(1) { - bounds_span = bounds_span.to(bound.span); - } - - let fixed_trait_snippet = unique_traits - .iter() - .filter_map(|b| snippet_opt(cx, b.span)) - .collect::>() - .join(" + "); - - span_lint_and_sugg( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - bounds_span, - "this trait bound is already specified in trait declaration", - "try", - fixed_trait_snippet, - Applicability::MaybeIncorrect, - ); - } + span_lint_and_sugg( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + bounds_span, + "this trait bound is already specified in trait declaration", + "try", + fixed_trait_snippet, + Applicability::MaybeIncorrect, + ); } } } @@ -267,36 +263,38 @@ impl TraitBounds { let mut map: UnhashMap, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.predicates { - if_chain! { - if let WherePredicate::BoundPredicate(ref p) = bound; - if p.origin != PredicateOrigin::ImplTrait; - if p.bounds.len() as u64 <= self.max_trait_bounds; - if !p.span.from_expansion(); - let bounds = p.bounds.iter().filter(|b| !self.cannot_combine_maybe_bound(cx, b)).collect::>(); - if !bounds.is_empty(); - if let Some(ref v) = map.insert(SpanlessTy { ty: p.bounded_ty, cx }, bounds); - if !is_from_proc_macro(cx, p.bounded_ty); - then { - let trait_bounds = v - .iter() - .copied() - .chain(p.bounds.iter()) - .filter_map(get_trait_info_from_bound) - .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) - .join(" + "); - let hint_string = format!( - "consider combining the bounds: `{}: {trait_bounds}`", - snippet(cx, p.bounded_ty.span, "_"), - ); - span_lint_and_help( - cx, - TYPE_REPETITION_IN_BOUNDS, - p.span, - "this type has already been used as a bound predicate", - None, - &hint_string, - ); - } + if let WherePredicate::BoundPredicate(ref p) = bound + && p.origin != PredicateOrigin::ImplTrait + && p.bounds.len() as u64 <= self.max_trait_bounds + && !p.span.from_expansion() + && let bounds = p + .bounds + .iter() + .filter(|b| !self.cannot_combine_maybe_bound(cx, b)) + .collect::>() + && !bounds.is_empty() + && let Some(ref v) = map.insert(SpanlessTy { ty: p.bounded_ty, cx }, bounds) + && !is_from_proc_macro(cx, p.bounded_ty) + { + let trait_bounds = v + .iter() + .copied() + .chain(p.bounds.iter()) + .filter_map(get_trait_info_from_bound) + .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) + .join(" + "); + let hint_string = format!( + "consider combining the bounds: `{}: {trait_bounds}`", + snippet(cx, p.bounded_ty.span, "_"), + ); + span_lint_and_help( + cx, + TYPE_REPETITION_IN_BOUNDS, + p.span, + "this type has already been used as a bound predicate", + None, + &hint_string, + ); } } } @@ -318,15 +316,19 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { .predicates .iter() .filter_map(|pred| { - if_chain! { - if pred.in_where_clause(); - if let WherePredicate::BoundPredicate(bound_predicate) = pred; - if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind; - then { - return Some( - rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements") - .into_iter().map(|(trait_ref, _)| (path.res, trait_ref))) - } + if pred.in_where_clause() + && let WherePredicate::BoundPredicate(bound_predicate) = pred + && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind + { + return Some( + rollup_traits( + cx, + bound_predicate.bounds, + "these where clauses contain repeated elements", + ) + .into_iter() + .map(|(trait_ref, _)| (path.res, trait_ref)), + ); } None }) @@ -340,25 +342,23 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { // compare trait bounds keyed by generic name and comparable trait to collected where // predicates eg. (T, Clone) for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) { - if_chain! { - if let WherePredicate::BoundPredicate(bound_predicate) = predicate; - if bound_predicate.origin != PredicateOrigin::ImplTrait; - if !bound_predicate.span.from_expansion(); - if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind; - then { - let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements"); - for (trait_ref, span) in traits { - let key = (path.res, trait_ref); - if where_predicates.contains(&key) { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - span, - "this trait bound is already specified in the where clause", - None, - "consider removing this trait bound", - ); - } + if let WherePredicate::BoundPredicate(bound_predicate) = predicate + && bound_predicate.origin != PredicateOrigin::ImplTrait + && !bound_predicate.span.from_expansion() + && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind + { + let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements"); + for (trait_ref, span) in traits { + let key = (path.res, trait_ref); + if where_predicates.contains(&key) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in the where clause", + None, + "consider removing this trait bound", + ); } } } @@ -401,10 +401,10 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef { .filter_map(|segment| { // get trait bound type arguments Some(segment.args?.args.iter().filter_map(|arg| { - if_chain! { - if let GenericArg::Type(ty) = arg; - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; - then { return Some(path.res) } + if let GenericArg::Type(ty) = arg + && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind + { + return Some(path.res); } None })) @@ -444,27 +444,24 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) - comparable_bounds[i] = (k, v); } - if_chain! { - if repeated_res; - if let [first_trait, .., last_trait] = bounds; - then { - let all_trait_span = first_trait.span().to(last_trait.span()); + if repeated_res && let [first_trait, .., last_trait] = bounds { + let all_trait_span = first_trait.span().to(last_trait.span()); - let traits = comparable_bounds.iter() - .filter_map(|&(_, span)| snippet_opt(cx, span)) - .collect::>(); - let traits = traits.join(" + "); + let traits = comparable_bounds + .iter() + .filter_map(|&(_, span)| snippet_opt(cx, span)) + .collect::>(); + let traits = traits.join(" + "); - span_lint_and_sugg( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - all_trait_span, - msg, - "try", - traits, - Applicability::MachineApplicable - ); - } + span_lint_and_sugg( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + all_trait_span, + msg, + "try", + traits, + Applicability::MachineApplicable, + ); } comparable_bounds diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 6eec40cb5..a3a50acb6 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -19,7 +19,6 @@ mod wrong_transmute; use clippy_config::msrvs::Msrv; use clippy_utils::in_constant; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -494,51 +493,47 @@ impl Transmute { } impl<'tcx> LateLintPass<'tcx> for Transmute { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(path_expr, [arg]) = e.kind; - if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind; - if let Some(def_id) = path.res.opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::transmute, def_id); - then { - // Avoid suggesting non-const operations in const contexts: - // - from/to bits (https://github.com/rust-lang/rust/issues/73736) - // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911) - // - char conversions (https://github.com/rust-lang/rust/issues/89259) - let const_context = in_constant(cx, e.hir_id); + if let ExprKind::Call(path_expr, [arg]) = e.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind + && let Some(def_id) = path.res.opt_def_id() + && cx.tcx.is_diagnostic_item(sym::transmute, def_id) + { + // Avoid suggesting non-const operations in const contexts: + // - from/to bits (https://github.com/rust-lang/rust/issues/73736) + // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911) + // - char conversions (https://github.com/rust-lang/rust/issues/89259) + let const_context = in_constant(cx, e.hir_id); - let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) { - [] => (cx.typeck_results().expr_ty(arg), false), - [.., a] => (a.target, true), - }; - // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them. - let to_ty = cx.typeck_results().expr_ty(e); + let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) { + [] => (cx.typeck_results().expr_ty(arg), false), + [.., a] => (a.target, true), + }; + // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them. + let to_ty = cx.typeck_results().expr_ty(e); - // If useless_transmute is triggered, the other lints can be skipped. - if useless_transmute::check(cx, e, from_ty, to_ty, arg) { - return; - } + // If useless_transmute is triggered, the other lints can be skipped. + if useless_transmute::check(cx, e, from_ty, to_ty, arg) { + return; + } - let linted = wrong_transmute::check(cx, e, from_ty, to_ty) - | crosspointer_transmute::check(cx, e, from_ty, to_ty) - | transmuting_null::check(cx, e, arg, to_ty) - | transmute_null_to_fn::check(cx, e, arg, to_ty) - | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) - | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) - | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) - | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) - | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context) - | ( - unsound_collection_transmute::check(cx, e, from_ty, to_ty) - || transmute_undefined_repr::check(cx, e, from_ty, to_ty) - ); + let linted = wrong_transmute::check(cx, e, from_ty, to_ty) + | crosspointer_transmute::check(cx, e, from_ty, to_ty) + | transmuting_null::check(cx, e, arg, to_ty) + | transmute_null_to_fn::check(cx, e, arg, to_ty) + | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) + | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) + | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) + | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) + | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context) + | (unsound_collection_transmute::check(cx, e, from_ty, to_ty) + || transmute_undefined_repr::check(cx, e, from_ty, to_ty)); - if !linted { - transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg); - } + if !linted { + transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg); } } } diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index 5ecba512b..aef520923 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -1,7 +1,6 @@ use super::TRANSMUTE_FLOAT_TO_INT; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg; -use if_chain::if_chain; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -32,17 +31,15 @@ pub(super) fn check<'tcx>( arg = inner_expr; } - if_chain! { + if let ExprKind::Lit(lit) = &arg.kind // if the expression is a float literal and it is unsuffixed then // add a suffix so the suggestion is valid and unambiguous - if let ExprKind::Lit(lit) = &arg.kind; - if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; - then { - let op = format!("{sugg}{}", float_ty.name_str()).into(); - match sugg { - sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), - _ => sugg = sugg::Sugg::NonParen(op) - } + && let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node + { + let op = format!("{sugg}{}", float_ty.name_str()).into(); + match sugg { + sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), + _ => sugg = sugg::Sugg::NonParen(op), } } diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index ea9ad9961..98e9ea2d7 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -2,7 +2,6 @@ use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -21,68 +20,59 @@ pub(super) fn check<'tcx>( let mut triggered = false; if let (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) = (&from_ty.kind(), &to_ty.kind()) { - if_chain! { - if let ty::Slice(slice_ty) = *ty_from.kind(); - if ty_to.is_str(); - if let ty::Uint(ty::UintTy::U8) = slice_ty.kind(); - if from_mutbl == to_mutbl; - then { - let postfix = if *from_mutbl == Mutability::Mut { - "_mut" + if let ty::Slice(slice_ty) = *ty_from.kind() + && ty_to.is_str() + && let ty::Uint(ty::UintTy::U8) = slice_ty.kind() + && from_mutbl == to_mutbl + { + let postfix = if *from_mutbl == Mutability::Mut { "_mut" } else { "" }; + + let snippet = snippet(cx, arg.span, ".."); + + span_lint_and_sugg( + cx, + TRANSMUTE_BYTES_TO_STR, + e.span, + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), + "consider using", + if const_context { + format!("std::str::from_utf8_unchecked{postfix}({snippet})") } else { - "" - }; + format!("std::str::from_utf8{postfix}({snippet}).unwrap()") + }, + Applicability::MaybeIncorrect, + ); + triggered = true; + } else if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) && !const_context { + span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_PTR, + e.span, + "transmute from a reference to a reference", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { + let ty_from_and_mut = ty::TypeAndMut { + ty: *ty_from, + mutbl: *from_mutbl, + }; + let ty_to_and_mut = ty::TypeAndMut { + ty: *ty_to, + mutbl: *to_mutbl, + }; + let sugg_paren = arg + .as_ty(Ty::new_ptr(cx.tcx, ty_from_and_mut)) + .as_ty(Ty::new_ptr(cx.tcx, ty_to_and_mut)); + let sugg = if *to_mutbl == Mutability::Mut { + sugg_paren.mut_addr_deref() + } else { + sugg_paren.addr_deref() + }; + diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); + } + }, + ); - let snippet = snippet(cx, arg.span, ".."); - - span_lint_and_sugg( - cx, - TRANSMUTE_BYTES_TO_STR, - e.span, - &format!("transmute from a `{from_ty}` to a `{to_ty}`"), - "consider using", - if const_context { - format!("std::str::from_utf8_unchecked{postfix}({snippet})") - } else { - format!("std::str::from_utf8{postfix}({snippet}).unwrap()") - }, - Applicability::MaybeIncorrect, - ); - triggered = true; - } else { - if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) - && !const_context { - span_lint_and_then( - cx, - TRANSMUTE_PTR_TO_PTR, - e.span, - "transmute from a reference to a reference", - |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let ty_from_and_mut = ty::TypeAndMut { - ty: *ty_from, - mutbl: *from_mutbl - }; - let ty_to_and_mut = ty::TypeAndMut { ty: *ty_to, mutbl: *to_mutbl }; - let sugg_paren = arg - .as_ty(Ty::new_ptr(cx.tcx,ty_from_and_mut)) - .as_ty(Ty::new_ptr(cx.tcx,ty_to_and_mut)); - let sugg = if *to_mutbl == Mutability::Mut { - sugg_paren.mut_addr_deref() - } else { - sugg_paren.addr_deref() - }; - diag.span_suggestion( - e.span, - "try", - sugg, - Applicability::Unspecified, - ); - }, - ); - - triggered = true; - } - } + triggered = true; } } diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 7c2223ca3..a65bc0ce4 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -299,14 +299,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> } fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - if_chain! { - if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty); - if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); - then { - layout.layout.size().bytes() == 0 - } else { - false - } + if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) + && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) + { + layout.layout.size().bytes() == 0 + } else { + false } } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 306ca5724..801e88626 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath, TyKind, @@ -15,66 +14,64 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m TyKind::Path(ref qpath) => { let hir_id = mut_ty.ty.hir_id; let def = cx.qpath_res(qpath, hir_id); - if_chain! { - if let Some(def_id) = def.opt_def_id(); - if Some(def_id) == cx.tcx.lang_items().owned_box(); - if let QPath::Resolved(None, path) = *qpath; - if let [ref bx] = *path.segments; - if let Some(params) = bx.args; - if params.parenthesized == hir::GenericArgsParentheses::No; - if let Some(inner) = params.args.iter().find_map(|arg| match arg { + if let Some(def_id) = def.opt_def_id() + && Some(def_id) == cx.tcx.lang_items().owned_box() + && let QPath::Resolved(None, path) = *qpath + && let [ref bx] = *path.segments + && let Some(params) = bx.args + && params.parenthesized == hir::GenericArgsParentheses::No + && let Some(inner) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, - }); - then { - if is_any_trait(cx, inner) { - // Ignore `Box` types; see issue #1884 for details. - return false; - } - - let ltopt = if lt.is_anonymous() { - String::new() - } else { - format!("{} ", lt.ident.as_str()) - }; - - if mut_ty.mutbl == Mutability::Mut { - // Ignore `&mut Box` types; see issue #2907 for - // details. - return false; - } - - // When trait objects or opaque types have lifetime or auto-trait bounds, - // we need to add parentheses to avoid a syntax error due to its ambiguity. - // Originally reported as the issue #3128. - let inner_snippet = snippet(cx, inner.span, ".."); - let suggestion = match &inner.kind { - TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { - format!("&{ltopt}({})", &inner_snippet) - }, - TyKind::Path(qpath) - if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) - .map_or(false, |bounds| bounds.len() > 1) => - { - format!("&{ltopt}({})", &inner_snippet) - }, - _ => format!("&{ltopt}{}", &inner_snippet), - }; - span_lint_and_sugg( - cx, - BORROWED_BOX, - hir_ty.span, - "you seem to be trying to use `&Box`. Consider using just `&T`", - "try", - suggestion, - // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item - // because the trait impls of it will break otherwise; - // and there may be other cases that result in invalid code. - // For example, type coercion doesn't work nicely. - Applicability::Unspecified, - ); - return true; + }) + { + if is_any_trait(cx, inner) { + // Ignore `Box` types; see issue #1884 for details. + return false; } + + let ltopt = if lt.is_anonymous() { + String::new() + } else { + format!("{} ", lt.ident.as_str()) + }; + + if mut_ty.mutbl == Mutability::Mut { + // Ignore `&mut Box` types; see issue #2907 for + // details. + return false; + } + + // When trait objects or opaque types have lifetime or auto-trait bounds, + // we need to add parentheses to avoid a syntax error due to its ambiguity. + // Originally reported as the issue #3128. + let inner_snippet = snippet(cx, inner.span, ".."); + let suggestion = match &inner.kind { + TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { + format!("&{ltopt}({})", &inner_snippet) + }, + TyKind::Path(qpath) + if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) + .map_or(false, |bounds| bounds.len() > 1) => + { + format!("&{ltopt}({})", &inner_snippet) + }, + _ => format!("&{ltopt}{}", &inner_snippet), + }; + span_lint_and_sugg( + cx, + BORROWED_BOX, + hir_ty.span, + "you seem to be trying to use `&Box`. Consider using just `&T`", + "try", + suggestion, + // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item + // because the trait impls of it will break otherwise; + // and there may be other cases that result in invalid code. + // For example, type coercion doesn't work nicely. + Applicability::Unspecified, + ); + return true; }; false }, @@ -84,33 +81,29 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Returns true if given type is `Any` trait. fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { - if_chain! { - if let TyKind::TraitObject(traits, ..) = t.kind; - if !traits.is_empty(); - if let Some(trait_did) = traits[0].trait_ref.trait_def_id(); + if let TyKind::TraitObject(traits, ..) = t.kind + && !traits.is_empty() + && let Some(trait_did) = traits[0].trait_ref.trait_def_id() // Only Send/Sync can be used as additional traits, so it is enough to // check only the first trait. - if cx.tcx.is_diagnostic_item(sym::Any, trait_did); - then { - return true; - } + && cx.tcx.is_diagnostic_item(sym::Any, trait_did) + { + return true; } false } fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option> { - if_chain! { - if let Some(did) = cx.qpath_res(qpath, id).opt_def_id(); - if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); - if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; - if synthetic; - if let Some(generics) = cx.tcx.hir().get_generics(id.owner.def_id); - if let Some(pred) = generics.bounds_for_param(did.expect_local()).next(); - then { - Some(pred.bounds) - } else { - None - } + if let Some(did) = cx.qpath_res(qpath, id).opt_def_id() + && let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did) + && let GenericParamKind::Type { synthetic, .. } = generic_param.kind + && synthetic + && let Some(generics) = cx.tcx.hir().get_generics(id.owner.def_id) + && let Some(pred) = generics.bounds_for_param(did.expect_local()).next() + { + Some(pred.bounds) + } else { + None } } diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 4a5a94f26..fc3420af0 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -8,30 +8,26 @@ use rustc_span::{sym, Symbol}; use super::BOX_COLLECTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - if_chain! { - if Some(def_id) == cx.tcx.lang_items().owned_box(); - if let Some(item_type) = get_std_collection(cx, qpath); - then { - let generic = match item_type { - sym::String => "", - _ => "<..>", - }; + if Some(def_id) == cx.tcx.lang_items().owned_box() + && let Some(item_type) = get_std_collection(cx, qpath) + { + let generic = match item_type { + sym::String => "", + _ => "<..>", + }; - let box_content = format!("{item_type}{generic}"); - span_lint_and_help( - cx, - BOX_COLLECTION, - hir_ty.span, - &format!( - "you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"), - None, - &format!( - "`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation") - ); - true - } else { - false - } + let box_content = format!("{item_type}{generic}"); + span_lint_and_help( + cx, + BOX_COLLECTION, + hir_ty.span, + &format!("you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"), + None, + &format!("`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation"), + ); + true + } else { + false } } diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs index 60622903a..d12d14f2b 100644 --- a/clippy_lints/src/types/option_option.rs +++ b/clippy_lints/src/types/option_option.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::{path_def_id, qpath_generic_tys}; -use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -9,21 +8,19 @@ use rustc_span::symbol::sym; use super::OPTION_OPTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - if_chain! { - if cx.tcx.is_diagnostic_item(sym::Option, def_id); - if let Some(arg) = qpath_generic_tys(qpath).next(); - if path_def_id(cx, arg) == Some(def_id); - then { - span_lint( - cx, - OPTION_OPTION, - hir_ty.span, - "consider using `Option` instead of `Option>` or a custom \ - enum if you need to distinguish all 3 cases", - ); - true - } else { - false - } + if cx.tcx.is_diagnostic_item(sym::Option, def_id) + && let Some(arg) = qpath_generic_tys(qpath).next() + && path_def_id(cx, arg) == Some(def_id) + { + span_lint( + cx, + OPTION_OPTION, + hir_ty.span, + "consider using `Option` instead of `Option>` or a custom \ + enum if you need to distinguish all 3 cases", + ); + true + } else { + false } } diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index a616c3e4e..afc319217 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{path_def_id, qpath_generic_tys}; -use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -9,22 +8,20 @@ use rustc_span::symbol::sym; use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - if_chain! { - if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ; - if let Some(arg) = qpath_generic_tys(qpath).next(); - if let Some(id) = path_def_id(cx, arg); - if cx.tcx.is_diagnostic_item(sym::Mutex, id); - then { - span_lint_and_help( - cx, - RC_MUTEX, - hir_ty.span, - "usage of `Rc>`", - None, - "consider using `Rc>` or `Arc>` instead", - ); - return true; - } + if cx.tcx.is_diagnostic_item(sym::Rc, def_id) + && let Some(arg) = qpath_generic_tys(qpath).next() + && let Some(id) = path_def_id(cx, arg) + && cx.tcx.is_diagnostic_item(sym::Mutex, id) + { + span_lint_and_help( + cx, + RC_MUTEX, + hir_ty.span, + "usage of `Rc>`", + None, + "consider using `Rc>` or `Arc>` instead", + ); + return true; } false diff --git a/clippy_lints/src/types/utils.rs b/clippy_lints/src/types/utils.rs index a30748db8..b1aa1ab45 100644 --- a/clippy_lints/src/types/utils.rs +++ b/clippy_lints/src/types/utils.rs @@ -1,22 +1,19 @@ use clippy_utils::last_path_segment; -use if_chain::if_chain; use rustc_hir::{GenericArg, GenericArgsParentheses, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::source_map::Span; pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let last = last_path_segment(qpath); - if_chain! { - if let Some(params) = last.args; - if params.parenthesized == GenericArgsParentheses::No; - if let Some(ty) = params.args.iter().find_map(|arg| match arg { + if let Some(params) = last.args + && params.parenthesized == GenericArgsParentheses::No + && let Some(ty) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, - }); - if let TyKind::Ref(..) = ty.kind; - then { - return Some(ty.span); - } + }) + && let TyKind::Ref(..) = ty.kind + { + return Some(ty.span); } None } diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 72569e10f..a71194345 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -83,41 +83,39 @@ fn handle_uninit_vec_pair<'tcx>( maybe_init_or_reserve: &'tcx Stmt<'tcx>, maybe_set_len: &'tcx Expr<'tcx>, ) { - if_chain! { - if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve); - if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len); - if vec.location.eq_expr(cx, set_len_self); - if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind(); - if let ty::Adt(_, args) = vec_ty.kind(); + if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve) + && let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len) + && vec.location.eq_expr(cx, set_len_self) + && let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind() + && let ty::Adt(_, args) = vec_ty.kind() // `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()` - if !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id); - then { - if vec.has_capacity() { - // with_capacity / reserve -> set_len + && !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id) + { + if vec.has_capacity() { + // with_capacity / reserve -> set_len - // Check T of Vec - if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) { - // FIXME: #7698, false positive of the internal lints - #[expect(clippy::collapsible_span_lint_calls)] - span_lint_and_then( - cx, - UNINIT_VEC, - vec![call_span, maybe_init_or_reserve.span], - "calling `set_len()` immediately after reserving a buffer creates uninitialized values", - |diag| { - diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); - }, - ); - } - } else { - // new / default -> set_len - span_lint( + // Check T of Vec + if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) { + // FIXME: #7698, false positive of the internal lints + #[expect(clippy::collapsible_span_lint_calls)] + span_lint_and_then( cx, UNINIT_VEC, vec![call_span, maybe_init_or_reserve.span], - "calling `set_len()` on empty `Vec` creates out-of-bound values", + "calling `set_len()` immediately after reserving a buffer creates uninitialized values", + |diag| { + diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); + }, ); } + } else { + // new / default -> set_len + span_lint( + cx, + UNINIT_VEC, + vec![call_span, maybe_init_or_reserve.span], + "calling `set_len()` on empty `Vec` creates out-of-bound values", + ); } } } @@ -156,16 +154,14 @@ impl<'tcx> VecLocation<'tcx> { fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option> { match stmt.kind { StmtKind::Local(local) => { - if_chain! { - if let Some(init_expr) = local.init; - if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind; - if let Some(init_kind) = get_vec_init_kind(cx, init_expr); - then { - return Some(TargetVec { - location: VecLocation::Local(hir_id), - init_kind: Some(init_kind), - }) - } + if let Some(init_expr) = local.init + && let PatKind::Binding(_, hir_id, _, None) = local.pat.kind + && let Some(init_kind) = get_vec_init_kind(cx, init_expr) + { + return Some(TargetVec { + location: VecLocation::Local(hir_id), + init_kind: Some(init_kind), + }); } }, StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind { diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index e76cc65fd..385f8255a 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -44,14 +43,12 @@ fn get_trait_predicates_for_trait_id<'tcx>( ) -> Vec> { let mut preds = Vec::new(); for (pred, _) in generics.predicates { - if_chain! { - if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder(); - let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred)); - if let Some(trait_def_id) = trait_id; - if trait_def_id == trait_pred.trait_ref.def_id; - then { - preds.push(trait_pred); - } + if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder() + && let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred)) + && let Some(trait_def_id) = trait_id + && trait_def_id == trait_pred.trait_ref.def_id + { + preds.push(trait_pred); } } preds @@ -94,21 +91,19 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve .enumerate() .for_each(|(i, inp)| { for trait_pred in &fn_mut_preds { - if_chain! { - if trait_pred.self_ty() == inp; - if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); - then { - if ord_preds - .iter() - .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) - { - args_to_check.push((i, "Ord".to_string())); - } else if partial_ord_preds - .iter() - .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) - { - args_to_check.push((i, "PartialOrd".to_string())); - } + if trait_pred.self_ty() == inp + && let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred) + { + if ord_preds + .iter() + .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) + { + args_to_check.push((i, "Ord".to_string())); + } else if partial_ord_preds + .iter() + .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) + { + args_to_check.push((i, "PartialOrd".to_string())); } } } @@ -118,30 +113,26 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve } fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option)> { - if_chain! { - if let ExprKind::Closure(&Closure { body, fn_decl_span, .. }) = arg.kind; - if let ty::Closure(_def_id, args) = &cx.typeck_results().node_type(arg.hir_id).kind(); - let ret_ty = args.as_closure().sig().output(); - let ty = cx.tcx.erase_late_bound_regions(ret_ty); - if ty.is_unit(); - then { - let body = cx.tcx.hir().body(body); - if_chain! { - if let ExprKind::Block(block, _) = body.value.kind; - if block.expr.is_none(); - if let Some(stmt) = block.stmts.last(); - if let StmtKind::Semi(_) = stmt.kind; - then { - let data = stmt.span.data(); - // Make a span out of the semicolon for the help message - Some((fn_decl_span, Some(data.with_lo(data.hi-BytePos(1))))) - } else { - Some((fn_decl_span, None)) - } - } + if let ExprKind::Closure(&Closure { body, fn_decl_span, .. }) = arg.kind + && let ty::Closure(_def_id, args) = &cx.typeck_results().node_type(arg.hir_id).kind() + && let ret_ty = args.as_closure().sig().output() + && let ty = cx.tcx.erase_late_bound_regions(ret_ty) + && ty.is_unit() + { + let body = cx.tcx.hir().body(body); + if let ExprKind::Block(block, _) = body.value.kind + && block.expr.is_none() + && let Some(stmt) = block.stmts.last() + && let StmtKind::Semi(_) = stmt.kind + { + let data = stmt.span.data(); + // Make a span out of the semicolon for the help message + Some((fn_decl_span, Some(data.with_lo(data.hi - BytePos(1))))) } else { - None + Some((fn_decl_span, None)) } + } else { + None } } diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index 462b1aa81..44cff78a7 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{self as hir, Block, Expr, ExprKind, MatchSource, Node, StmtKind}; use rustc_lint::LateContext; @@ -22,12 +21,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { } let map = &cx.tcx.hir(); let opt_parent_node = map.find_parent(expr.hir_id); - if_chain! { - if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; - if is_questionmark_desugar_marked_call(parent_expr); - then { - return; - } + if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node + && is_questionmark_desugar_marked_call(parent_expr) + { + return; } let args: Vec<_> = match expr.kind { @@ -80,21 +77,15 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp args_to_recover .iter() .filter_map(|arg| { - if_chain! { - if let ExprKind::Block(block, _) = arg.kind; - if block.expr.is_none(); - if let Some(last_stmt) = block.stmts.iter().last(); - if let StmtKind::Semi(last_expr) = last_stmt.kind; - if let Some(snip) = snippet_opt(cx, last_expr.span); - then { - Some(( - last_stmt.span, - snip, - )) - } - else { - None - } + if let ExprKind::Block(block, _) = arg.kind + && block.expr.is_none() + && let Some(last_stmt) = block.stmts.iter().last() + && let StmtKind::Semi(last_expr) = last_stmt.kind + && let Some(snip) = snippet_opt(cx, last_expr.span) + { + Some((last_stmt.span, snip)) + } else { + None } }) .for_each(|(span, sugg)| { diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index e7355f923..2223cbcb0 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -76,55 +75,50 @@ impl LateLintPass<'_> for UnnamedAddress { matches!(cx.typeck_results().expr_ty(expr).kind(), ty::FnDef(..)) } - if_chain! { - if let ExprKind::Binary(binop, left, right) = expr.kind; - if is_comparison(binop.node); - if is_trait_ptr(cx, left) && is_trait_ptr(cx, right); - then { - span_lint_and_help( - cx, - VTABLE_ADDRESS_COMPARISONS, - expr.span, - "comparing trait object pointers compares a non-unique vtable address", - None, - "consider extracting and comparing data pointers only", - ); - } + if let ExprKind::Binary(binop, left, right) = expr.kind + && is_comparison(binop.node) + && is_trait_ptr(cx, left) + && is_trait_ptr(cx, right) + { + span_lint_and_help( + cx, + VTABLE_ADDRESS_COMPARISONS, + expr.span, + "comparing trait object pointers compares a non-unique vtable address", + None, + "consider extracting and comparing data pointers only", + ); } - if_chain! { - if let ExprKind::Call(func, [ref _left, ref _right]) = expr.kind; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::ptr_eq, def_id); - let ty_param = cx.typeck_results().node_args(func.hir_id).type_at(0); - if ty_param.is_trait(); - then { - span_lint_and_help( - cx, - VTABLE_ADDRESS_COMPARISONS, - expr.span, - "comparing trait object pointers compares a non-unique vtable address", - None, - "consider extracting and comparing data pointers only", - ); - } + if let ExprKind::Call(func, [ref _left, ref _right]) = expr.kind + && let ExprKind::Path(ref func_qpath) = func.kind + && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::ptr_eq, def_id) + && let ty_param = cx.typeck_results().node_args(func.hir_id).type_at(0) + && ty_param.is_trait() + { + span_lint_and_help( + cx, + VTABLE_ADDRESS_COMPARISONS, + expr.span, + "comparing trait object pointers compares a non-unique vtable address", + None, + "consider extracting and comparing data pointers only", + ); } - if_chain! { - if let ExprKind::Binary(binop, left, right) = expr.kind; - if is_comparison(binop.node); - if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr(); - if cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr(); - if is_fn_def(cx, left) || is_fn_def(cx, right); - then { - span_lint( - cx, - FN_ADDRESS_COMPARISONS, - expr.span, - "comparing with a non-unique address of a function item", - ); - } + if let ExprKind::Binary(binop, left, right) = expr.kind + && is_comparison(binop.node) + && cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr() + && cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr() + && (is_fn_def(cx, left) || is_fn_def(cx, right)) + { + span_lint( + cx, + FN_ADDRESS_COMPARISONS, + expr.span, + "comparing with a non-unique address of a function item", + ); } } } diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 28ea02e4d..14694bb3a 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -36,46 +35,40 @@ declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRI impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind; - if let ExprKind::Call(fun, args) = inner_expr.kind; - if let ExprKind::Path(ref qpath) = fun.kind; - if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); - if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); - if inner_str.is_str(); - then { - if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { - span_lint_and_sugg( - cx, - UNNECESSARY_OWNED_EMPTY_STRINGS, - expr.span, - "usage of `&String::new()` for a function expecting a `&str` argument", - "try", - "\"\"".to_owned(), - Applicability::MachineApplicable, - ); - } else { - if_chain! { - if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id); - if let [.., last_arg] = args; - if let ExprKind::Lit(spanned) = &last_arg.kind; - if let LitKind::Str(symbol, _) = spanned.node; - if symbol.is_empty(); - let inner_expr_type = cx.typeck_results().expr_ty(inner_expr); - if is_type_lang_item(cx, inner_expr_type, LangItem::String); - then { - span_lint_and_sugg( - cx, - UNNECESSARY_OWNED_EMPTY_STRINGS, - expr.span, - "usage of `&String::from(\"\")` for a function expecting a `&str` argument", - "try", - "\"\"".to_owned(), - Applicability::MachineApplicable, - ); - } - } - } + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind + && let ExprKind::Call(fun, args) = inner_expr.kind + && let ExprKind::Path(ref qpath) = fun.kind + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind() + && inner_str.is_str() + { + if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::new()` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id) + && let [.., last_arg] = args + && let ExprKind::Lit(spanned) = &last_arg.kind + && let LitKind::Str(symbol, _) = spanned.node + && symbol.is_empty() + && let inner_expr_type = cx.typeck_results().expr_ty(inner_expr) + && is_type_lang_item(cx, inner_expr_type, LangItem::String) + { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::from(\"\")` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs index a1083a0a6..1e2b20469 100644 --- a/clippy_lints/src/unnecessary_self_imports.rs +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use if_chain::if_chain; use rustc_ast::{Item, ItemKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -36,35 +35,36 @@ declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]); impl EarlyLintPass for UnnecessarySelfImports { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if_chain! { - if let ItemKind::Use(use_tree) = &item.kind; - if let UseTreeKind::Nested(nodes) = &use_tree.kind; - if let [(self_tree, _)] = &**nodes; - if let [self_seg] = &*self_tree.prefix.segments; - if self_seg.ident.name == kw::SelfLower; - if let Some(last_segment) = use_tree.prefix.segments.last(); - - then { - span_lint_and_then( - cx, - UNNECESSARY_SELF_IMPORTS, - item.span, - "import ending with `::{self}`", - |diag| { - diag.span_suggestion( - last_segment.span().with_hi(item.span.hi()), - "consider omitting `::{self}`", - format!( - "{}{};", - last_segment.ident, - if let UseTreeKind::Simple(Some(alias)) = self_tree.kind { format!(" as {alias}") } else { String::new() }, - ), - Applicability::MaybeIncorrect, - ); - diag.note("this will slightly change semantics; any non-module items at the same path will also be imported"); - }, - ); - } + if let ItemKind::Use(use_tree) = &item.kind + && let UseTreeKind::Nested(nodes) = &use_tree.kind + && let [(self_tree, _)] = &**nodes + && let [self_seg] = &*self_tree.prefix.segments + && self_seg.ident.name == kw::SelfLower + && let Some(last_segment) = use_tree.prefix.segments.last() + { + span_lint_and_then( + cx, + UNNECESSARY_SELF_IMPORTS, + item.span, + "import ending with `::{self}`", + |diag| { + diag.span_suggestion( + last_segment.span().with_hi(item.span.hi()), + "consider omitting `::{self}`", + format!( + "{}{};", + last_segment.ident, + if let UseTreeKind::Simple(Some(alias)) = self_tree.kind { + format!(" as {alias}") + } else { + String::new() + }, + ), + Applicability::MaybeIncorrect, + ); + diag.note("this will slightly change semantics; any non-module items at the same path will also be imported"); + }, + ); } } } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index ab8de17b0..5599a9dc4 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::LangItem::{OptionSome, ResultOk}; @@ -119,28 +118,24 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Check if all return expression respect the following condition and collect them. let mut suggs = Vec::new(); let can_sugg = find_all_ret_expressions(cx, body.value, |ret_expr| { - if_chain! { - if !ret_expr.span.from_expansion(); + if !ret_expr.span.from_expansion() // Check if a function call. - if let ExprKind::Call(func, [arg]) = ret_expr.kind; - if is_res_lang_ctor(cx, path_res(cx, func), lang_item); + && let ExprKind::Call(func, [arg]) = ret_expr.kind + && is_res_lang_ctor(cx, path_res(cx, func), lang_item) // Make sure the function argument does not contain a return expression. - if !contains_return(arg); - then { - suggs.push( - ( - ret_expr.span, - if inner_type.is_unit() { - String::new() - } else { - snippet(cx, arg.span.source_callsite(), "..").to_string() - } - ) - ); - true - } else { - false - } + && !contains_return(arg) + { + suggs.push(( + ret_expr.span, + if inner_type.is_unit() { + String::new() + } else { + snippet(cx, arg.span.source_callsite(), "..").to_string() + }, + )); + true + } else { + false } }); diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index f864c5203..532207310 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::visitors::is_local_used; -use if_chain::if_chain; use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -73,25 +72,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { }) .is_some() }; - if_chain! { - if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind; - if assoc_item.fn_has_self_parameter; - if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; - if !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) || !self.avoid_breaking_exported_api; - let body = cx.tcx.hir().body(*body_id); - if let [self_param, ..] = body.params; - if !is_local_used(cx, body, self_param.pat.hir_id); - if !contains_todo(cx, body); - then { - span_lint_and_help( - cx, - UNUSED_SELF, - self_param.span, - "unused `self` argument", - None, - "consider refactoring to an associated function", - ); - } + if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind + && assoc_item.fn_has_self_parameter + && let ImplItemKind::Fn(.., body_id) = &impl_item.kind + && (!cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) || !self.avoid_breaking_exported_api) + && let body = cx.tcx.hir().body(*body_id) + && let [self_param, ..] = body.params + && !is_local_used(cx, body, self_param.pat.hir_id) + && !contains_todo(cx, body) + { + span_lint_and_help( + cx, + UNUSED_SELF, + self_param.span, + "unused `self` argument", + None, + "consider refactoring to an associated function", + ); } } } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index ae844673b..633040f26 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{position_before_rarrow, snippet_opt}; -use if_chain::if_chain; use rustc_ast::visit::FnKind; use rustc_ast::{ast, ClosureBinder}; use rustc_errors::Applicability; @@ -38,40 +37,39 @@ declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); impl EarlyLintPass for UnusedUnit { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if_chain! { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; - if let ast::TyKind::Tup(ref vals) = ty.kind; - if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); - then { - // implicit types in closure signatures are forbidden when `for<...>` is present - if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { - return; - } - - lint_unneeded_unit_return(cx, ty, span); + if let ast::FnRetTy::Ty(ref ty) = kind.decl().output + && let ast::TyKind::Tup(ref vals) = ty.kind + && vals.is_empty() + && !ty.span.from_expansion() + && get_def(span) == get_def(ty.span) + { + // implicit types in closure signatures are forbidden when `for<...>` is present + if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + return; } + + lint_unneeded_unit_return(cx, ty, span); } } fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if_chain! { - if let Some(stmt) = block.stmts.last(); - if let ast::StmtKind::Expr(ref expr) = stmt.kind; - if is_unit_expr(expr); - let ctxt = block.span.ctxt(); - if stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt; - then { - let sp = expr.span; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - sp, - "unneeded unit expression", - "remove the final `()`", - String::new(), - Applicability::MachineApplicable, - ); - } + if let Some(stmt) = block.stmts.last() + && let ast::StmtKind::Expr(ref expr) = stmt.kind + && is_unit_expr(expr) + && let ctxt = block.span.ctxt() + && stmt.span.ctxt() == ctxt + && expr.span.ctxt() == ctxt + { + let sp = expr.span; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + sp, + "unneeded unit expression", + "remove the final `()`", + String::new(), + Applicability::MachineApplicable, + ); } } @@ -97,16 +95,14 @@ impl EarlyLintPass for UnusedUnit { fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef) { let segments = &poly.trait_ref.path.segments; - if_chain! { - if segments.len() == 1; - if ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()); - if let Some(args) = &segments[0].args; - if let ast::GenericArgs::Parenthesized(generic_args) = &**args; - if let ast::FnRetTy::Ty(ty) = &generic_args.output; - if ty.kind.is_unit(); - then { - lint_unneeded_unit_return(cx, ty, generic_args.span); - } + if segments.len() == 1 + && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) + && let Some(args) = &segments[0].args + && let ast::GenericArgs::Parenthesized(generic_args) = &**args + && let ast::FnRetTy::Ty(ty) = &generic_args.output + && ty.kind.is_unit() + { + lint_unneeded_unit_return(cx, ty, generic_args.span); } } } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index e52cfb29f..5e5b1ab38 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{higher, path_to_local}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; @@ -156,41 +155,35 @@ fn collect_unwrap_info<'tcx>( } } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind { return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false); - } else { - if_chain! { - if let ExprKind::MethodCall(method_name, receiver, args, _) = &expr.kind; - if let Some(local_id) = path_to_local(receiver); - let ty = cx.typeck_results().expr_ty(receiver); - let name = method_name.ident.as_str(); - if is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name); - then { - assert!(args.is_empty()); - let unwrappable = match name { - "is_some" | "is_ok" => true, - "is_err" | "is_none" => false, - _ => unreachable!(), - }; - let safe_to_unwrap = unwrappable != invert; - let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { - UnwrappableKind::Option - } else { - UnwrappableKind::Result - }; + } else if let ExprKind::MethodCall(method_name, receiver, args, _) = &expr.kind + && let Some(local_id) = path_to_local(receiver) + && let ty = cx.typeck_results().expr_ty(receiver) + && let name = method_name.ident.as_str() + && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) + { + assert!(args.is_empty()); + let unwrappable = match name { + "is_some" | "is_ok" => true, + "is_err" | "is_none" => false, + _ => unreachable!(), + }; + let safe_to_unwrap = unwrappable != invert; + let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { + UnwrappableKind::Option + } else { + UnwrappableKind::Result + }; - return vec![ - UnwrapInfo { - local_id, - if_expr, - check: expr, - check_name: method_name, - branch, - safe_to_unwrap, - kind, - is_entire_condition, - } - ] - } - } + return vec![UnwrapInfo { + local_id, + if_expr, + check: expr, + check_name: method_name, + branch, + safe_to_unwrap, + kind, + is_entire_condition, + }]; } Vec::new() } @@ -320,73 +313,72 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { } } else { // find `unwrap[_err]()` calls: - if_chain! { - if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind; - let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg); - if let Some(id) = path_to_local(self_arg); - if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name); - let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name); - if let Some(unwrappable) = self.unwrappables.iter() - .find(|u| u.local_id == id); + if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind + && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg) + && let Some(id) = path_to_local(self_arg) + && [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name) + && let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name) + && let Some(unwrappable) = self.unwrappables.iter() + .find(|u| u.local_id == id) // Span contexts should not differ with the conditional branch - let span_ctxt = expr.span.ctxt(); - if unwrappable.branch.span.ctxt() == span_ctxt; - if unwrappable.check.span.ctxt() == span_ctxt; - then { - if call_to_unwrap == unwrappable.safe_to_unwrap { - let is_entire_condition = unwrappable.is_entire_condition; - let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id); - let suggested_pattern = if call_to_unwrap { - unwrappable.kind.success_variant_pattern() - } else { - unwrappable.kind.error_variant_pattern() - }; - - span_lint_hir_and_then( - self.cx, - UNNECESSARY_UNWRAP, - expr.hir_id, - expr.span, - &format!( - "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", - method_name.ident.name, - unwrappable.check_name.ident.as_str(), - ), - |diag| { - if is_entire_condition { - diag.span_suggestion( - unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), - "try", - format!( - "if let {suggested_pattern} = {borrow_prefix}{unwrappable_variable_name}", - borrow_prefix = match as_ref_kind { - Some(AsRefKind::AsRef) => "&", - Some(AsRefKind::AsMut) => "&mut ", - None => "", - }, - ), - // We don't track how the unwrapped value is used inside the - // block or suggest deleting the unwrap, so we can't offer a - // fixable solution. - Applicability::Unspecified, - ); - } else { - diag.span_label(unwrappable.check.span, "the check is happening here"); - diag.help("try using `if let` or `match`"); - } - }, - ); + && let span_ctxt = expr.span.ctxt() + && unwrappable.branch.span.ctxt() == span_ctxt + && unwrappable.check.span.ctxt() == span_ctxt + { + if call_to_unwrap == unwrappable.safe_to_unwrap { + let is_entire_condition = unwrappable.is_entire_condition; + let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id); + let suggested_pattern = if call_to_unwrap { + unwrappable.kind.success_variant_pattern() } else { - span_lint_hir_and_then( - self.cx, - PANICKING_UNWRAP, - expr.hir_id, - expr.span, - &format!("this call to `{}()` will always panic", - method_name.ident.name), - |diag| { diag.span_label(unwrappable.check.span, "because of this check"); }, - ); - } + unwrappable.kind.error_variant_pattern() + }; + + span_lint_hir_and_then( + self.cx, + UNNECESSARY_UNWRAP, + expr.hir_id, + expr.span, + &format!( + "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", + method_name.ident.name, + unwrappable.check_name.ident.as_str(), + ), + |diag| { + if is_entire_condition { + diag.span_suggestion( + unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), + "try", + format!( + "if let {suggested_pattern} = {borrow_prefix}{unwrappable_variable_name}", + borrow_prefix = match as_ref_kind { + Some(AsRefKind::AsRef) => "&", + Some(AsRefKind::AsMut) => "&mut ", + None => "", + }, + ), + // We don't track how the unwrapped value is used inside the + // block or suggest deleting the unwrap, so we can't offer a + // fixable solution. + Applicability::Unspecified, + ); + } else { + diag.span_label(unwrappable.check.span, "the check is happening here"); + diag.help("try using `if let` or `match`"); + } + }, + ); + } else { + span_lint_hir_and_then( + self.cx, + PANICKING_UNWRAP, + expr.hir_id, + expr.span, + &format!("this call to `{}()` will always panic", method_name.ident.name), + |diag| { + diag.span_label(unwrappable.check.span, "because of this check"); + }, + ); } } walk_expr(self, expr); diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 21592abbf..df4b42133 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -3,7 +3,6 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; use clippy_utils::{method_chain_args, return_ty}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::ImplItemKind; use rustc_lint::{LateContext, LateLintPass}; @@ -60,15 +59,13 @@ declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if_chain! { + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind // first check if it's a method or function - if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option); - then { - lint_impl_body(cx, impl_item.span, impl_item); - } + && (is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option)) + { + lint_impl_body(cx, impl_item.span, impl_item); } } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index c3fe16ad5..f058fe5f8 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -2,7 +2,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::ty::same_type_and_consts; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -93,32 +92,40 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // relevant for linting, since this is the self type of the `impl` we're currently in. To // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that // we're in an `impl` or nested item, that we don't want to lint - let stack_item = if_chain! { - if let ItemKind::Impl(Impl { self_ty, generics,.. }) = item.kind; - if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind; - let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; - if parameters.as_ref().map_or(true, |params| { - params.parenthesized == GenericArgsParentheses::No + let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind + && let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind + && let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args + && parameters.as_ref().map_or(true, |params| { + params.parenthesized == GenericArgsParentheses::No && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) - }); - if !item.span.from_expansion(); - if !is_from_proc_macro(cx, item); // expensive, should be last check - then { - // Self cannot be used inside const generic parameters - let types_to_skip = generics.params.iter().filter_map(|param| { - match param { - GenericParam { kind: GenericParamKind::Const { ty: Ty { hir_id, ..}, ..}, ..} => Some(*hir_id), - _ => None, - } - }).chain(std::iter::once(self_ty.hir_id)).collect(); - StackItem::Check { - impl_id: item.owner_id.def_id, - in_body: 0, - types_to_skip, - } - } else { - StackItem::NoCheck + }) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + // expensive, should be last check + { + // Self cannot be used inside const generic parameters + let types_to_skip = generics + .params + .iter() + .filter_map(|param| match param { + GenericParam { + kind: + GenericParamKind::Const { + ty: Ty { hir_id, .. }, .. + }, + .. + } => Some(*hir_id), + _ => None, + }) + .chain(std::iter::once(self_ty.hir_id)) + .collect(); + StackItem::Check { + impl_id: item.owner_id.def_id, + in_body: 0, + types_to_skip, } + } else { + StackItem::NoCheck }; self.stack.push(stack_item); } @@ -132,56 +139,54 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait // declaration. The collection of those types is all this method implementation does. - if_chain! { - if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind; - if let Some(&mut StackItem::Check { + if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind + && let Some(&mut StackItem::Check { impl_id, ref mut types_to_skip, .. - }) = self.stack.last_mut(); - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id); - then { - // `self_ty` is the semantic self type of `impl for `. This cannot be - // `Self`. - let self_ty = impl_trait_ref.instantiate_identity().self_ty(); + }) = self.stack.last_mut() + && let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id) + { + // `self_ty` is the semantic self type of `impl for `. This cannot be + // `Self`. + let self_ty = impl_trait_ref.instantiate_identity().self_ty(); - // `trait_method_sig` is the signature of the function, how it is declared in the - // trait, not in the impl of the trait. - let trait_method = cx - .tcx - .associated_item(impl_item.owner_id) - .trait_item_def_id - .expect("impl method matches a trait method"); - let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity(); - let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig); + // `trait_method_sig` is the signature of the function, how it is declared in the + // trait, not in the impl of the trait. + let trait_method = cx + .tcx + .associated_item(impl_item.owner_id) + .trait_item_def_id + .expect("impl method matches a trait method"); + let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity(); + let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig); - // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the - // implementation of the trait. - let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output { - Some(&**ty) - } else { - None - }; - let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty); + // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the + // implementation of the trait. + let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output { + Some(&**ty) + } else { + None + }; + let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty); - // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature. - // - // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the - // trait declaration. This is used to check if `Self` was used in the trait - // declaration. - // - // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed - // to `Self`), we want to skip linting that type and all subtypes of it. This - // avoids suggestions to e.g. replace `Vec` with `Vec`, in an `impl Trait - // for u8`, when the trait always uses `Vec`. - // - // See also https://github.com/rust-lang/rust-clippy/issues/2894. - for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { - if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { - let mut visitor = SkipTyCollector::default(); - visitor.visit_ty(impl_hir_ty); - types_to_skip.extend(visitor.types_to_skip); - } + // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature. + // + // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the + // trait declaration. This is used to check if `Self` was used in the trait + // declaration. + // + // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed + // to `Self`), we want to skip linting that type and all subtypes of it. This + // avoids suggestions to e.g. replace `Vec` with `Vec`, in an `impl Trait + // for u8`, when the trait always uses `Vec`. + // + // See also https://github.com/rust-lang/rust-clippy/issues/2894. + for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { + if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { + let mut visitor = SkipTyCollector::default(); + visitor.visit_ty(impl_hir_ty); + types_to_skip.extend(visitor.types_to_skip); } } } @@ -203,41 +208,38 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if_chain! { - if !hir_ty.span.from_expansion(); - if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); - if let Some(&StackItem::Check { + if !hir_ty.span.from_expansion() + && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) + && let Some(&StackItem::Check { impl_id, in_body, ref types_to_skip, - }) = self.stack.last(); - if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; - if !matches!( + }) = self.stack.last() + && let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind + && !matches!( path.res, - Res::SelfTyParam { .. } - | Res::SelfTyAlias { .. } - | Res::Def(DefKind::TyParam, _) - ); - if !types_to_skip.contains(&hir_ty.hir_id); - let ty = if in_body > 0 { + Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::Def(DefKind::TyParam, _) + ) + && !types_to_skip.contains(&hir_ty.hir_id) + && let ty = if in_body > 0 { cx.typeck_results().node_type(hir_ty.hir_id) } else { hir_ty_to_ty(cx.tcx, hir_ty) - }; - if same_type_and_consts(ty, cx.tcx.type_of(impl_id).instantiate_identity()); - then { - span_lint(cx, hir_ty.span); } + && same_type_and_consts(ty, cx.tcx.type_of(impl_id).instantiate_identity()) + { + span_lint(cx, hir_ty.span); } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); - if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); - if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity(); - then {} else { return; } + if !expr.span.from_expansion() + && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) + && let Some(&StackItem::Check { impl_id, .. }) = self.stack.last() + && cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity() + { + } else { + return; } match expr.kind { ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), @@ -252,18 +254,16 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { - if_chain! { - if !pat.span.from_expansion(); - if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); - if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); + if !pat.span.from_expansion() + && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) + && let Some(&StackItem::Check { impl_id, .. }) = self.stack.last() // get the path from the pattern - if let PatKind::Path(QPath::Resolved(_, path)) + && let PatKind::Path(QPath::Resolved(_, path)) | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) - | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind; - if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity(); - then { - check_path(cx, path); - } + | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind + && cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity() + { + check_path(cx, path); } } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 28f1d487e..52327b82e 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -3,7 +3,6 @@ use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_con use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, path_to_local}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -311,76 +310,63 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if_chain! { - if is_trait_method(cx, e, sym::TryInto) && name.ident.name == sym::try_into; - let a = cx.typeck_results().expr_ty(e); - let b = cx.typeck_results().expr_ty(recv); - if is_type_diagnostic_item(cx, a, sym::Result); - if let ty::Adt(_, args) = a.kind(); - if let Some(a_type) = args.types().next(); - if same_type_and_consts(a_type, b); + if is_trait_method(cx, e, sym::TryInto) + && name.ident.name == sym::try_into + && let a = cx.typeck_results().expr_ty(e) + && let b = cx.typeck_results().expr_ty(recv) + && is_type_diagnostic_item(cx, a, sym::Result) + && let ty::Adt(_, args) = a.kind() + && let Some(a_type) = args.types().next() + && same_type_and_consts(a_type, b) + { + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + &format!("useless conversion to the same type: `{b}`"), + None, + "consider removing `.try_into()`", + ); + } + }, - then { + ExprKind::Call(path, [arg]) => { + if let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && !is_ty_alias(qpath) + { + let a = cx.typeck_results().expr_ty(e); + let b = cx.typeck_results().expr_ty(arg); + if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id) + && is_type_diagnostic_item(cx, a, sym::Result) + && let ty::Adt(_, args) = a.kind() + && let Some(a_type) = args.types().next() + && same_type_and_consts(a_type, b) + { + let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); span_lint_and_help( cx, USELESS_CONVERSION, e.span, &format!("useless conversion to the same type: `{b}`"), None, - "consider removing `.try_into()`", + &hint, ); } - } - }, - ExprKind::Call(path, [arg]) => { - if_chain! { - if let ExprKind::Path(ref qpath) = path.kind; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - if !is_ty_alias(qpath); - then { - let a = cx.typeck_results().expr_ty(e); - let b = cx.typeck_results().expr_ty(arg); - if_chain! { - if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id); - if is_type_diagnostic_item(cx, a, sym::Result); - if let ty::Adt(_, args) = a.kind(); - if let Some(a_type) = args.types().next(); - if same_type_and_consts(a_type, b); - - then { - let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); - span_lint_and_help( - cx, - USELESS_CONVERSION, - e.span, - &format!("useless conversion to the same type: `{b}`"), - None, - &hint, - ); - } - } - - if_chain! { - if cx.tcx.is_diagnostic_item(sym::from_fn, def_id); - if same_type_and_consts(a, b); - - then { - let mut app = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_par(); - let sugg_msg = - format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); - span_lint_and_sugg( - cx, - USELESS_CONVERSION, - e.span, - &format!("useless conversion to the same type: `{b}`"), - &sugg_msg, - sugg.to_string(), - app, - ); - } - } + if cx.tcx.is_diagnostic_item(sym::from_fn, def_id) && same_type_and_consts(a, b) { + let mut app = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_par(); + let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + &format!("useless conversion to the same type: `{b}`"), + &sugg_msg, + sugg.to_string(), + app, + ); } } }, diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index ddcb9f27c..877a77fd6 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,7 +1,6 @@ pub mod almost_standard_lint_formulation; pub mod collapsible_calls; pub mod compiler_lint_functions; -pub mod if_chain_style; pub mod interning_defined_symbol; pub mod invalid_paths; pub mod lint_without_lint_pass; diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs index d7666b77f..f514f166c 100644 --- a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, Expr, ExprKind}; @@ -78,45 +77,43 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { return; } - if_chain! { - if let ExprKind::Call(func, and_then_args) = expr.kind; - if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); - if and_then_args.len() == 5; - if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; - let body = cx.tcx.hir().body(body); - let only_expr = peel_blocks_with_stmt(body.value); - if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; - if let ExprKind::Path(..) = recv.kind; - then { - let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - suggest_suggestion( - cx, - expr, - &and_then_snippets, - &span_suggestion_snippets(cx, span_call_args), - ); - }, - "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); - }, - "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); - }, - "help" => { - let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); - }, - "note" => { - let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); - }, - _ => (), - } + if let ExprKind::Call(func, and_then_args) = expr.kind + && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]) + && and_then_args.len() == 5 + && let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind + && let body = cx.tcx.hir().body(body) + && let only_expr = peel_blocks_with_stmt(body.value) + && let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind + && let ExprKind::Path(..) = recv.kind + { + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); + match ps.ident.as_str() { + "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + suggest_suggestion( + cx, + expr, + &and_then_snippets, + &span_suggestion_snippets(cx, span_call_args), + ); + }, + "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); + }, + "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); + }, + "help" => { + let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); + }, + "note" => { + let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); + }, + _ => (), } } } diff --git a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs index cacd05262..5aa1417cf 100644 --- a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs +++ b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::match_type; use clippy_utils::{is_lint_allowed, paths}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -56,22 +55,20 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { return; } - if_chain! { - if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; - let fn_name = path.ident; - if let Some(sugg) = self.map.get(fn_name.as_str()); - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); - then { - span_lint_and_help( - cx, - COMPILER_LINT_FUNCTIONS, - path.ident.span, - "usage of a compiler lint function", - None, - &format!("please use the Clippy variant of this function: `{sugg}`"), - ); - } + if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind + && let fn_name = path.ident + && let Some(sugg) = self.map.get(fn_name.as_str()) + && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() + && (match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT)) + { + span_lint_and_help( + cx, + COMPILER_LINT_FUNCTIONS, + path.ident.span, + "usage of a compiler lint function", + None, + &format!("please use the Clippy variant of this function: `{sugg}`"), + ); } } } diff --git a/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/clippy_lints/src/utils/internal_lints/if_chain_style.rs deleted file mode 100644 index 8cdd5ea89..000000000 --- a/clippy_lints/src/utils/internal_lints/if_chain_style.rs +++ /dev/null @@ -1,166 +0,0 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::{higher, is_else_clause, is_expn_of}; -use if_chain::if_chain; -use rustc_hir as hir; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{BytePos, Span}; - -declare_clippy_lint! { - /// Finds unidiomatic usage of `if_chain!` - pub IF_CHAIN_STYLE, - internal, - "non-idiomatic `if_chain!` usage" -} - -declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); - -impl<'tcx> LateLintPass<'tcx> for IfChainStyle { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - let (local, after, if_chain_span) = if_chain! { - if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; - if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); - then { (local, after, if_chain_span) } else { return } - }; - if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be above the `if_chain!`", - ); - } else if local.span.eq_ctxt(block.span) && is_if_chain_then(after, block.expr, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be inside `then { .. }`", - ); - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { - (cond, then, r#else.is_some()) - } else { - return; - }; - let ExprKind::Block(then_block, _) = then.kind else { - return; - }; - let if_chain_span = is_expn_of(expr.span, "if_chain"); - if !els { - check_nested_if_chains(cx, expr, then_block, if_chain_span); - } - let Some(if_chain_span) = if_chain_span else { return }; - // check for `if a && b;` - if_chain! { - if let ExprKind::Binary(op, _, _) = cond.kind; - if op.node == BinOpKind::And; - if cx.sess().source_map().is_multiline(cond.span); - then { - span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); - } - } - if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) - && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) - { - span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); - } - } -} - -fn check_nested_if_chains( - cx: &LateContext<'_>, - if_expr: &Expr<'_>, - then_block: &Block<'_>, - if_chain_span: Option, -) { - #[rustfmt::skip] - let (head, tail) = match *then_block { - Block { stmts, expr: Some(tail), .. } => (stmts, tail), - Block { - stmts: &[ - ref head @ .., - Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } - ], - .. - } => (head, tail), - _ => return, - }; - if_chain! { - if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); - let sm = cx.sess().source_map(); - if head - .iter() - .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); - if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); - then { - } else { - return; - } - } - let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { - (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), - (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), - (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), - _ => return, - }; - span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { - let (span, msg) = match head { - [] => return, - [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), - [a, .., b] => ( - a.span.to(b.span), - "these `let` statements can also be in the `if_chain!`", - ), - }; - diag.span_help(span, msg); - }); -} - -fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { - cx.tcx - .hir() - .parent_iter(hir_id) - .find(|(_, node)| { - #[rustfmt::skip] - !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) - }) - .map_or(false, |(id, _)| { - is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) - }) -} - -/// Checks a trailing slice of statements and expression of a `Block` to see if they are part -/// of the `then {..}` portion of an `if_chain!` -fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { - let span = if let [stmt, ..] = stmts { - stmt.span - } else if let Some(expr) = expr { - expr.span - } else { - // empty `then {}` - return true; - }; - is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) -} - -/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. -fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { - let mut span = local.pat.span; - if let Some(init) = local.init { - span = span.to(init.span); - } - span.adjust(if_chain_span.ctxt().outer_expn()); - let sm = cx.sess().source_map(); - let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); - let span = sm.span_extend_to_next_char(span, ';', false); - Span::new( - span.lo() - BytePos(3), - span.hi() + BytePos(1), - span.ctxt(), - span.parent(), - ) -} diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index fc9afe5ca..16d0636b8 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -77,15 +76,13 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { for def_id in def_path_def_ids(cx, module) { for item in cx.tcx.module_children(def_id) { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id).instantiate_identity(); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item_def_id); - } + if let Res::Def(DefKind::Const, item_def_id) = item.res + && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() + && match_type(cx, ty, &paths::SYMBOL) + && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id) + && let Ok(value) = value.to_u32() + { + self.symbol_map.insert(value, item_def_id); } } } @@ -93,24 +90,22 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, [arg]) = &expr.kind; - if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); - if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); - if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); - let value = Symbol::intern(&arg).as_u32(); - if let Some(&def_id) = self.symbol_map.get(&value); - then { - span_lint_and_sugg( - cx, - INTERNING_DEFINED_SYMBOL, - is_expn_of(expr.span, "sym").unwrap_or(expr.span), - "interning a defined symbol", - "try", - cx.tcx.def_path_str(def_id), - Applicability::MachineApplicable, - ); - } + if let ExprKind::Call(func, [arg]) = &expr.kind + && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() + && match_def_path(cx, *def_id, &paths::SYMBOL_INTERN) + && let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg) + && let value = Symbol::intern(&arg).as_u32() + && let Some(&def_id) = self.symbol_map.get(&value) + { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + cx.tcx.def_path_str(def_id), + Applicability::MachineApplicable, + ); } if let ExprKind::Binary(op, left, right) = expr.kind { if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { @@ -163,27 +158,28 @@ impl InterningDefinedSymbol { fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR]; static SYMBOL_STR_PATHS: &[&[&str]] = &[&paths::SYMBOL_AS_STR, &paths::SYMBOL_TO_IDENT_STRING]; - let call = if_chain! { - if let ExprKind::AddrOf(_, _, e) = expr.kind; - if let ExprKind::Unary(UnOp::Deref, e) = e.kind; - then { e } else { expr } + let call = if let ExprKind::AddrOf(_, _, e) = expr.kind + && let ExprKind::Unary(UnOp::Deref, e) = e.kind + { + e + } else { + expr }; - if_chain! { + if let ExprKind::MethodCall(_, item, [], _) = call.kind // is a method call - if let ExprKind::MethodCall(_, item, [], _) = call.kind; - if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); - let ty = cx.typeck_results().expr_ty(item); + && let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id) + && let ty = cx.typeck_results().expr_ty(item) // ...on either an Ident or a Symbol - if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + && let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { Some(false) } else if match_type(cx, ty, &paths::IDENT) { Some(true) } else { None - }; + } // ...which converts it to a string - let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; - if let Some(is_to_owned) = paths + && let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS } + && let Some(is_to_owned) = paths .iter() .find_map(|path| if match_def_path(cx, did, path) { Some(path == &paths::SYMBOL_TO_IDENT_STRING) @@ -194,14 +190,13 @@ impl InterningDefinedSymbol { Some(true) } else { None - }); - then { - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } + }) + { + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); } // is a string constant if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 250772238..66d32087f 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -1,7 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; -use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::Item; @@ -31,13 +30,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let local_def_id = &cx.tcx.parent_module(item.hir_id()); let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if_chain! { - if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(.., body_id) = item.kind; - let body = cx.tcx.hir().body(body_id); - let typeck_results = cx.tcx.typeck_body(body_id); - if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - if let Some(path) = path + if mod_name.as_str() == "paths" + && let hir::ItemKind::Const(.., body_id) = item.kind + && let body = cx.tcx.hir().body(body_id) + && let typeck_results = cx.tcx.typeck_body(body_id) + && let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value) + && let Some(path) = path .iter() .map(|x| { if let Constant::Str(s) = x { @@ -46,11 +44,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { None } }) - .collect::>>(); - if !check_path(cx, &path[..]); - then { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } + .collect::>>() + && !check_path(cx, &path[..]) + { + span_lint(cx, INVALID_PATHS, item.span, "invalid path"); } } } diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 00e352961..486e82204 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -2,7 +2,6 @@ use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::{is_lint_allowed, match_def_path, paths}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; @@ -309,14 +308,16 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { let attrs = cx.tcx.hir().attrs(item.hir_id()); attrs.iter().find_map(|attr| { - if_chain! { + if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind // Identify attribute - if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; - if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; - if tool_name.ident.name == sym::clippy; - if attr_name.ident.name == sym::version; - if let Some(version) = attr.value_str(); - then { Some(version) } else { None } + && let [tool_name, attr_name] = &attr_kind.item.path.segments[..] + && tool_name.ident.name == sym::clippy + && attr_name.ident.name == sym::version + && let Some(version) = attr.value_str() + { + Some(version) + } else { + None } }) } diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 51abe0c1d..8ecdba47f 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -14,7 +14,6 @@ use clippy_config::{get_configuration_metadata, ClippyConfiguration}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths}; -use if_chain::if_chain; use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; @@ -543,49 +542,45 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { if let ItemKind::Static(ty, Mutability::Not, _) = item.kind { // Normal lint - if_chain! { + if is_lint_ref_type(cx, ty) // item validation - if is_lint_ref_type(cx, ty); // disallow check - let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase() // metadata extraction - if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item); - if let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item); - then { - if let Some(configuration_section) = self.get_lint_configs(&lint_name) { - raw_docs.push_str(&configuration_section); - } - let version = get_lint_version(cx, item); - - self.lints.push(LintMetadata::new( - lint_name, - SerializableSpan::from_item(cx, item), - group, - level, - version, - raw_docs, - )); + && let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item) + && let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item) + { + if let Some(configuration_section) = self.get_lint_configs(&lint_name) { + raw_docs.push_str(&configuration_section); } + let version = get_lint_version(cx, item); + + self.lints.push(LintMetadata::new( + lint_name, + SerializableSpan::from_item(cx, item), + group, + level, + version, + raw_docs, + )); } - if_chain! { - if is_deprecated_lint(cx, ty); + if is_deprecated_lint(cx, ty) // disallow check - let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase() // Metadata the little we can get from a deprecated lint - if let Some(raw_docs) = extract_attr_docs_or_lint(cx, item); - then { - let version = get_lint_version(cx, item); + && let Some(raw_docs) = extract_attr_docs_or_lint(cx, item) + { + let version = get_lint_version(cx, item); - self.lints.push(LintMetadata::new( - lint_name, - SerializableSpan::from_item(cx, item), - DEPRECATED_LINT_GROUP_STR.to_string(), - DEPRECATED_LINT_LEVEL, - version, - raw_docs, - )); - } + self.lints.push(LintMetadata::new( + lint_name, + SerializableSpan::from_item(cx, item), + DEPRECATED_LINT_GROUP_STR.to_string(), + DEPRECATED_LINT_LEVEL, + version, + raw_docs, + )); } } } @@ -789,15 +784,13 @@ fn collect_renames(lints: &mut Vec) { loop { if let Some(lint_name) = names.pop() { for (k, v) in RENAMED_LINTS { - if_chain! { - if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); - if name == lint_name; - if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); - then { - lint.former_ids.insert(past_name.to_owned()); - writeln!(collected, "* `{past_name}`").unwrap(); - names.push(past_name.to_string()); - } + if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX) + && name == lint_name + && let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX) + { + lint.former_ids.insert(past_name.to_owned()); + writeln!(collected, "* `{past_name}`").unwrap(); + names.push(past_name.to_string()); } } @@ -927,20 +920,17 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { } fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - if_chain! { - if let ExprKind::Path(qpath) = &expr.kind; - if let QPath::Resolved(_, path) = qpath; - - let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)); - if match_type(self.cx, expr_ty, &paths::LINT); - then { - if let hir::def::Res::Def(DefKind::Static(..), _) = path.res { - let lint_name = last_path_segment(qpath).ident.name; - self.lints.push(sym_to_string(lint_name).to_ascii_lowercase()); - } else if let Some(local) = get_parent_local(self.cx, expr) { - if let Some(local_init) = local.init { - intravisit::walk_expr(self, local_init); - } + if let ExprKind::Path(qpath) = &expr.kind + && let QPath::Resolved(_, path) = qpath + && let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)) + && match_type(self.cx, expr_ty, &paths::LINT) + { + if let hir::def::Res::Def(DefKind::Static(..), _) = path.res { + let lint_name = last_path_segment(qpath).ident.name; + self.lints.push(sym_to_string(lint_name).to_ascii_lowercase()); + } else if let Some(local) = get_parent_local(self.cx, expr) { + if let Some(local_init) = local.init { + intravisit::walk_expr(self, local_init); } } } @@ -992,13 +982,11 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)); - if_chain! { - if match_type(self.cx, expr_ty, &paths::APPLICABILITY); - if let Some(local) = get_parent_local(self.cx, expr); - if let Some(local_init) = local.init; - then { - intravisit::walk_expr(self, local_init); - } + if match_type(self.cx, expr_ty, &paths::APPLICABILITY) + && let Some(local) = get_parent_local(self.cx, expr) + && let Some(local_init) = local.init + { + intravisit::walk_expr(self, local_init); }; intravisit::walk_expr(self, expr); diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index 86b77a77f..86b1a0ae6 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -22,40 +21,41 @@ declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); impl LateLintPass<'_> for MsrvAttrImpl { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if_chain! { - if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(_), - items, - .. - }) = &item.kind; - if let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity); - let is_late_pass = match_def_path(cx, trait_ref.def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS); - if let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind(); - if self_ty_def.is_struct(); - if self_ty_def.all_fields().any(|f| { + if let hir::ItemKind::Impl(hir::Impl { + of_trait: Some(_), + items, + .. + }) = &item.kind + && let Some(trait_ref) = cx + .tcx + .impl_trait_ref(item.owner_id) + .map(EarlyBinder::instantiate_identity) + && let is_late_pass = match_def_path(cx, trait_ref.def_id, &paths::LATE_LINT_PASS) + && (is_late_pass || match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS)) + && let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind() + && self_ty_def.is_struct() + && self_ty_def.all_fields().any(|f| { cx.tcx .type_of(f.did) .instantiate_identity() .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV)) - }); - if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); - then { - let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; - let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; - let span = cx.sess().source_map().span_through_char(item.span, '{'); - span_lint_and_sugg( - cx, - MISSING_MSRV_ATTR_IMPL, - span, - &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), - &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), - format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), - Applicability::MachineApplicable, - ); - } + }) + && !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)) + { + let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; + let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; + let span = cx.sess().source_map().span_through_char(item.span, '{'); + span_lint_and_sugg( + cx, + MISSING_MSRV_ATTR_IMPL, + span, + &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), + &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), + format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs index 2b13fad80..77b95e51f 100644 --- a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs +++ b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::match_type; use clippy_utils::{is_lint_allowed, method_calls, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -40,23 +39,21 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { let (method_names, arg_lists, spans) = method_calls(expr, 2); let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if_chain! { - if let ["expn_data", "outer_expn"] = method_names.as_slice(); - let (self_arg, args) = arg_lists[1]; - if args.is_empty(); - let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); - then { - span_lint_and_sugg( - cx, - OUTER_EXPN_EXPN_DATA, - spans[1].with_hi(expr.span.hi()), - "usage of `outer_expn().expn_data()`", - "try", - "outer_expn_data()".to_string(), - Applicability::MachineApplicable, - ); - } + if let ["expn_data", "outer_expn"] = method_names.as_slice() + && let (self_arg, args) = arg_lists[1] + && args.is_empty() + && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs() + && match_type(cx, self_ty, &paths::SYNTAX_CONTEXT) + { + span_lint_and_sugg( + cx, + OUTER_EXPN_EXPN_DATA, + spans[1].with_hi(expr.span.hi()), + "usage of `outer_expn().expn_data()`", + "try", + "outer_expn_data()".to_string(), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 81be04659..9aa23b6ef 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::Applicability; @@ -102,108 +101,106 @@ impl UnnecessaryDefPath { &["clippy_utils", "is_expr_path_def_path"], ]; - if_chain! { - if let [cx_arg, def_arg, args @ ..] = args; - if let ExprKind::Path(path) = &func.kind; - if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if let Some(which_path) = match_any_def_paths(cx, id, PATHS); - let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; + if let [cx_arg, def_arg, args @ ..] = args + && let ExprKind::Path(path) = &func.kind + && let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && let Some(which_path) = match_any_def_paths(cx, id, PATHS) + && let item_arg = if which_path == 4 { &args[1] } else { &args[0] } // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, item_arg); - let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = def_path_def_ids(cx, &segments[..]).next(); - then { - // Check if the target item is a diagnostic item or LangItem. - #[rustfmt::skip] - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(item_name) = get_lang_item_name(cx, def_id) { - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; - }; + && let Some(segments) = path_to_matched_type(cx, item_arg) + && let segments = segments.iter().map(|sym| &**sym).collect::>() + && let Some(def_id) = def_path_def_ids(cx, &segments[..]).next() + { + // Check if the target item is a diagnostic item or LangItem. + #[rustfmt::skip] + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - _ => false, - }; + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + _ => false, + }; - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => ( - format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), - has_ctor, + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => ( + format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), + has_ctor, + ), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"), + has_ctor, + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => { + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) + }, + // match_type + (2, Item::DiagnosticItem(item)) => ( + format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (2, Item::LangItem(item)) => ( + format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), + false, + ), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), + false, + ), + (3, Item::DiagnosticItem(item)) => ( + format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))", ), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"), - has_ctor, - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => { - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) - }, - // match_type - (2, Item::DiagnosticItem(item)) => ( - format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (2, Item::LangItem(item)) => ( - format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), - false, - ), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), - false, - ), - (3, Item::DiagnosticItem(item)) => ( - format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))", - ), - false, - ), - _ => return, - }; + false, + ), + _ => return, + }; - span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { - diag.span_suggestion(span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead", - ); - } - }); + span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { + diag.span_suggestion(span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead", + ); + } + }); - self.linted_def_ids.insert(def_id); - } + self.linted_def_ids.insert(def_id); } } diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index a9dd44afa..9ad2ad2d1 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -7,7 +7,6 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, higher, is_trait_method}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -72,21 +71,19 @@ fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]` - if_chain! { - if adjusts_to_slice(cx, expr); - if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()); - then { - let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind { - // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.) - (SuggestedType::SliceRef(mutability), expr.span) - } else { - // `expr` is the `vec![_]` expansion, so suggest `[_]` - // and also use the span of the actual `vec![_]` expression - (SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site) - }; + if adjusts_to_slice(cx, expr) + && let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) + { + let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind { + // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.) + (SuggestedType::SliceRef(mutability), expr.span) + } else { + // `expr` is the `vec![_]` expansion, so suggest `[_]` + // and also use the span of the actual `vec![_]` expression + (SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site) + }; - self.check_vec_macro(cx, &vec_args, span, suggest_slice); - } + self.check_vec_macro(cx, &vec_args, span, suggest_slice); } // search for `let foo = vec![_]` expressions where all uses of `foo` @@ -124,15 +121,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { } // search for `for _ in vec![…]` - if_chain! { - if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr); - if let Some(vec_args) = higher::VecArgs::hir(cx, arg); - if self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR); - then { - // report the error around the `vec!` not inside `:` - let span = arg.span.ctxt().outer_expn_data().call_site; - self.check_vec_macro(cx, &vec_args, span, SuggestedType::Array); - } + if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) + && let Some(vec_args) = higher::VecArgs::hir(cx, arg) + && self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR) + { + // report the error around the `vec!` not inside `:` + let span = arg.span.ctxt().outer_expn_data().call_site; + self.check_vec_macro(cx, &vec_args, span, SuggestedType::Array); } } diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index d88ede763..5c1bea744 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_test_module_or_function; use clippy_utils::source::{snippet, snippet_with_applicability}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind, PathSegment, UseKind}; @@ -127,70 +126,55 @@ impl LateLintPass<'_> for WildcardImports { if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) { return; } - if_chain! { - if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - if self.warn_on_all || !self.check_exceptions(item, use_path.segments); - let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id); - if !used_imports.is_empty(); // Already handled by `unused_imports` - if !used_imports.contains(&kw::Underscore); - then { - let mut applicability = Applicability::MachineApplicable; - let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); - let (span, braced_glob) = if import_source_snippet.is_empty() { - // This is a `_::{_, *}` import - // In this case `use_path.span` is empty and ends directly in front of the `*`, - // so we need to extend it by one byte. - ( - use_path.span.with_hi(use_path.span.hi() + BytePos(1)), - true, - ) - } else { - // In this case, the `use_path.span` ends right before the `::*`, so we need to - // extend it up to the `*`. Since it is hard to find the `*` in weird - // formattings like `use _ :: *;`, we extend it up to, but not including the - // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we - // can just use the end of the item span - let mut span = use_path.span.with_hi(item.span.hi()); - if snippet(cx, span, "").ends_with(';') { - span = use_path.span.with_hi(item.span.hi() - BytePos(1)); - } - ( - span, false, - ) - }; + if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind + && (self.warn_on_all || !self.check_exceptions(item, use_path.segments)) + && let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id) + && !used_imports.is_empty() // Already handled by `unused_imports` + && !used_imports.contains(&kw::Underscore) + { + let mut applicability = Applicability::MachineApplicable; + let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); + let (span, braced_glob) = if import_source_snippet.is_empty() { + // This is a `_::{_, *}` import + // In this case `use_path.span` is empty and ends directly in front of the `*`, + // so we need to extend it by one byte. + (use_path.span.with_hi(use_path.span.hi() + BytePos(1)), true) + } else { + // In this case, the `use_path.span` ends right before the `::*`, so we need to + // extend it up to the `*`. Since it is hard to find the `*` in weird + // formattings like `use _ :: *;`, we extend it up to, but not including the + // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we + // can just use the end of the item span + let mut span = use_path.span.with_hi(item.span.hi()); + if snippet(cx, span, "").ends_with(';') { + span = use_path.span.with_hi(item.span.hi() - BytePos(1)); + } + (span, false) + }; - let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); - let imports_string = if imports.len() == 1 { - imports.pop().unwrap() - } else if braced_glob { - imports.join(", ") - } else { - format!("{{{}}}", imports.join(", ")) - }; + let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); + let imports_string = if imports.len() == 1 { + imports.pop().unwrap() + } else if braced_glob { + imports.join(", ") + } else { + format!("{{{}}}", imports.join(", ")) + }; - let sugg = if braced_glob { - imports_string - } else { - format!("{import_source_snippet}::{imports_string}") - }; + let sugg = if braced_glob { + imports_string + } else { + format!("{import_source_snippet}::{imports_string}") + }; - // Glob imports always have a single resolution. - let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] { - (ENUM_GLOB_USE, "usage of wildcard import for enum variants") - } else { - (WILDCARD_IMPORTS, "usage of wildcard import") - }; + // Glob imports always have a single resolution. + let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] { + (ENUM_GLOB_USE, "usage of wildcard import for enum variants") + } else { + (WILDCARD_IMPORTS, "usage of wildcard import") + }; - span_lint_and_sugg( - cx, - lint, - span, - message, - "try", - sugg, - applicability, - ); - } + span_lint_and_sugg(cx, lint, span, message, "try", sugg, applicability); } } diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index f2f0699ef..1d6217d18 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -1,6 +1,5 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -32,35 +31,30 @@ declare_lint_pass!(ZeroDiv => [ZERO_DIVIDED_BY_ZERO]); impl<'tcx> LateLintPass<'tcx> for ZeroDiv { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // check for instances of 0.0/0.0 - if_chain! { - if let ExprKind::Binary(ref op, left, right) = expr.kind; - if op.node == BinOpKind::Div; + if let ExprKind::Binary(ref op, left, right) = expr.kind + && op.node == BinOpKind::Div // TODO - constant_simple does not fold many operations involving floats. // That's probably fine for this lint - it's pretty unlikely that someone would // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. - if let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left); - if let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right); - if Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value; - if Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value; - then { - // since we're about to suggest a use of f32::NAN or f64::NAN, - // match the precision of the literals that are given. - let float_type = match (lhs_value, rhs_value) { - (Constant::F64(_), _) - | (_, Constant::F64(_)) => "f64", - _ => "f32" - }; - span_lint_and_help( - cx, - ZERO_DIVIDED_BY_ZERO, - expr.span, - "constant division of `0.0` with `0.0` will always result in NaN", - None, - &format!( - "consider using `{float_type}::NAN` if you would like a constant representing NaN", - ), - ); - } + && let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left) + && let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right) + && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) + && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) + { + // since we're about to suggest a use of f32::NAN or f64::NAN, + // match the precision of the literals that are given. + let float_type = match (lhs_value, rhs_value) { + (Constant::F64(_), _) | (_, Constant::F64(_)) => "f64", + _ => "f32", + }; + span_lint_and_help( + cx, + ZERO_DIVIDED_BY_ZERO, + expr.span, + "constant division of `0.0` with `0.0` will always result in NaN", + None, + &format!("consider using `{float_type}::NAN` if you would like a constant representing NaN",), + ); } } } diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index fee100fe1..41c1757fd 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item}; -use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -46,23 +45,28 @@ declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]); impl LateLintPass<'_> for ZeroSizedMapValues { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if_chain! { - if !hir_ty.span.from_expansion(); - if !in_trait_impl(cx, hir_ty.hir_id); - let ty = ty_from_hir_ty(cx, hir_ty); - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap); - if let Adt(_, args) = ty.kind(); - let ty = args.type_at(1); + if !hir_ty.span.from_expansion() + && !in_trait_impl(cx, hir_ty.hir_id) + && let ty = ty_from_hir_ty(cx, hir_ty) + && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + && let Adt(_, args) = ty.kind() + && let ty = args.type_at(1) // Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of // https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968 - if !ty.has_escaping_bound_vars(); + && !ty.has_escaping_bound_vars() // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. - if is_normalizable(cx, cx.param_env, ty); - if let Ok(layout) = cx.layout_of(ty); - if layout.is_zst(); - then { - span_lint_and_help(cx, ZERO_SIZED_MAP_VALUES, hir_ty.span, "map with zero-sized value type", None, "consider using a set instead"); - } + && is_normalizable(cx, cx.param_env, ty) + && let Ok(layout) = cx.layout_of(ty) + && layout.is_zst() + { + span_lint_and_help( + cx, + ZERO_SIZED_MAP_VALUES, + hir_ty.span, + "map with zero-sized value type", + None, + "consider using a set instead", + ); } } } diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index c9b01a68f..1f9802dc5 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -7,7 +7,6 @@ publish = false [dependencies] clippy_config = { path = "../clippy_config" } arrayvec = { version = "0.7", default-features = false } -if_chain = "1.0" itertools = "0.10.1" rustc-semver = "1.1" diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index b7d8c0a8f..3748e2a26 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -2,7 +2,7 @@ use crate::source::{get_source_text, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; -use if_chain::if_chain; + use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; @@ -372,27 +372,25 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { ExprKind::Binary(op, left, right) => self.binop(op, left, right), ExprKind::Call(callee, args) => { // We only handle a few const functions for now. - if_chain! { - if args.is_empty(); - if let ExprKind::Path(qpath) = &callee.kind; - let res = self.typeck_results.qpath_res(qpath, callee.hir_id); - if let Some(def_id) = res.opt_def_id(); - let def_path = self.lcx.get_def_path(def_id); - let def_path: Vec<&str> = def_path.iter().take(4).map(Symbol::as_str).collect(); - if let ["core", "num", int_impl, "max_value"] = *def_path; - then { - let value = match int_impl { - "" => i8::MAX as u128, - "" => i16::MAX as u128, - "" => i32::MAX as u128, - "" => i64::MAX as u128, - "" => i128::MAX as u128, - _ => return None, - }; - Some(Constant::Int(value)) - } else { - None - } + if args.is_empty() + && let ExprKind::Path(qpath) = &callee.kind + && let res = self.typeck_results.qpath_res(qpath, callee.hir_id) + && let Some(def_id) = res.opt_def_id() + && let def_path = self.lcx.get_def_path(def_id) + && let def_path = def_path.iter().take(4).map(Symbol::as_str).collect::>() + && let ["core", "num", int_impl, "max_value"] = *def_path + { + let value = match int_impl { + "" => i8::MAX as u128, + "" => i16::MAX as u128, + "" => i32::MAX as u128, + "" => i64::MAX as u128, + "" => i128::MAX as u128, + _ => return None, + }; + Some(Constant::Int(value)) + } else { + None } }, ExprKind::Index(arr, index, _) => self.index(arr, index), diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index edea4b366..3135a0336 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -5,7 +5,7 @@ use crate::consts::{constant_simple, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; -use if_chain::if_chain; + use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; @@ -30,24 +30,22 @@ pub struct ForLoop<'tcx> { impl<'tcx> ForLoop<'tcx> { /// Parses a desugared `for` loop pub fn hir(expr: &Expr<'tcx>) -> Option { - if_chain! { - if let hir::ExprKind::DropTemps(e) = expr.kind; - if let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind; - if let hir::ExprKind::Call(_, [arg]) = iterexpr.kind; - if let hir::ExprKind::Loop(block, ..) = arm.body.kind; - if let [stmt] = block.stmts; - if let hir::StmtKind::Expr(e) = stmt.kind; - if let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind; - if let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind; - then { - return Some(Self { - pat: field.pat, - arg, - body: some_arm.body, - loop_id: arm.body.hir_id, - span: expr.span.ctxt().outer_expn_data().call_site, - }); - } + if let hir::ExprKind::DropTemps(e) = expr.kind + && let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind + && let hir::ExprKind::Call(_, [arg]) = iterexpr.kind + && let hir::ExprKind::Loop(block, ..) = arm.body.kind + && let [stmt] = block.stmts + && let hir::StmtKind::Expr(e) = stmt.kind + && let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind + && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind + { + return Some(Self { + pat: field.pat, + arg, + body: some_arm.body, + loop_id: arm.body.hir_id, + span: expr.span.ctxt().outer_expn_data().call_site, + }); } None } @@ -277,29 +275,28 @@ impl<'a> VecArgs<'a> { /// Returns the arguments of the `vec!` macro if this expression was expanded /// from `vec!`. pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option> { - if_chain! { - if let hir::ExprKind::Call(fun, 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(); - then { - return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 { - // `vec![elem; size]` case - Some(VecArgs::Repeat(&args[0], &args[1])) - } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { - // `vec![a, b, c]` case - if let hir::ExprKind::Call(_, [arg]) = &args[0].kind - && let hir::ExprKind::Array(args) = arg.kind { - Some(VecArgs::Vec(args)) - } else { - None - } - } else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() { - Some(VecArgs::Vec(&[])) + if let hir::ExprKind::Call(fun, args) = expr.kind + && let hir::ExprKind::Path(ref qpath) = fun.kind + && is_expn_of(fun.span, "vec").is_some() + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + { + return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 { + // `vec![elem; size]` case + Some(VecArgs::Repeat(&args[0], &args[1])) + } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { + // `vec![a, b, c]` case + if let hir::ExprKind::Call(_, [arg]) = &args[0].kind + && let hir::ExprKind::Array(args) = arg.kind + { + Some(VecArgs::Vec(args)) } else { None - }; - } + } + } else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() { + Some(VecArgs::Vec(&[])) + } else { + None + }; } None diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1181dfc0e..21225dc70 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -76,7 +76,6 @@ use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use std::sync::{Mutex, MutexGuard, OnceLock}; -use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_data_structures::fx::FxHashMap; @@ -176,14 +175,12 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr /// canonical binding `HirId`. pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { let hir = cx.tcx.hir(); - if_chain! { - if let Some(Node::Pat(pat)) = hir.find(hir_id); - if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)); - let parent = hir.parent_id(hir_id); - if let Some(Node::Local(local)) = hir.find(parent); - then { - return local.init; - } + if let Some(Node::Pat(pat)) = hir.find(hir_id) + && matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)) + && let parent = hir.parent_id(hir_id) + && let Some(Node::Local(local)) = hir.find(parent) + { + return local.init; } None } @@ -713,13 +710,11 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> // Get the implemented trait for the current function let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); let parent_impl = cx.tcx.hir().get_parent_item(hir_id); - if_chain! { - if parent_impl != hir::CRATE_OWNER_ID; - if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id); - if let hir::ItemKind::Impl(impl_) = &item.kind; - then { - return impl_.of_trait.as_ref(); - } + if parent_impl != hir::CRATE_OWNER_ID + && let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id) + && let hir::ItemKind::Impl(impl_) = &item.kind + { + return impl_.of_trait.as_ref(); } None } @@ -823,12 +818,14 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< /// Returns true if the expr is equal to `Default::default` when evaluated. pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool { - if_chain! { - if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if is_diag_trait_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); - then { true } else { false } + if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind + && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() + && (is_diag_trait_item(cx, repl_def_id, sym::Default) + || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath)) + { + true + } else { + false } } @@ -843,14 +840,14 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { _ => false, }, ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)), - ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! { - if let ExprKind::Lit(const_lit) = cx.tcx.hir().body(len.body).value.kind; - if let LitKind::Int(v, _) = const_lit.node; - if v <= 32 && is_default_equivalent(cx, x); - then { + ExprKind::Repeat(x, ArrayLen::Body(len)) => { + if let ExprKind::Lit(const_lit) = cx.tcx.hir().body(len.body).value.kind + && let LitKind::Int(v, _) = const_lit.node + && v <= 32 + && is_default_equivalent(cx, x) + { true - } - else { + } else { false } }, @@ -1736,15 +1733,13 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It /// operator or the `try` macro. pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - if_chain! { - if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind; - if ddpos.as_opt_usize().is_none(); - if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk); - if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; - if path_to_local_id(arm.body, hir_id); - then { - return true; - } + if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind + && ddpos.as_opt_usize().is_none() + && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk) + && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind + && path_to_local_id(arm.body, hir_id) + { + return true; } false } @@ -1763,14 +1758,12 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc return Some(expr); } - if_chain! { - if arms.len() == 2; - if arms[0].guard.is_none(); - if arms[1].guard.is_none(); - if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])); - then { - return Some(expr); - } + if arms.len() == 2 + && arms[0].guard.is_none() + && arms[1].guard.is_none() + && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]))) + { + return Some(expr); } } @@ -1887,14 +1880,12 @@ pub fn match_function_call<'tcx>( expr: &'tcx Expr<'_>, path: &[&str], ) -> Option<&'tcx [Expr<'tcx>]> { - if_chain! { - if let ExprKind::Call(fun, args) = expr.kind; - if let ExprKind::Path(ref qpath) = fun.kind; - if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); - if match_def_path(cx, fun_def_id, path); - then { - return Some(args); - } + if let ExprKind::Call(fun, args) = expr.kind + && let ExprKind::Path(ref qpath) = fun.kind + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && match_def_path(cx, fun_def_id, path) + { + return Some(args); }; None } @@ -1904,13 +1895,11 @@ pub fn match_function_call_with_def_id<'tcx>( expr: &'tcx Expr<'_>, fun_def_id: DefId, ) -> Option<&'tcx [Expr<'tcx>]> { - if_chain! { - if let ExprKind::Call(fun, args) = expr.kind; - if let ExprKind::Path(ref qpath) = fun.kind; - if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id); - then { - return Some(args); - } + if let ExprKind::Call(fun, args) = expr.kind + && let ExprKind::Path(ref qpath) = fun.kind + && cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id) + { + return Some(args); }; None } @@ -2008,10 +1997,10 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t // check if expr is calling method or function with #[must_use] attribute pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let did = match expr.kind { - ExprKind::Call(path, _) => if_chain! { - if let ExprKind::Path(ref qpath) = path.kind; - if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id); - then { + ExprKind::Call(path, _) => { + if let ExprKind::Path(ref qpath) = path.kind + && let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id) + { Some(did) } else { None @@ -2071,14 +2060,12 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { }, _, ) => { - if_chain! { - if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind; - if let ExprKind::Ret(Some(ret_val)) = e.kind; - then { - expr = ret_val; - } else { - return false; - } + if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind + && let ExprKind::Ret(Some(ret_val)) = e.kind + { + expr = ret_val; + } else { + return false; } }, _ => return check_pat(cx, param.pat, expr), diff --git a/tests/ui-internal/if_chain_style.rs b/tests/ui-internal/if_chain_style.rs deleted file mode 100644 index b462b20e0..000000000 --- a/tests/ui-internal/if_chain_style.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![warn(clippy::if_chain_style)] -#![allow( - clippy::needless_if, - clippy::no_effect, - clippy::nonminimal_bool, - clippy::missing_clippy_version_attribute -)] - -extern crate if_chain; - -use if_chain::if_chain; - -fn main() { - if true { - let x = ""; - // `if_chain!` inside `if` - if_chain! { - if true; - if true; - then {} - } - } - if_chain! { - if true - // multi-line AND'ed conditions - && false; - if let Some(1) = Some(1); - // `let` before `then` - let x = ""; - then { - (); - } - } - if_chain! { - // single `if` condition - if true; - then { - let x = ""; - // nested if - if true {} - } - } - if_chain! { - // starts with `let ..` - let x = ""; - if let Some(1) = Some(1); - then { - let x = ""; - let x = ""; - // nested if_chain! - if_chain! { - if true; - if true; - then {} - } - } - } -} - -fn negative() { - if true { - (); - if_chain! { - if true; - if true; - then { (); } - } - } - if_chain! { - if true; - let x = ""; - if true; - then { (); } - } - if_chain! { - if true; - if true; - then { - if true { 1 } else { 2 } - } else { - 3 - } - }; - if true { - if_chain! { - if true; - if true; - then {} - } - } else if false { - if_chain! { - if true; - if false; - then {} - } - } -} diff --git a/tests/ui-internal/if_chain_style.stderr b/tests/ui-internal/if_chain_style.stderr deleted file mode 100644 index ea0495532..000000000 --- a/tests/ui-internal/if_chain_style.stderr +++ /dev/null @@ -1,86 +0,0 @@ -error: this `if` can be part of the inner `if_chain!` - --> $DIR/if_chain_style.rs:14:5 - | -LL | / if true { -LL | | let x = ""; -LL | | // `if_chain!` inside `if` -LL | | if_chain! { -... | -LL | | } -LL | | } - | |_____^ - | -help: this `let` statement can also be in the `if_chain!` - --> $DIR/if_chain_style.rs:15:9 - | -LL | let x = ""; - | ^^^^^^^^^^^ - = note: `-D clippy::if-chain-style` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::if_chain_style)]` - -error: `if a && b;` should be `if a; if b;` - --> $DIR/if_chain_style.rs:24:12 - | -LL | if true - | ____________^ -LL | | // multi-line AND'ed conditions -LL | | && false; - | |____________________^ - -error: `let` expression should be inside `then { .. }` - --> $DIR/if_chain_style.rs:29:9 - | -LL | let x = ""; - | ^^^^^^^^^^^ - -error: this `if` can be part of the outer `if_chain!` - --> $DIR/if_chain_style.rs:40:13 - | -LL | if true {} - | ^^^^^^^^^^ - | -help: this `let` statement can also be in the `if_chain!` - --> $DIR/if_chain_style.rs:38:13 - | -LL | let x = ""; - | ^^^^^^^^^^^ - -error: `if_chain!` only has one `if` - --> $DIR/if_chain_style.rs:34:5 - | -LL | / if_chain! { -LL | | // single `if` condition -LL | | if true; -LL | | then { -... | -LL | | } -LL | | } - | |_____^ - | - = note: this error originates in the macro `__if_chain` which comes from the expansion of the macro `if_chain` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: `let` expression should be above the `if_chain!` - --> $DIR/if_chain_style.rs:45:9 - | -LL | let x = ""; - | ^^^^^^^^^^^ - -error: this `if_chain!` can be merged with the outer `if_chain!` - --> $DIR/if_chain_style.rs:51:13 - | -LL | / if_chain! { -LL | | if true; -LL | | if true; -LL | | then {} -LL | | } - | |_____________^ - | -help: these `let` statements can also be in the `if_chain!` - --> $DIR/if_chain_style.rs:48:13 - | -LL | / let x = ""; -LL | | let x = ""; - | |_______________________^ - -error: aborting due to 7 previous errors -