Introduce is_lang_ctor

This commit is contained in:
Cameron Steffen 2021-04-02 17:09:58 -05:00
parent 624e8aad32
commit 7468542328
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::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 <local> { .. }
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> {

View File

@ -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(_, _, _)) {

View File

@ -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" };

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::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: [],

View File

@ -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 }
}
}

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::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();

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::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<BindingAnnotation> = 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<BindingAnnotation> = 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<BindingAnnotation> {
fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
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;

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::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

View File

@ -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
};

View File

@ -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),
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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,
}
}

View File

@ -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;

View File

@ -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]);

View File

@ -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,

View File

@ -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
}