Skip to content

Commit

Permalink
Minor cleanup of map_entry and a few additional tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarcho committed Apr 15, 2021
1 parent 3323ff7 commit bcf3488
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 220 deletions.
187 changes: 95 additions & 92 deletions clippy_lints/src/entry.rs

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions clippy_lints/src/manual_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
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};
use clippy_utils::{
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, 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, QPath,
};
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, 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};
Expand Down
59 changes: 14 additions & 45 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visito
use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{
def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
ImplItem, ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath,
Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment,
QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::exports::Export;
Expand Down Expand Up @@ -1312,48 +1312,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
}

pub fn get_expr_use_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
let map = tcx.hir();
let mut child_id = expr.hir_id;
let mut iter = map.parent_iter(child_id);
loop {
match iter.next() {
None => break None,
Some((id, Node::Block(_))) => child_id = id,
Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
Some((_, Node::Expr(expr))) => match expr.kind {
ExprKind::Break(
Destination {
target_id: Ok(dest), ..
},
_,
) => {
iter = map.parent_iter(dest);
child_id = dest;
},
ExprKind::DropTemps(_) | ExprKind::Block(..) => child_id = expr.hir_id,
ExprKind::If(control_expr, ..) | ExprKind::Match(control_expr, ..)
if control_expr.hir_id != child_id =>
{
child_id = expr.hir_id
},
_ => break Some(Node::Expr(expr)),
},
Some((_, node)) => break Some(node),
}
}
}

pub fn is_expr_used(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
!matches!(
get_expr_use_node(tcx, expr),
Some(Node::Stmt(Stmt {
kind: StmtKind::Expr(_) | StmtKind::Semi(_),
..
}))
)
}

/// 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<Node<'tcx>> {
let map = tcx.hir();
let mut child_id = expr.hir_id;
Expand All @@ -1374,16 +1333,26 @@ pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> O
}
}

/// Checks if the result of an expression is used, or it's type is unified with another branch.
pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
!matches!(
get_expr_use_or_unification_node(tcx, expr),
None | Some(Node::Stmt(Stmt {
kind: StmtKind::Expr(_) | StmtKind::Semi(_),
kind: StmtKind::Expr(_)
| StmtKind::Semi(_)
| StmtKind::Local(Local {
pat: Pat {
kind: PatKind::Wild,
..
},
..
}),
..
}))
)
}

/// Checks if the expression is the final expression returned from a block.
pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
}
Expand Down
58 changes: 53 additions & 5 deletions tests/ui/entry.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
#![warn(clippy::map_entry)]
#![feature(asm)]

use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
Expand All @@ -10,11 +11,19 @@ macro_rules! m {
($e:expr) => {{ $e }};
}

macro_rules! insert {
($map:expr, $key:expr, $val:expr) => {
$map.insert($key, $val)
};
}

fn foo() {}

fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
// or_insert(v)
m.entry(k).or_insert(v);

// semicolon on insert, use or_insert_with(..)
m.entry(k).or_insert_with(|| {
if true {
v
Expand All @@ -23,6 +32,7 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
}
});

// semicolon on if, use or_insert_with(..)
m.entry(k).or_insert_with(|| {
if true {
v
Expand All @@ -31,6 +41,7 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
}
});

// early return, use if let
if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
if true {
e.insert(v);
Expand All @@ -40,11 +51,13 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
}
}

// use or_insert_with(..)
m.entry(k).or_insert_with(|| {
foo();
v
});

// semicolon on insert and match, use or_insert_with(..)
m.entry(k).or_insert_with(|| {
match 0 {
1 if true => {
Expand All @@ -56,18 +69,17 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
}
});

// one branch doesn't insert, use if let
if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
match 0 {
0 => {},
1 => {
e.insert(v);
},
0 => foo(),
_ => {
e.insert(v2);
},
};
}

// use or_insert_with
m.entry(k).or_insert_with(|| {
foo();
match 0 {
Expand All @@ -94,10 +106,46 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
}
});

// ok, insert in loop
if !m.contains_key(&k) {
for _ in 0..2 {
m.insert(k, v);
}
}

// macro_expansion test, use or_insert(..)
m.entry(m!(k)).or_insert_with(|| m!(v));

// ok, map used before insertion
if !m.contains_key(&k) {
let _ = m.len();
m.insert(k, v);
}

// ok, inline asm
if !m.contains_key(&k) {
unsafe { asm!("nop") }
m.insert(k, v);
}

// ok, different keys.
if !m.contains_key(&k) {
m.insert(k2, v);
}

// ok, different maps
if !m.contains_key(&k) {
m2.insert(k, v);
}

// ok, insert in macro
if !m.contains_key(&k) {
insert!(m, k, v);
}
}

fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
// insert then do something, use if let
if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
e.insert(v);
foo();
Expand Down
58 changes: 53 additions & 5 deletions tests/ui/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
#![warn(clippy::map_entry)]
#![feature(asm)]

use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
Expand All @@ -10,13 +11,21 @@ macro_rules! m {
($e:expr) => {{ $e }};
}

macro_rules! insert {
($map:expr, $key:expr, $val:expr) => {
$map.insert($key, $val)
};
}

fn foo() {}

fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
// or_insert(v)
if !m.contains_key(&k) {
m.insert(k, v);
}

// semicolon on insert, use or_insert_with(..)
if !m.contains_key(&k) {
if true {
m.insert(k, v);
Expand All @@ -25,6 +34,7 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
}
}

// semicolon on if, use or_insert_with(..)
if !m.contains_key(&k) {
if true {
m.insert(k, v)
Expand All @@ -33,6 +43,7 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
};
}

// early return, use if let
if !m.contains_key(&k) {
if true {
m.insert(k, v);
Expand All @@ -42,11 +53,13 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
}
}

// use or_insert_with(..)
if !m.contains_key(&k) {
foo();
m.insert(k, v);
}

// semicolon on insert and match, use or_insert_with(..)
if !m.contains_key(&k) {
match 0 {
1 if true => {
Expand All @@ -58,18 +71,17 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
};
}

// one branch doesn't insert, use if let
if !m.contains_key(&k) {
match 0 {
0 => {},
1 => {
m.insert(k, v);
},
0 => foo(),
_ => {
m.insert(k, v2);
},
};
}

// use or_insert_with
if !m.contains_key(&k) {
foo();
match 0 {
Expand All @@ -96,12 +108,48 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2:
}
}

// ok, insert in loop
if !m.contains_key(&k) {
for _ in 0..2 {
m.insert(k, v);
}
}

// macro_expansion test, use or_insert(..)
if !m.contains_key(&m!(k)) {
m.insert(m!(k), m!(v));
}

// ok, map used before insertion
if !m.contains_key(&k) {
let _ = m.len();
m.insert(k, v);
}

// ok, inline asm
if !m.contains_key(&k) {
unsafe { asm!("nop") }
m.insert(k, v);
}

// ok, different keys.
if !m.contains_key(&k) {
m.insert(k2, v);
}

// ok, different maps
if !m.contains_key(&k) {
m2.insert(k, v);
}

// ok, insert in macro
if !m.contains_key(&k) {
insert!(m, k, v);
}
}

fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
// insert then do something, use if let
if !m.contains_key(&k) {
m.insert(k, v);
foo();
Expand Down
Loading

0 comments on commit bcf3488

Please sign in to comment.