use rustc_errors::Applicability;
use rustc_hir::{
hir_id::HirIdSet,
- intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
- Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp,
+ intravisit::{walk_expr, Visitor},
+ Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
/// map.insert(k, v);
/// }
/// ```
- /// can both be rewritten as:
+ /// Use instead:
/// ```rust
/// # use std::collections::HashMap;
/// # let mut map = HashMap::new();
/// # let v = 1;
/// map.entry(k).or_insert(v);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MAP_ENTRY,
perf,
"use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
impl<'tcx> LateLintPass<'tcx> for HashMapPass {
- #[allow(clippy::too_many_lines)]
+ #[expect(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) {
Some(higher::If { cond, then, r#else }) => (cond, then, r#else),
),
};
format!(
- "if let {}::{} = {}.entry({}) {} else {}",
+ "if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}",
map_ty.entry_path(),
- entry_kind,
- map_str,
- key_str,
- then_str,
- else_str,
)
} else {
// if .. { insert } else { insert }
let indent_str = snippet_indent(cx, expr.span);
let indent_str = indent_str.as_deref().unwrap_or("");
format!(
- "match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\
- {indent} {entry}::{} => {}\n{indent}}}",
- map_str,
- key_str,
- then_entry,
+ "match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\
+ {indent_str} {entry}::{else_entry} => {}\n{indent_str}}}",
reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
- else_entry,
reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
entry = map_ty.entry_path(),
- indent = indent_str,
)
}
} else {
then_search.snippet_occupied(cx, then_expr.span, &mut app)
};
format!(
- "if let {}::{} = {}.entry({}) {}",
+ "if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}",
map_ty.entry_path(),
- entry_kind,
- map_str,
- key_str,
- body_str,
)
} else if let Some(insertion) = then_search.as_single_insertion() {
let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
if contains_expr.negated {
if insertion.value.can_have_side_effects() {
- format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str)
+ format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});")
} else {
- format!("{}.entry({}).or_insert({});", map_str, key_str, value_str)
+ format!("{map_str}.entry({key_str}).or_insert({value_str});")
}
} else {
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
} else {
let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
if contains_expr.negated {
- format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str)
+ format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});")
} else {
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
// This would need to be a different lint.
key: &'tcx Expr<'tcx>,
call_ctxt: SyntaxContext,
}
-fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
+fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
let mut negated = false;
let expr = peel_hir_expr_while(expr, |e| match e.kind {
ExprKind::Unary(UnOp::Not, e) => {
match expr.kind {
ExprKind::MethodCall(
_,
- _,
+ map,
[
- map,
Expr {
kind: ExprKind::AddrOf(_, _, key),
span: key_span,
key: &'tcx Expr<'tcx>,
value: &'tcx Expr<'tcx>,
}
-fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
- if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind {
+fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
+ if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind {
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
Some(InsertExpr { map, key, value })
/// An insertion into the map.
Insertion(Insertion<'tcx>),
}
-impl Edit<'tcx> {
+impl<'tcx> Edit<'tcx> {
fn as_insertion(self) -> Option<Insertion<'tcx>> {
if let Self::Insertion(i) = self { Some(i) } else { None }
}
/// `or_insert_with`.
/// * Determine if there's any sub-expression that can't be placed in a closure.
/// * Determine if there's only a single insert statement. `or_insert` can be used in this case.
-#[allow(clippy::struct_excessive_bools)]
+#[expect(clippy::struct_excessive_bools)]
struct InsertSearcher<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
/// The map expression used in the contains call.
}
}
impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
- type Map = ErasedMap<'tcx>;
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
- }
-
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
match stmt.kind {
StmtKind::Semi(e) => {
let mut is_map_used = self.is_map_used;
for arm in arms {
self.visit_pat(arm.pat);
- if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
+ if let Some(Guard::If(guard) | Guard::IfLet(&Let { init: guard, .. })) = arm.guard {
self.visit_non_tail_expr(guard);
}
is_map_used |= self.visit_cond_arm(arm.body);
self.loops.pop();
},
ExprKind::Block(block, _) => self.visit_block(block),
- ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => {
+ ExprKind::InlineAsm(_) => {
self.can_use_entry = false;
},
_ => {
allow_insert_closure: bool,
is_single_insert: bool,
}
-impl InsertSearchResults<'tcx> {
+impl<'tcx> InsertSearchResults<'tcx> {
fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
}
}
}
-fn find_insert_calls(
+fn find_insert_calls<'tcx>(
cx: &LateContext<'tcx>,
contains_expr: &ContainsExpr<'tcx>,
expr: &'tcx Expr<'_>,
let allow_insert_closure = s.allow_insert_closure;
let is_single_insert = s.is_single_insert;
let edits = s.edits;
- s.can_use_entry.then(|| InsertSearchResults {
+ s.can_use_entry.then_some(InsertSearchResults {
edits,
allow_insert_closure,
is_single_insert,