diff options
| author | Cameron Steffen <cam.steffen94@gmail.com> | 2021-06-09 09:17:34 -0500 |
|---|---|---|
| committer | Cameron Steffen <cam.steffen94@gmail.com> | 2021-06-09 09:47:42 -0500 |
| commit | b792bb301c66e271d88d2e579ec79fec153b7b89 (patch) | |
| tree | 44247c2827821c4bc92f960749c781e195186af5 /clippy_utils/src | |
| parent | dd0246813f3f42a6cadc2109cdbda76300495981 (diff) | |
| download | rust-b792bb301c66e271d88d2e579ec79fec153b7b89.tar.gz rust-b792bb301c66e271d88d2e579ec79fec153b7b89.zip | |
Factor out match_var
Diffstat (limited to 'clippy_utils/src')
| -rw-r--r-- | clippy_utils/src/lib.rs | 94 | ||||
| -rw-r--r-- | clippy_utils/src/ptr.rs | 32 |
2 files changed, 55 insertions, 71 deletions
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d3efd32d727..66e75b0c206 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -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) => {{ diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs index 791688cd194..8adb6915952 100644 --- a/clippy_utils/src/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -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) -} |
