Auto merge of #7333 - camsteffen:match-var, r=llogiq
Factor out match_var and get_pat_name utils ...because checking vars by name is bad, because of shadowing. changelog: none
This commit is contained in:
commit
f7d09b45e9
@ -1,15 +1,15 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::ty::match_type;
|
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 if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
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_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, UintTy};
|
use rustc_middle::ty::{self, UintTy};
|
||||||
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;
|
||||||
use rustc_span::Symbol;
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// **What it does:** Checks for naive byte counts
|
/// **What it does:** Checks for naive byte counts
|
||||||
@ -38,42 +38,43 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
|
|||||||
impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
if_chain! {
|
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.ident.name == sym!(count);
|
||||||
if count_args.len() == 1;
|
if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
|
||||||
if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind;
|
|
||||||
if filter.ident.name == sym!(filter);
|
if filter.ident.name == sym!(filter);
|
||||||
if filter_args.len() == 2;
|
if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
|
||||||
if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
|
|
||||||
let body = cx.tcx.hir().body(body_id);
|
let body = cx.tcx.hir().body(body_id);
|
||||||
if body.params.len() == 1;
|
if let [param] = body.params;
|
||||||
if let Some(argname) = get_pat_name(body.params[0].pat);
|
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
|
||||||
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
||||||
if op.node == BinOpKind::Eq;
|
if op.node == BinOpKind::Eq;
|
||||||
if match_type(cx,
|
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);
|
&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 {
|
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, _) =
|
let haystack = if let ExprKind::MethodCall(path, _, args, _) =
|
||||||
filter_args[0].kind {
|
filter_recv.kind {
|
||||||
let p = path.ident.name;
|
let p = path.ident.name;
|
||||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||||
&args[0]
|
&args[0]
|
||||||
} else {
|
} else {
|
||||||
&filter_args[0]
|
&filter_recv
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
&filter_args[0]
|
&filter_recv
|
||||||
};
|
};
|
||||||
let mut applicability = Applicability::MaybeIncorrect;
|
let mut applicability = Applicability::MaybeIncorrect;
|
||||||
span_lint_and_sugg(
|
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::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::visitors::LocalUsedVisitor;
|
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 if_chain::if_chain;
|
||||||
use rustc_hir::LangItem::OptionNone;
|
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_lint::{LateContext, LateLintPass};
|
||||||
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};
|
||||||
|
|
||||||
@ -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());
|
if arms_inner.iter().all(|arm| arm.guard.is_none());
|
||||||
// match expression must be a local binding
|
// match expression must be a local binding
|
||||||
// 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(peel_ref_operators(cx, expr_in));
|
||||||
// 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(cx, arm_inner));
|
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) =
|
||||||
@ -163,16 +162,3 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
|
|||||||
});
|
});
|
||||||
result
|
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::source::{snippet_with_applicability, snippet_with_context};
|
||||||
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
|
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
|
||||||
use clippy_utils::{
|
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_ast::util::parser::PREC_POSTFIX;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
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_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::{
|
use rustc_span::{sym, SyntaxContext};
|
||||||
symbol::{sym, Ident},
|
|
||||||
SyntaxContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// **What it does:** Checks for usages of `match` which could be implemented using `map`
|
/// **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()
|
scrutinee_str.into()
|
||||||
};
|
};
|
||||||
|
|
||||||
let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
|
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
|
||||||
match can_pass_as_func(cx, some_binding, some_expr) {
|
match can_pass_as_func(cx, id, some_expr) {
|
||||||
Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
|
Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
|
||||||
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
|
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)
|
&& !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
|
||||||
&& binding_ref.is_some()
|
&& 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.
|
// 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.
|
// 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 {
|
match expr.kind {
|
||||||
ExprKind::Call(func, [arg])
|
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)
|
Some(func)
|
||||||
},
|
},
|
||||||
|
@ -72,7 +72,7 @@ use rustc_hir::LangItem::{ResultErr, ResultOk};
|
|||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
|
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,
|
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_lint::{LateContext, Level, Lint, LintContext};
|
||||||
use rustc_middle::hir::exports::Export;
|
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))
|
.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> {
|
pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
|
||||||
match *path {
|
match *path {
|
||||||
QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
|
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 struct ContainsName {
|
||||||
pub name: Symbol,
|
pub name: Symbol,
|
||||||
pub result: bool,
|
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
|
/// Checks if an expression represents the identity function
|
||||||
/// Only examines closures and `std::convert::identity`
|
/// Only examines closures and `std::convert::identity`
|
||||||
pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
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:
|
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
|
||||||
/// * `|x| x`
|
/// * `|x| x`
|
||||||
/// * `|x| return x`
|
/// * `|x| return x`
|
||||||
/// * `|x| { return x }`
|
/// * `|x| { return x }`
|
||||||
/// * `|x| { return x; }`
|
/// * `|x| { return x; }`
|
||||||
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
|
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
|
||||||
let body = remove_blocks(&func.value);
|
let id = if_chain! {
|
||||||
|
if let [param] = func.params;
|
||||||
let value_pat = if let [value_param] = func.params {
|
if let PatKind::Binding(_, id, _, _) = param.pat.kind;
|
||||||
value_param.pat
|
then {
|
||||||
} else {
|
id
|
||||||
return false;
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match body.kind {
|
let mut expr = &func.value;
|
||||||
ExprKind::Path(QPath::Resolved(None, _)) => is_expr_pat_binding(cx, body, value_pat),
|
loop {
|
||||||
ExprKind::Ret(Some(ret_val)) => is_expr_pat_binding(cx, ret_val, value_pat),
|
match expr.kind {
|
||||||
ExprKind::Block(block, _) => {
|
#[rustfmt::skip]
|
||||||
if_chain! {
|
ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
|
||||||
if let &[block_stmt] = &block.stmts;
|
| ExprKind::Ret(Some(e)) => expr = e,
|
||||||
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block_stmt.kind;
|
#[rustfmt::skip]
|
||||||
if let ExprKind::Ret(Some(ret_val)) = expr.kind;
|
ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
|
||||||
then {
|
if_chain! {
|
||||||
is_expr_pat_binding(cx, ret_val, value_pat)
|
if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
|
||||||
} else {
|
if let ExprKind::Ret(Some(ret_val)) = e.kind;
|
||||||
false
|
then {
|
||||||
|
expr = ret_val;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
_ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
|
||||||
_ => false,
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1710,6 +1685,19 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
|
|||||||
(e, count)
|
(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_export]
|
||||||
macro_rules! unwrap_cargo_metadata {
|
macro_rules! unwrap_cargo_metadata {
|
||||||
($cx: ident, $lint: ident, $deps: expr) => {{
|
($cx: ident, $lint: ident, $deps: expr) => {{
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::source::snippet;
|
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::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_lint::LateContext;
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::Span;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub fn get_spans(
|
pub fn get_spans(
|
||||||
@ -14,10 +14,11 @@ pub fn get_spans(
|
|||||||
replacements: &[(&'static str, &'static str)],
|
replacements: &[(&'static str, &'static str)],
|
||||||
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
||||||
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
|
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
|
||||||
get_binding_name(&body.params[idx]).map_or_else(
|
if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind {
|
||||||
|| Some(vec![]),
|
extract_clone_suggestions(cx, binding_id, replacements, body)
|
||||||
|name| extract_clone_suggestions(cx, name, replacements, body),
|
} else {
|
||||||
)
|
Some(vec![])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(vec![])
|
Some(vec![])
|
||||||
}
|
}
|
||||||
@ -25,13 +26,13 @@ pub fn get_spans(
|
|||||||
|
|
||||||
fn extract_clone_suggestions<'tcx>(
|
fn extract_clone_suggestions<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
name: Symbol,
|
id: HirId,
|
||||||
replace: &[(&'static str, &'static str)],
|
replace: &[(&'static str, &'static str)],
|
||||||
body: &'tcx Body<'_>,
|
body: &'tcx Body<'_>,
|
||||||
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
||||||
let mut visitor = PtrCloneVisitor {
|
let mut visitor = PtrCloneVisitor {
|
||||||
cx,
|
cx,
|
||||||
name,
|
id,
|
||||||
replace,
|
replace,
|
||||||
spans: vec![],
|
spans: vec![],
|
||||||
abort: false,
|
abort: false,
|
||||||
@ -42,7 +43,7 @@ fn extract_clone_suggestions<'tcx>(
|
|||||||
|
|
||||||
struct PtrCloneVisitor<'a, 'tcx> {
|
struct PtrCloneVisitor<'a, 'tcx> {
|
||||||
cx: &'a LateContext<'tcx>,
|
cx: &'a LateContext<'tcx>,
|
||||||
name: Symbol,
|
id: HirId,
|
||||||
replace: &'a [(&'static str, &'static str)],
|
replace: &'a [(&'static str, &'static str)],
|
||||||
spans: Vec<(Span, Cow<'static, str>)>,
|
spans: Vec<(Span, Cow<'static, str>)>,
|
||||||
abort: bool,
|
abort: bool,
|
||||||
@ -55,16 +56,15 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
|
|||||||
if self.abort {
|
if self.abort {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let ExprKind::MethodCall(seg, _, args, _) = expr.kind {
|
if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
|
||||||
if args.len() == 1 && match_var(&args[0], self.name) {
|
if path_to_local_id(recv, self.id) {
|
||||||
if seg.ident.name.as_str() == "capacity" {
|
if seg.ident.name.as_str() == "capacity" {
|
||||||
self.abort = true;
|
self.abort = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for &(fn_name, suffix) in self.replace {
|
for &(fn_name, suffix) in self.replace {
|
||||||
if seg.ident.name.as_str() == fn_name {
|
if seg.ident.name.as_str() == fn_name {
|
||||||
self.spans
|
self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
|
||||||
.push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +77,3 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
|
|||||||
NestedVisitorMap::None
|
NestedVisitorMap::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> {
|
|
||||||
get_pat_name(arg.pat)
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user