Auto merge of #7047 - camsteffen:lang-ctor, r=flip1995

Introduce `is_lang_ctor`

changelog: none

Replaces `is_some_ctor` and `is_ok_ctor`. Removes many path usages.
This commit is contained in:
bors
2021-04-12 08:52:10 +00:00
17 changed files with 121 additions and 157 deletions

View File

@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::visitors::LocalUsedVisitor; 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 if_chain::if_chain;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; 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_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{MultiSpan, Span}; use rustc_span::{MultiSpan, Span};
@@ -52,7 +52,7 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let ExprKind::Match(_expr, arms, _source) = expr.kind { 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 { for arm in arms {
check_arm(arm, wild_arm, cx); 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 <local> { .. } // match <local> { .. }
if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results())); if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
// one of the branches must be "wild-like" // 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) = let (wild_inner_arm, non_wild_inner_arm) =
(&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
if !pat_contains_or(non_wild_inner_arm.pat); 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`. /// A "wild-like" pattern is wild ("_") or `None`.
/// For this lint to apply, both the outer and inner match expressions /// For this lint to apply, both the outer and inner match expressions
/// must have "wild-like" branches that can be combined. /// 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() { if arm.guard.is_some() {
return false; return false;
} }
match arm.pat.kind { match arm.pat.kind {
PatKind::Binding(..) | PatKind::Wild => true, 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, _ => false,
} }
} }
@@ -164,17 +164,6 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
result 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 /// 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. /// 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> { fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {

View File

@@ -1,7 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet_with_macro_callsite; 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 if_chain::if_chain;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; 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 Some(then_expr) = then_block.expr;
if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
if let ExprKind::Path(ref then_call_qpath) = then_call.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 let ExprKind::Block(els_block, _) = els.kind;
if els_block.stmts.is_empty(); if els_block.stmts.is_empty();
if let Some(els_expr) = els_block.expr; if let Some(els_expr) = els_block.expr;
if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; if let ExprKind::Path(ref qpath) = els_expr.kind;
if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE); if is_lang_ctor(cx, qpath, OptionNone);
then { then {
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {

View File

@@ -1,10 +1,11 @@
use super::utils::make_iterator_snippet; use super::utils::make_iterator_snippet;
use super::MANUAL_FLATTEN; use super::MANUAL_FLATTEN;
use clippy_utils::diagnostics::span_lint_and_then; 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 if_chain::if_chain;
use rustc_errors::Applicability; 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_lint::LateContext;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_span::source_map::Span; 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 let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
if path_to_local_id(match_expr, pat_hir_id); 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` // 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; if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind;
let some_ctor = is_some_ctor(cx, path.res); let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
let ok_ctor = is_ok_ctor(cx, path.res); let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
if some_ctor || ok_ctor; if some_ctor || ok_ctor;
then { then {
let if_let_type = if some_ctor { "Some" } else { "Ok" }; let if_let_type = if some_ctor { "Some" } else { "Ok" };

View File

@@ -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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; 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::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_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{ use rustc_hir::{
def::Res, def::Res,
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, 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 { match pat.kind {
PatKind::Wild => Some(OptionPat::Wild), PatKind::Wild => Some(OptionPat::Wild),
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
PatKind::Path(QPath::Resolved(None, path)) PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
if path PatKind::TupleStruct(ref qpath, [pattern], _)
.res if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
.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 =>
{ {
Some(OptionPat::Some { pattern, ref_count }) 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 { match expr.kind {
ExprKind::Call( ExprKind::Call(
Expr { Expr {
kind: ExprKind::Path(QPath::Resolved(None, path)), kind: ExprKind::Path(ref qpath),
.. ..
}, },
[arg], [arg],
) if ctxt == expr.span.ctxt() => { ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg),
if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) {
Some(arg)
} else {
None
}
},
ExprKind::Block( ExprKind::Block(
Block { Block {
stmts: [], stmts: [],
@@ -324,10 +308,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
// Checks for the `None` value. // Checks for the `None` value.
fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
match expr.kind { match expr.kind {
ExprKind::Path(QPath::Resolved(None, path)) => path ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
.res
.opt_def_id()
.map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)),
ExprKind::Block( ExprKind::Block(
Block { Block {
stmts: [], stmts: [],

View File

@@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item; 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 if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{Expr, ExprKind, PatKind}; use rustc_hir::{Expr, ExprKind, PatKind};
use rustc_lint::LintContext; use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@@ -54,7 +55,7 @@ impl LateLintPass<'_> for ManualOkOr {
let or_expr = &args[1]; let or_expr = &args[1];
if is_ok_wrapping(cx, &args[2]); if is_ok_wrapping(cx, &args[2]);
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; 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(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
if let Some(indent) = indent_of(cx, scrutinee.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 { fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
if let ExprKind::Path(ref qpath) = map_expr.kind { 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; return true;
} }
} }
@@ -90,7 +91,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
let body = cx.tcx.hir().body(body_id); let body = cx.tcx.hir().body(body_id);
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; 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 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 } then { path_to_local_id(ok_arg, param_id) } else { false }
} }
} }

View File

@@ -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::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::contains_return_break_continue_macro; 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 if_chain::if_chain;
use rustc_errors::Applicability; 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::LintContext;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; 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 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_chain! {
if arms.len() == 2; if arms.len() == 2;
if arms.iter().all(|arm| arm.guard.is_none()); 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 { match arm.pat.kind {
PatKind::Path(ref some_qpath) => PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
match_qpath(some_qpath, &paths::OPTION_NONE), PatKind::TupleStruct(ref qpath, &[pat], _) =>
PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
match_qpath(err_qpath, &paths::RESULT_ERR),
_ => false, _ => false,
} }
); });
let unwrap_arm = &arms[1 - idx]; let unwrap_arm = &arms[1 - idx];
if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
if match_qpath(unwrap_qpath, &paths::OPTION_SOME) if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
|| match_qpath(unwrap_qpath, &paths::RESULT_OK);
if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
if path_to_local_id(unwrap_arm.body, binding_hir_id); if path_to_local_id(unwrap_arm.body, binding_hir_id);
if !contains_return_break_continue_macro(or_arm.body); 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 { } else {
None 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(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
if let Some(indent) = indent_of(cx, expr.span); if let Some(indent) = indent_of(cx, expr.span);
if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();

View File

@@ -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::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
use clippy_utils::visitors::LocalUsedVisitor; use clippy_utils::visitors::LocalUsedVisitor;
use clippy_utils::{ 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, 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}; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
@@ -15,6 +15,7 @@ use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{ use rustc_hir::{
self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind, Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
@@ -1189,10 +1190,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<'_>) { 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() { if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) { let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
is_ref_some_arm(&arms[1]) is_ref_some_arm(cx, &arms[1])
} else if is_none_arm(&arms[1]) { } else if is_none_arm(cx, &arms[1]) {
is_ref_some_arm(&arms[0]) is_ref_some_arm(cx, &arms[0])
} else { } else {
None None
}; };
@@ -1575,20 +1576,20 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool {
} }
// Checks if arm has the form `None => None` // Checks if arm has the form `None => None`
fn is_none_arm(arm: &Arm<'_>) -> bool { fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE)) 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`) // 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<BindingAnnotation> { fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
if_chain! { if_chain! {
if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind; if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind;
if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME); if is_lang_ctor(cx, qpath, OptionSome);
if let PatKind::Binding(rb, .., ident, _) = pats[0].kind; if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut; if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind; if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
if let ExprKind::Path(ref some_path) = e.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 let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
then { then {
@@ -1700,10 +1701,11 @@ mod redundant_pattern_match {
use super::REDUNDANT_PATTERN_MATCHING; use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet; 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 if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; 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_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::sym; use rustc_span::sym;
@@ -1735,13 +1737,13 @@ mod redundant_pattern_match {
let good_method = match kind { let good_method = match kind {
PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => { PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => {
if let PatKind::Wild = patterns[0].kind { if let PatKind::Wild = patterns[0].kind {
if match_qpath(path, &paths::RESULT_OK) { if is_lang_ctor(cx, path, ResultOk) {
"is_ok()" "is_ok()"
} else if match_qpath(path, &paths::RESULT_ERR) { } else if is_lang_ctor(cx, path, ResultErr) {
"is_err()" "is_err()"
} else if match_qpath(path, &paths::OPTION_SOME) { } else if is_lang_ctor(cx, path, OptionSome) {
"is_some()" "is_some()"
} else if match_qpath(path, &paths::POLL_READY) { } else if is_lang_ctor(cx, path, PollReady) {
"is_ready()" "is_ready()"
} else if match_qpath(path, &paths::IPADDR_V4) { } else if match_qpath(path, &paths::IPADDR_V4) {
"is_ipv4()" "is_ipv4()"
@@ -1755,9 +1757,9 @@ mod redundant_pattern_match {
} }
}, },
PatKind::Path(ref path) => { PatKind::Path(ref path) => {
if match_qpath(path, &paths::OPTION_NONE) { if is_lang_ctor(cx, path, OptionNone) {
"is_none()" "is_none()"
} else if match_qpath(path, &paths::POLL_PENDING) { } else if is_lang_ctor(cx, path, PollPending) {
"is_pending()" "is_pending()"
} else { } else {
return; return;

View File

@@ -1,10 +1,11 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; 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::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 if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; 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) { fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
if let ExprKind::Path(ref replacement_qpath) = src.kind { if let ExprKind::Path(ref replacement_qpath) = src.kind {
// Check that second argument is `Option::None` // 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), // Since this is a late pass (already type-checked),
// and we already know that the second argument is an // and we already know that the second argument is an
// `Option`, we do not need to check the first // `Option`, we do not need to check the first

View File

@@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{match_qpath, paths};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@@ -32,7 +33,7 @@ pub(super) fn check<'tcx>(
let (lint_name, msg, instead, hint) = { let (lint_name, msg, instead, hint) = {
let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind { 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 { } else {
return; 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 { 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 { } else {
false false
}; };

View File

@@ -1,8 +1,9 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::usage::mutated_variables; 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 as hir;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_span::sym; use rustc_span::sym;
@@ -54,7 +55,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
match &expr.kind { match &expr.kind {
hir::ExprKind::Call(func, args) => { hir::ExprKind::Call(func, args) => {
if let hir::ExprKind::Path(ref path) = func.kind { 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) { if path_to_local_id(&args[0], arg_id) {
return (false, false); 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); let else_check = check_expression(cx, arg_id, else_arm);
(if_check.0 | else_check.0, if_check.1 | else_check.1) (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), _ => (true, true),
} }
} }

View File

@@ -1,9 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item; 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 if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionSome, ResultOk};
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
@@ -159,8 +161,8 @@ fn is_some_or_ok_call<'a>(
if_chain! { if_chain! {
// Check outer expression matches CALL_IDENT(ARGUMENT) format // Check outer expression matches CALL_IDENT(ARGUMENT) format
if let ExprKind::Call(path, args) = &expr.kind; if let ExprKind::Call(path, args) = &expr.kind;
if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; if let ExprKind::Path(ref qpath) = &path.kind;
if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
// Extract inner expression from ARGUMENT // Extract inner expression from ARGUMENT
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;

View File

@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::contains_return_break_continue_macro; 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 if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionSome;
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; 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 arms.len() == 2;
if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already 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 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 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[0].body);
if !contains_return_break_continue_macro(arms[1].body); if !contains_return_break_continue_macro(arms[1].body);

View File

@@ -1,12 +1,13 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item; 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 if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::OptionNone;
use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym; use rustc_span::sym;
@@ -156,15 +157,7 @@ impl QuestionMark {
false false
}, },
ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr), ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
ExprKind::Path(ref qp) => { ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
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
},
_ => false, _ => false,
} }
} }

View File

@@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::source::{snippet, snippet_with_macro_callsite};
use clippy_utils::ty::is_type_diagnostic_item; 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 if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::ResultErr;
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; 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 ExprKind::Call(err_fun, err_args) = try_arg.kind;
if let Some(err_arg) = err_args.get(0); if let Some(err_arg) = err_args.get(0);
if let ExprKind::Path(ref err_fun_path) = err_fun.kind; 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); if let Some(return_ty) = find_return_type(cx, &expr.kind);
then { then {
let prefix; let prefix;

View File

@@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet; 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 if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::LangItem::{OptionSome, ResultOk};
use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
@@ -85,11 +86,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
} }
// Get the wrapper and inner types, if can't, abort. // 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) { 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) { } 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 { } else {
return; return;
} }
@@ -107,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
// Get the Path of the function call. // Get the Path of the function call.
if let ExprKind::Path(ref qpath) = func.kind; if let ExprKind::Path(ref qpath) = func.kind;
// Check if OPTION_SOME or RESULT_OK, depending on return type. // 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; if args.len() == 1;
// Make sure the function argument does not contain a return expression. // Make sure the function argument does not contain a return expression.
if !contains_return(&args[0]); if !contains_return(&args[0]);

View File

@@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
}; };
match expr.kind { 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 let hir::ExprKind::Call(func, args) = res.kind {
if matches!( if matches!(
func.kind, func.kind,

View File

@@ -57,9 +57,10 @@ use if_chain::if_chain;
use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir; 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::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{ use rustc_hir::{
def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, 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, ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath,
@@ -222,6 +223,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. /// Returns `true` if this `span` was expanded by any macro.
#[must_use] #[must_use]
pub fn in_macro(span: Span) -> bool { pub fn in_macro(span: Span) -> bool {
@@ -1011,11 +1025,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 `?` /// Checks if a given expression is a match expression expanded from the `?`
/// operator or the `try` macro. /// operator or the `try` macro.
pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
fn is_ok(arm: &Arm<'_>) -> bool { fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
if_chain! { if_chain! {
if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind; 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 let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
if path_to_local_id(arm.body, hir_id); if path_to_local_id(arm.body, hir_id);
then { then {
@@ -1025,9 +1039,9 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
false false
} }
fn is_err(arm: &Arm<'_>) -> bool { fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
match_qpath(path, &paths::RESULT_ERR[1..]) is_lang_ctor(cx, path, ResultErr)
} else { } else {
false false
} }
@@ -1043,8 +1057,8 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
if arms.len() == 2; if arms.len() == 2;
if arms[0].guard.is_none(); if arms[0].guard.is_none();
if arms[1].guard.is_none(); if arms[1].guard.is_none();
if (is_ok(&arms[0]) && is_err(&arms[1])) || if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
(is_ok(&arms[1]) && is_err(&arms[0])); (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
then { then {
return Some(expr); return Some(expr);
} }
@@ -1450,27 +1464,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
}