X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=clippy_utils%2Fsrc%2Flib.rs;h=7f5a1bf9c0741707f0e4e34a66cb0fb6a6e199d7;hb=9fc4b92eb2831b5175a4720aff593252a716a2fa;hp=40d7963a5e545152d0e7ebb0be5e1b782509187f;hpb=588367b62ec0e6c30cb8d8783644775742da91fc;p=rust.git diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 40d7963a5e5..7f5a1bf9c07 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -18,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; @@ -25,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; @@ -70,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, @@ -520,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) @@ -557,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. @@ -644,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. @@ -774,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, @@ -1165,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 @@ -1185,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 { @@ -1200,8 +1256,7 @@ 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(( _, @@ -1735,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,