From 7468542328caf3f0114ac4733fe2b76e2ed79954 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 2 Apr 2021 17:09:58 -0500 Subject: [PATCH] Introduce is_lang_ctor --- clippy_lints/src/collapsible_match.rs | 27 +++------- clippy_lints/src/if_then_some_else_none.rs | 9 ++-- clippy_lints/src/loops/manual_flatten.rs | 11 ++-- clippy_lints/src/manual_map.rs | 35 +++--------- clippy_lints/src/manual_ok_or.rs | 9 ++-- clippy_lints/src/manual_unwrap_or.rs | 25 +++++---- clippy_lints/src/matches.rs | 38 ++++++------- clippy_lints/src/mem_replace.rs | 7 +-- .../src/methods/option_map_or_none.rs | 7 +-- .../src/methods/unnecessary_filter_map.rs | 7 +-- clippy_lints/src/needless_question_mark.rs | 8 +-- clippy_lints/src/option_if_let_else.rs | 6 +-- clippy_lints/src/question_mark.rs | 17 ++---- clippy_lints/src/try_err.rs | 5 +- clippy_lints/src/unnecessary_wraps.rs | 11 ++-- clippy_lints/src/unused_io_amount.rs | 2 +- clippy_utils/src/lib.rs | 54 ++++++++----------- 17 files changed, 121 insertions(+), 157 deletions(-) diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 04fff237bb4c..ab22578abd67 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{path_to_local, SpanlessEq}; +use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq}; use if_chain::if_chain; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp}; +use rustc_hir::LangItem::OptionNone; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{DefIdTree, TyCtxt, TypeckResults}; +use rustc_middle::ty::TypeckResults; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -52,7 +52,7 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::Match(_expr, arms, _source) = expr.kind { - if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) { + if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { for arm in arms { check_arm(arm, wild_arm, cx); } @@ -75,7 +75,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext // match { .. } if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results())); // one of the branches must be "wild-like" - if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx)); + if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner)); let (wild_inner_arm, non_wild_inner_arm) = (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); if !pat_contains_or(non_wild_inner_arm.pat); @@ -126,13 +126,13 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> /// A "wild-like" pattern is wild ("_") or `None`. /// For this lint to apply, both the outer and inner match expressions /// must have "wild-like" branches that can be combined. -fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool { +fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if arm.guard.is_some() { return false; } match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, - PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true, + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, } } @@ -164,17 +164,6 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool { result } -fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool { - if let Some(none_id) = tcx.lang_items().option_none_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res { - if let Some(variant_id) = tcx.parent(id) { - return variant_id == none_id; - } - } - } - false -} - /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed. fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> { diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index ee16519692f9..f73b3a1fe67c 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr}; +use clippy_utils::{is_lang_ctor, meets_msrv, parent_node_is_if_expr}; use if_chain::if_chain; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -77,12 +78,12 @@ impl LateLintPass<'_> for IfThenSomeElseNone { if let Some(then_expr) = then_block.expr; if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; if let ExprKind::Path(ref then_call_qpath) = then_call.kind; - if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME); + if is_lang_ctor(cx, then_call_qpath, OptionSome); if let ExprKind::Block(els_block, _) = els.kind; if els_block.stmts.is_empty(); if let Some(els_expr) = els_block.expr; - if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; - if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE); + if let ExprKind::Path(ref qpath) = els_expr.kind; + if is_lang_ctor(cx, qpath, OptionNone); then { let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 94743cfcf465..64ff7574f86b 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,10 +1,11 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id}; +use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::source_map::Span; @@ -42,9 +43,9 @@ pub(super) fn check<'tcx>( if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; if path_to_local_id(match_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(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; - let some_ctor = is_some_ctor(cx, path.res); - let ok_ctor = is_ok_ctor(cx, path.res); + if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind; + let some_ctor = is_lang_ctor(cx, qpath, OptionSome); + let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); if some_ctor || ok_ctor; then { let if_let_type = if some_ctor { "Some" } else { "Ok" }; diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 8c9e3af62f48..29be07399774 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,9 +2,10 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; +use clippy_utils::{in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{ def::Res, intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, @@ -269,20 +270,9 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(QPath::Resolved(None, path)) - if path - .res - .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) => - { - Some(OptionPat::None) - }, - PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _) - if path - .res - .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) - && pat.span.ctxt() == ctxt => + PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -298,17 +288,11 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte match expr.kind { ExprKind::Call( Expr { - kind: ExprKind::Path(QPath::Resolved(None, path)), + kind: ExprKind::Path(ref qpath), .. }, [arg], - ) if ctxt == expr.span.ctxt() => { - if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) { - Some(arg) - } else { - None - } - }, + ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg), ExprKind::Block( Block { stmts: [], @@ -324,10 +308,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { match expr.kind { - ExprKind::Path(QPath::Resolved(None, path)) => path - .res - .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)), + ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), ExprKind::Block( Block { stmts: [], diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index 9bfae602c407..847c8c648b00 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -1,9 +1,10 @@ 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::{match_qpath, path_to_local_id, paths}; +use clippy_utils::{is_lang_ctor, 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}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; @@ -54,7 +55,7 @@ impl LateLintPass<'_> for ManualOkOr { let or_expr = &args[1]; if is_ok_wrapping(cx, &args[2]); if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; - if match_qpath(err_path, &paths::RESULT_ERR); + if is_lang_ctor(cx, err_path, ResultErr); if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span); if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); if let Some(indent) = indent_of(cx, scrutinee.span); @@ -81,7 +82,7 @@ impl LateLintPass<'_> for ManualOkOr { fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { if let ExprKind::Path(ref qpath) = map_expr.kind { - if match_qpath(qpath, &paths::RESULT_OK) { + if is_lang_ctor(cx, qpath, ResultOk) { return true; } } @@ -90,7 +91,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { let body = cx.tcx.hir().body(body_id); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; - if match_qpath(ok_path, &paths::RESULT_OK); + if is_lang_ctor(cx, ok_path, ResultOk); then { path_to_local_id(ok_arg, param_id) } else { false } } } diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index f296d6a1a15f..65baa2552ccc 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -3,10 +3,11 @@ 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::usage::contains_return_break_continue_macro; -use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg}; +use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::{Arm, Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -68,23 +69,21 @@ impl Case { } fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { + 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)| + if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { match arm.pat.kind { - PatKind::Path(ref some_qpath) => - match_qpath(some_qpath, &paths::OPTION_NONE), - PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => - match_qpath(err_qpath, &paths::RESULT_ERR), + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::TupleStruct(ref qpath, &[pat], _) => + matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), _ => false, } - ); + }); let unwrap_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; - if match_qpath(unwrap_qpath, &paths::OPTION_SOME) - || match_qpath(unwrap_qpath, &paths::RESULT_OK); + if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); if !contains_return_break_continue_macro(or_arm.body); @@ -106,7 +105,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { } else { None }; - if let Some(or_arm) = applicable_or_arm(match_arms); + if let Some(or_arm) = applicable_or_arm(cx, match_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(); diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index a892e24482b9..72f5eed6531f 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -7,7 +7,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::LocalUsedVisitor; use clippy_utils::{ - get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local, + get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs, }; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; @@ -15,6 +15,7 @@ use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{ self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind, @@ -1188,10 +1189,10 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() { - let arm_ref: Option = if is_none_arm(&arms[0]) { - is_ref_some_arm(&arms[1]) - } else if is_none_arm(&arms[1]) { - is_ref_some_arm(&arms[0]) + let arm_ref: Option = if is_none_arm(cx, &arms[0]) { + is_ref_some_arm(cx, &arms[1]) + } else if is_none_arm(cx, &arms[1]) { + is_ref_some_arm(cx, &arms[0]) } else { None }; @@ -1574,20 +1575,20 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool { } // Checks if arm has the form `None => None` -fn is_none_arm(arm: &Arm<'_>) -> bool { - matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE)) +fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) } // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) -fn is_ref_some_arm(arm: &Arm<'_>) -> Option { +fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if_chain! { - if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind; - if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME); + if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind; + if is_lang_ctor(cx, qpath, OptionSome); if let PatKind::Binding(rb, .., ident, _) = pats[0].kind; if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut; if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind; if let ExprKind::Path(ref some_path) = e.kind; - if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1; + if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1; if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { @@ -1699,10 +1700,11 @@ mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; - use clippy_utils::{is_trait_method, match_qpath, paths}; + use clippy_utils::{is_lang_ctor, is_trait_method, match_qpath, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; + use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -1734,13 +1736,13 @@ mod redundant_pattern_match { let good_method = match kind { PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { - if match_qpath(path, &paths::RESULT_OK) { + if is_lang_ctor(cx, path, ResultOk) { "is_ok()" - } else if match_qpath(path, &paths::RESULT_ERR) { + } else if is_lang_ctor(cx, path, ResultErr) { "is_err()" - } else if match_qpath(path, &paths::OPTION_SOME) { + } else if is_lang_ctor(cx, path, OptionSome) { "is_some()" - } else if match_qpath(path, &paths::POLL_READY) { + } else if is_lang_ctor(cx, path, PollReady) { "is_ready()" } else if match_qpath(path, &paths::IPADDR_V4) { "is_ipv4()" @@ -1754,9 +1756,9 @@ mod redundant_pattern_match { } }, PatKind::Path(ref path) => { - if match_qpath(path, &paths::OPTION_NONE) { + if is_lang_ctor(cx, path, OptionNone) { "is_none()" - } else if match_qpath(path, &paths::POLL_PENDING) { + } else if is_lang_ctor(cx, path, PollPending) { "is_pending()" } else { return; diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index e1d351aee454..1f521ec60909 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::is_diagnostic_assoc_item; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths}; +use clippy_utils::{in_macro, match_def_path, meets_msrv, paths}; +use clippy_utils::{is_diagnostic_assoc_item, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -102,7 +103,7 @@ impl_lint_pass!(MemReplace => fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { if let ExprKind::Path(ref replacement_qpath) = src.kind { // Check that second argument is `Option::None` - if match_qpath(replacement_qpath, &paths::OPTION_NONE) { + if is_lang_ctor(cx, replacement_qpath, OptionNone) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 013a6f90ac97..36a1c13d5be1 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_qpath, paths}; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -32,7 +33,7 @@ pub(super) fn check<'tcx>( let (lint_name, msg, instead, hint) = { let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind { - match_qpath(qpath, &paths::OPTION_NONE) + is_lang_ctor(cx, qpath, OptionNone) } else { return; }; @@ -43,7 +44,7 @@ pub(super) fn check<'tcx>( } let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind { - match_qpath(qpath, &paths::OPTION_SOME) + is_lang_ctor(cx, qpath, OptionSome) } else { false }; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 0f28bfdf09e8..66255b77331e 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::usage::mutated_variables; -use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; +use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::sym; @@ -54,7 +55,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc match &expr.kind { hir::ExprKind::Call(func, args) => { if let hir::ExprKind::Path(ref path) = func.kind { - if match_qpath(path, &paths::OPTION_SOME) { + if is_lang_ctor(cx, path, OptionSome) { if path_to_local_id(&args[0], arg_id) { return (false, false); } @@ -85,7 +86,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true), + hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true), _ => (true, true), } } diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 9852633b7342..cfe7ae6630e0 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv}; +use clippy_utils::{differing_macro_contexts, meets_msrv}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_semver::RustcVersion; @@ -159,8 +161,8 @@ fn is_some_or_ok_call<'a>( if_chain! { // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; - if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; - if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + if let ExprKind::Path(ref qpath) = &path.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); // Extract inner expression from ARGUMENT if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 1b9120ae45f5..e527adbb8929 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath}; +use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::OptionSome; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -164,7 +164,7 @@ fn detect_option_if_let_else<'tcx>( if arms.len() == 2; if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; - if match_qpath(struct_qpath, &paths::OPTION_SOME); + if is_lang_ctor(cx, struct_qpath, OptionSome); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue_macro(arms[0].body); if !contains_return_break_continue_macro(arms[1].body); diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 6d720f43851a..ea9ff37e13fd 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths}; +use clippy_utils::{eq_expr_value, match_qpath}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; +use rustc_hir::LangItem::OptionNone; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -156,15 +157,7 @@ impl QuestionMark { false }, ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr), - ExprKind::Path(ref qp) => { - if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) = - cx.qpath_res(qp, expression.hir_id) - { - return match_def_path(cx, def_id, &paths::OPTION_NONE); - } - - false - }, + ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, } } diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 23a1953fface..0b8c03c6865c 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths}; +use clippy_utils::{differing_macro_contexts, in_macro, is_lang_ctor, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { if let ExprKind::Call(err_fun, err_args) = try_arg.kind; if let Some(err_arg) = err_args.get(0); if let ExprKind::Path(ref err_fun_path) = err_fun.kind; - if match_qpath(err_fun_path, &paths::RESULT_ERR); + if is_lang_ctor(cx, err_fun_path, ResultErr); if let Some(return_ty) = find_return_type(cx, &expr.kind); then { let prefix; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 5bb417cb1be4..d2c0f60d2967 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions}; +use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; +use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -85,11 +86,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } // Get the wrapper and inner types, if can't, abort. - let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() { + let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() { if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) { - ("Option", &paths::OPTION_SOME, subst.type_at(0)) + ("Option", OptionSome, subst.type_at(0)) } else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) { - ("Result", &paths::RESULT_OK, subst.type_at(0)) + ("Result", ResultOk, subst.type_at(0)) } else { return; } @@ -107,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Get the Path of the function call. if let ExprKind::Path(ref qpath) = func.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. - if match_qpath(qpath, path); + if is_lang_ctor(cx, qpath, lang_item); if args.len() == 1; // Make sure the function argument does not contain a return expression. if !contains_return(&args[0]); diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 024ab03fd418..5e8e530f480f 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { }; match expr.kind { - hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => { + hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => { if let hir::ExprKind::Call(func, args) = res.kind { if matches!( func.kind, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 64f86f5f101e..1d61b7dd4128 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -56,9 +56,10 @@ use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, @@ -221,6 +222,19 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { } } +/// Checks if a `QPath` resolves to a constructor of a `LangItem`. +/// For example, use this to check whether a function call or a pattern is `Some(..)`. +pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool { + if let QPath::Resolved(_, path) = qpath { + if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res { + if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) { + return cx.tcx.parent(ctor_id) == Some(item_id); + } + } + } + false +} + /// Returns `true` if this `span` was expanded by any macro. #[must_use] pub fn in_macro(span: Span) -> bool { @@ -1010,11 +1024,11 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It /// Checks if a given expression is a match expression expanded from the `?` /// operator or the `try` macro. -pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - fn is_ok(arm: &Arm<'_>) -> bool { +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, ref pat, None) = arm.pat.kind; - if match_qpath(path, &paths::RESULT_OK[1..]); + if is_lang_ctor(cx, path, ResultOk); if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; if path_to_local_id(arm.body, hir_id); then { @@ -1024,9 +1038,9 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { false } - fn is_err(arm: &Arm<'_>) -> bool { + fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { - match_qpath(path, &paths::RESULT_ERR[1..]) + is_lang_ctor(cx, path, ResultErr) } else { false } @@ -1042,8 +1056,8 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if arms.len() == 2; if arms[0].guard.is_none(); if arms[1].guard.is_none(); - if (is_ok(&arms[0]) && is_err(&arms[1])) || - (is_ok(&arms[1]) && is_err(&arms[0])); + 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); } @@ -1456,27 +1470,3 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { } } } - -/// Check if the resolution of a given path is an `Ok` variant of `Result`. -pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == ok_id; - } - } - } - false -} - -/// Check if the resolution of a given path is a `Some` variant of `Option`. -pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == some_id; - } - } - } - false -}