X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=clippy_utils%2Fsrc%2Flib.rs;h=7f5a1bf9c0741707f0e4e34a66cb0fb6a6e199d7;hb=9fc4b92eb2831b5175a4720aff593252a716a2fa;hp=bd229402f418203b67e9845c5617450566d6043f;hpb=f0444d73def161f7f0fda7b3f5a13e9908e9c550;p=rust.git diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index bd229402f41..7f5a1bf9c07 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2,6 +2,7 @@ #![feature(in_band_lifetimes)] #![feature(iter_zip)] #![feature(rustc_private)] +#![feature(control_flow_enum)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] @@ -17,6 +18,7 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; +extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; @@ -24,7 +26,6 @@ extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_mir; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; @@ -69,7 +70,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::LangItem::{ResultErr, ResultOk}; +use rustc_hir::LangItem::{OptionNone, 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, Mutability, Node, Param, Pat, @@ -258,6 +259,20 @@ pub fn in_macro(span: Span) -> bool { } } +pub fn is_unit_expr(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Block( + Block { + stmts: [], + expr: None, + .. + }, + _ + ) | ExprKind::Tup([]) + ) +} + /// Checks if given pattern is a wildcard (`_`) pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) @@ -505,7 +520,7 @@ macro_rules! try_res { } }; } - fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> { + fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> { tcx.item_children(def_id) .iter() .find(|item| item.ident.name.as_str() == name) @@ -542,7 +557,7 @@ fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Opt None } }); - try_res!(last).res + try_res!(last).res.expect_non_local() } /// Convenience function to get the `DefId` of a trait by path. @@ -587,7 +602,7 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio /// For example, if `e` represents the `v[0].a.b[x]` /// this method will return a tuple, composed of a `Vec` /// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]` -/// and a `Expr` for root of them, `v` +/// and an `Expr` for root of them, `v` fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) { let mut result = vec![]; let root = loop { @@ -629,6 +644,65 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - false } +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent" +/// constructor from the std library +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { + let std_types_symbols = &[ + sym::string_type, + sym::vec_type, + sym::vecdeque_type, + sym::LinkedList, + sym::hashmap_type, + sym::BTreeMap, + sym::hashset_type, + sym::BTreeSet, + sym::BinaryHeap, + ]; + + if let QPath::TypeRelative(_, method) = path { + if method.ident.name == sym::new { + if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { + if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { + return std_types_symbols + .iter() + .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did)); + } + } + } + } + false +} + +/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated. +/// It doesn't cover all cases, for example indirect function calls (some of std +/// functions are supported) but it is the best we have. +pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + match &e.kind { + ExprKind::Lit(lit) => match lit.node { + LitKind::Bool(false) | LitKind::Int(0, _) => true, + LitKind::Str(s, _) => s.is_empty(), + _ => false, + }, + ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)), + ExprKind::Repeat(x, _) => is_default_equivalent(cx, x), + ExprKind::Call(repl_func, _) => if_chain! { + if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; + if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); + if is_diag_trait_item(cx, repl_def_id, sym::Default) + || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); + then { + true + } + else { + false + } + }, + ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone), + ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), + _ => false, + } +} + /// Checks if the top level expression can be moved into a closure as is. /// Currently checks for: /// * Break/Continue outside the given loop HIR ids. @@ -709,6 +783,11 @@ pub enum CaptureKind { Value, Ref(Mutability), } +impl CaptureKind { + pub fn is_imm_ref(self) -> bool { + self == Self::Ref(Mutability::Not) + } +} impl std::ops::BitOr for CaptureKind { type Output = Self; fn bitor(self, rhs: Self) -> Self::Output { @@ -754,12 +833,11 @@ fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind { ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. })) )); - let map = cx.tcx.hir(); let mut child_id = e.hir_id; let mut capture = CaptureKind::Value; let mut capture_expr_ty = e; - for (parent_id, parent) in map.parent_iter(e.hir_id) { + for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) { if let [Adjustment { kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)), target, @@ -788,6 +866,13 @@ fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind { capture_expr_ty = e; } }, + ExprKind::Let(pat, ..) => { + let mutability = match pat_capture_kind(cx, pat) { + CaptureKind::Value => Mutability::Not, + CaptureKind::Ref(m) => m, + }; + return CaptureKind::Ref(mutability); + }, ExprKind::Match(_, arms, _) => { let mut mutability = Mutability::Not; for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) { @@ -1092,7 +1177,7 @@ fn line_span(cx: &T, span: Span) -> Span { let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); let line_no = source_map_and_line.line; let line_start = source_map_and_line.sf.lines[line_no]; - Span::new(line_start, span.hi(), span.ctxt()) + span.with_lo(line_start) } /// Gets the parent node, if any. @@ -1138,8 +1223,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio /// Gets the loop or closure enclosing the given expression, if any. pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - let map = tcx.hir(); - for (_, node) in map.parent_iter(expr.hir_id) { + for (_, node) in tcx.hir().parent_iter(expr.hir_id) { match node { Node::Expr( e @@ -1158,8 +1242,7 @@ pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Opti /// Gets the parent node if it's an impl block. pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { - let map = tcx.hir(); - match map.parent_iter(id).next() { + match tcx.hir().parent_iter(id).next() { Some(( _, Node::Item(Item { @@ -1173,20 +1256,8 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { /// Checks if the given expression is the else clause of either an `if` or `if let` expression. pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { - let map = tcx.hir(); - let mut iter = map.parent_iter(expr.hir_id); + let mut iter = tcx.hir().parent_iter(expr.hir_id); match iter.next() { - Some((arm_id, Node::Arm(..))) => matches!( - iter.next(), - Some(( - _, - Node::Expr(Expr { - kind: ExprKind::Match(_, [_, else_arm], MatchSource::IfLetDesugar { .. }), - .. - }) - )) - if else_arm.hir_id == arm_id - ), Some(( _, Node::Expr(Expr { @@ -1585,15 +1656,15 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, let mut conds = Vec::new(); let mut blocks: Vec<&Block<'_>> = Vec::new(); - while let ExprKind::If(cond, then_expr, ref else_expr) = expr.kind { - conds.push(cond); - if let ExprKind::Block(block, _) = then_expr.kind { + while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) { + conds.push(&*cond); + if let ExprKind::Block(block, _) = then.kind { blocks.push(block); } else { panic!("ExprKind::If node is not an ExprKind::Block"); } - if let Some(else_expr) = *else_expr { + if let Some(else_expr) = r#else { expr = else_expr; } else { break; @@ -1719,9 +1790,8 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { /// Gets the node where an expression is either used, or it's type is unified with another branch. pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { - let map = tcx.hir(); let mut child_id = expr.hir_id; - let mut iter = map.parent_iter(child_id); + let mut iter = tcx.hir().parent_iter(child_id); loop { match iter.next() { None => break None, @@ -1803,7 +1873,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { .predicates_of(did) .predicates .iter() - .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); + .filter_map(|(p, _)| if p.is_global(cx.tcx) { Some(*p) } else { None }); traits::impossible_predicates( cx.tcx, traits::elaborate_predicates(cx.tcx, predicates) @@ -1849,7 +1919,7 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option return Some("slice".into()), rustc_ty::Array(..) => return Some("array".into()), rustc_ty::Tuple(..) => return Some("tuple".into()), @@ -1857,7 +1927,7 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option