Factor out match_var
This commit is contained in:
parent
dd0246813f
commit
b792bb301c
@ -1,15 +1,15 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path};
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, UintTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for naive byte counts
|
||||
@ -38,42 +38,43 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
|
||||
impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind;
|
||||
if let ExprKind::MethodCall(count, _, [count_recv], _) = expr.kind;
|
||||
if count.ident.name == sym!(count);
|
||||
if count_args.len() == 1;
|
||||
if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind;
|
||||
if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
|
||||
if filter.ident.name == sym!(filter);
|
||||
if filter_args.len() == 2;
|
||||
if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
|
||||
if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
if body.params.len() == 1;
|
||||
if let Some(argname) = get_pat_name(body.params[0].pat);
|
||||
if let [param] = body.params;
|
||||
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
|
||||
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
||||
if op.node == BinOpKind::Eq;
|
||||
if match_type(cx,
|
||||
cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
|
||||
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
|
||||
&paths::SLICE_ITER);
|
||||
let operand_is_arg = |expr| {
|
||||
let expr = peel_ref_operators(cx, remove_blocks(expr));
|
||||
path_to_local_id(expr, arg_id)
|
||||
};
|
||||
let needle = if operand_is_arg(l) {
|
||||
r
|
||||
} else if operand_is_arg(r) {
|
||||
l
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
|
||||
if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
|
||||
then {
|
||||
let needle = match get_path_name(l) {
|
||||
Some(name) if check_arg(name, argname, r) => r,
|
||||
_ => match get_path_name(r) {
|
||||
Some(name) if check_arg(name, argname, l) => l,
|
||||
_ => { return; }
|
||||
}
|
||||
};
|
||||
if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
|
||||
return;
|
||||
}
|
||||
let haystack = if let ExprKind::MethodCall(path, _, args, _) =
|
||||
filter_args[0].kind {
|
||||
filter_recv.kind {
|
||||
let p = path.ident.name;
|
||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||
&args[0]
|
||||
} else {
|
||||
&filter_args[0]
|
||||
&filter_recv
|
||||
}
|
||||
} else {
|
||||
&filter_args[0]
|
||||
&filter_recv
|
||||
};
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
span_lint_and_sugg(
|
||||
@ -91,24 +92,3 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool {
|
||||
name == arg && !contains_name(name, needle)
|
||||
}
|
||||
|
||||
fn get_path_name(expr: &Expr<'_>) -> Option<Symbol> {
|
||||
match expr.kind {
|
||||
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Deref, e) => {
|
||||
get_path_name(e)
|
||||
},
|
||||
ExprKind::Block(b, _) => {
|
||||
if b.stmts.is_empty() {
|
||||
b.expr.as_ref().and_then(|p| get_path_name(p))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
|
||||
use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TypeckResults;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{MultiSpan, Span};
|
||||
|
||||
@ -73,7 +72,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
|
||||
if arms_inner.iter().all(|arm| arm.guard.is_none());
|
||||
// match expression must be a local binding
|
||||
// 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(peel_ref_operators(cx, expr_in));
|
||||
// one of the branches must be "wild-like"
|
||||
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
|
||||
let (wild_inner_arm, non_wild_inner_arm) =
|
||||
@ -163,16 +162,3 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
|
||||
});
|
||||
result
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
loop {
|
||||
match expr.kind {
|
||||
ExprKind::AddrOf(_, _, e) => expr = e,
|
||||
ExprKind::Unary(UnOp::Deref, e) if typeck_results.expr_ty(e).is_ref() => expr = e,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
@ -3,19 +3,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
|
||||
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, path_to_local_id,
|
||||
peel_hir_expr_refs,
|
||||
};
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{
|
||||
symbol::{sym, Ident},
|
||||
SyntaxContext,
|
||||
};
|
||||
use rustc_span::{sym, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usages of `match` which could be implemented using `map`
|
||||
@ -141,13 +139,13 @@ impl LateLintPass<'_> for ManualMap {
|
||||
scrutinee_str.into()
|
||||
};
|
||||
|
||||
let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
|
||||
match can_pass_as_func(cx, some_binding, some_expr) {
|
||||
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
|
||||
match can_pass_as_func(cx, id, some_expr) {
|
||||
Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
|
||||
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
|
||||
},
|
||||
_ => {
|
||||
if match_var(some_expr, some_binding.name)
|
||||
if path_to_local_id(some_expr, id)
|
||||
&& !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
|
||||
&& binding_ref.is_some()
|
||||
{
|
||||
@ -199,10 +197,10 @@ impl LateLintPass<'_> for ManualMap {
|
||||
|
||||
// Checks whether the expression could be passed as a function, or whether a closure is needed.
|
||||
// Returns the function to be passed to `map` if it exists.
|
||||
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(func, [arg])
|
||||
if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
|
||||
if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
|
||||
{
|
||||
Some(func)
|
||||
},
|
||||
|
@ -72,7 +72,7 @@ use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{
|
||||
def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
|
||||
ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
|
||||
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
|
||||
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc_middle::hir::exports::Export;
|
||||
@ -326,16 +326,6 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol)
|
||||
.map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
|
||||
}
|
||||
|
||||
/// Checks if an expression references a variable of the given name.
|
||||
pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
|
||||
if let [p] = path.segments {
|
||||
return p.ident.name == var;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
|
||||
match *path {
|
||||
QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
|
||||
@ -707,16 +697,6 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the name of a `Pat`, if any.
|
||||
pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
|
||||
match pat.kind {
|
||||
PatKind::Binding(.., ref spname, _) => Some(spname.name),
|
||||
PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
|
||||
PatKind::Box(p) | PatKind::Ref(p, _) => get_pat_name(&*p),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContainsName {
|
||||
pub name: Symbol,
|
||||
pub result: bool,
|
||||
@ -1404,47 +1384,42 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
/// Checks if an expression represents the identity function
|
||||
/// Only examines closures and `std::convert::identity`
|
||||
pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
/// Returns true if the expression is a binding to the given pattern
|
||||
fn is_expr_pat_binding(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
|
||||
if let PatKind::Binding(_, _, ident, _) = pat.kind {
|
||||
if match_var(expr, ident.name) {
|
||||
return !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr));
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
|
||||
/// * `|x| x`
|
||||
/// * `|x| return x`
|
||||
/// * `|x| { return x }`
|
||||
/// * `|x| { return x; }`
|
||||
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
|
||||
let body = remove_blocks(&func.value);
|
||||
|
||||
let value_pat = if let [value_param] = func.params {
|
||||
value_param.pat
|
||||
} else {
|
||||
return false;
|
||||
let id = if_chain! {
|
||||
if let [param] = func.params;
|
||||
if let PatKind::Binding(_, id, _, _) = param.pat.kind;
|
||||
then {
|
||||
id
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
match body.kind {
|
||||
ExprKind::Path(QPath::Resolved(None, _)) => is_expr_pat_binding(cx, body, value_pat),
|
||||
ExprKind::Ret(Some(ret_val)) => is_expr_pat_binding(cx, ret_val, value_pat),
|
||||
ExprKind::Block(block, _) => {
|
||||
if_chain! {
|
||||
if let &[block_stmt] = &block.stmts;
|
||||
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block_stmt.kind;
|
||||
if let ExprKind::Ret(Some(ret_val)) = expr.kind;
|
||||
then {
|
||||
is_expr_pat_binding(cx, ret_val, value_pat)
|
||||
} else {
|
||||
false
|
||||
let mut expr = &func.value;
|
||||
loop {
|
||||
match expr.kind {
|
||||
#[rustfmt::skip]
|
||||
ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
|
||||
| ExprKind::Ret(Some(e)) => expr = e,
|
||||
#[rustfmt::skip]
|
||||
ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
|
||||
if_chain! {
|
||||
if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
|
||||
if let ExprKind::Ret(Some(ret_val)) = e.kind;
|
||||
then {
|
||||
expr = ret_val;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
},
|
||||
_ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1710,6 +1685,19 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
|
||||
(e, count)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
|
||||
loop {
|
||||
match expr.kind {
|
||||
ExprKind::AddrOf(_, _, e) => expr = e,
|
||||
ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_cargo_metadata {
|
||||
($cx: ident, $lint: ident, $deps: expr) => {{
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::source::snippet;
|
||||
use crate::{get_pat_name, match_var};
|
||||
use crate::{path_to_local_id, strip_pat_refs};
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
|
||||
use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_span::Span;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub fn get_spans(
|
||||
@ -14,10 +14,11 @@ pub fn get_spans(
|
||||
replacements: &[(&'static str, &'static str)],
|
||||
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
||||
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
|
||||
get_binding_name(&body.params[idx]).map_or_else(
|
||||
|| Some(vec![]),
|
||||
|name| extract_clone_suggestions(cx, name, replacements, body),
|
||||
)
|
||||
if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind {
|
||||
extract_clone_suggestions(cx, binding_id, replacements, body)
|
||||
} else {
|
||||
Some(vec![])
|
||||
}
|
||||
} else {
|
||||
Some(vec![])
|
||||
}
|
||||
@ -25,13 +26,13 @@ pub fn get_spans(
|
||||
|
||||
fn extract_clone_suggestions<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
name: Symbol,
|
||||
id: HirId,
|
||||
replace: &[(&'static str, &'static str)],
|
||||
body: &'tcx Body<'_>,
|
||||
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
||||
let mut visitor = PtrCloneVisitor {
|
||||
cx,
|
||||
name,
|
||||
id,
|
||||
replace,
|
||||
spans: vec![],
|
||||
abort: false,
|
||||
@ -42,7 +43,7 @@ fn extract_clone_suggestions<'tcx>(
|
||||
|
||||
struct PtrCloneVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
name: Symbol,
|
||||
id: HirId,
|
||||
replace: &'a [(&'static str, &'static str)],
|
||||
spans: Vec<(Span, Cow<'static, str>)>,
|
||||
abort: bool,
|
||||
@ -55,16 +56,15 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
|
||||
if self.abort {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::MethodCall(seg, _, args, _) = expr.kind {
|
||||
if args.len() == 1 && match_var(&args[0], self.name) {
|
||||
if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
|
||||
if path_to_local_id(recv, self.id) {
|
||||
if seg.ident.name.as_str() == "capacity" {
|
||||
self.abort = true;
|
||||
return;
|
||||
}
|
||||
for &(fn_name, suffix) in self.replace {
|
||||
if seg.ident.name.as_str() == fn_name {
|
||||
self.spans
|
||||
.push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
|
||||
self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -77,7 +77,3 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> {
|
||||
get_pat_name(arg.pat)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user