Skip to content

Commit 4241ce9

Browse files
committed
Suggest use .get_mut instead of &mut when occur HashMap and BtreeMap
Signed-off-by: xizheyin <[email protected]>
1 parent 5e66041 commit 4241ce9

File tree

2 files changed

+63
-4
lines changed

2 files changed

+63
-4
lines changed

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
11761176
self.body.source_info(location).span
11771177
}
11781178
});
1179+
1180+
let is_rhs_map_index = self.is_rhs_index_from_hashmap_or_btreemap(local);
11791181
match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
11801182
// on for loops, RHS points to the iterator part
11811183
Some(DesugaringKind::ForLoop) => {
@@ -1203,6 +1205,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12031205
decl_span,
12041206
opt_assignment_rhs_span,
12051207
opt_ty_info,
1208+
is_rhs_map_index,
12061209
)
12071210
} else {
12081211
match local_decl.local_info() {
@@ -1226,6 +1229,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12261229
decl_span,
12271230
opt_assignment_rhs_span,
12281231
opt_ty_info,
1232+
is_rhs_map_index,
12291233
),
12301234
}
12311235
}
@@ -1414,6 +1418,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
14141418
None => {}
14151419
}
14161420
}
1421+
1422+
/// check if the RHS is an overloaded index expression from hashmap or btreemap,
1423+
/// if so, suggest using .get_mut() instead of &mut, see issue #143732
1424+
/// For example
1425+
/// ```text
1426+
/// let mut map = HashMap::new();
1427+
/// let value = &map["key"];
1428+
/// ```
1429+
fn is_rhs_index_from_hashmap_or_btreemap(&self, local: Local) -> bool {
1430+
self.find_assignments(local)
1431+
.first()
1432+
.map(|&location| {
1433+
if let Some(mir::Statement {
1434+
source_info: _,
1435+
kind: mir::StatementKind::Assign(box (_, mir::Rvalue::Ref(_, _, place))),
1436+
..
1437+
}) = self.body[location.block].statements.get(location.statement_index)
1438+
&& let BorrowedContentSource::OverloadedIndex(ty) =
1439+
self.borrowed_content_source(place.as_ref())
1440+
&& let Some(def) = ty.ty_adt_def()
1441+
&& (self.infcx.tcx.is_diagnostic_item(sym::HashMap, def.did())
1442+
|| self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, def.did()))
1443+
{
1444+
true
1445+
} else {
1446+
false
1447+
}
1448+
})
1449+
.unwrap_or(false)
1450+
}
14171451
}
14181452

14191453
struct BindingFinder {
@@ -1507,6 +1541,7 @@ fn suggest_ampmut<'tcx>(
15071541
decl_span: Span,
15081542
opt_assignment_rhs_span: Option<Span>,
15091543
opt_ty_info: Option<Span>,
1544+
is_rhs_map_index: bool,
15101545
) -> Option<AmpMutSugg> {
15111546
// if there is a RHS and it starts with a `&` from it, then check if it is
15121547
// mutable, and if not, put suggest putting `mut ` to make it mutable.
@@ -1553,6 +1588,28 @@ fn suggest_ampmut<'tcx>(
15531588
// if the reference is already mutable then there is nothing we can do
15541589
// here.
15551590
if !is_mut {
1591+
// If this is an overloaded index expression, suggest using .get_mut() instead of &mut
1592+
if is_rhs_map_index {
1593+
// Try to extract the expression from &expr[key] to suggest expr.get_mut(key).unwrap()
1594+
let content = rhs_str.strip_prefix('&')?;
1595+
if content.contains('[') && content.contains(']') {
1596+
let bracket_start = content.find('[')?;
1597+
let bracket_end = content.rfind(']')?;
1598+
1599+
if bracket_start < bracket_end {
1600+
let map_part = &content[..bracket_start];
1601+
let key_part = &content[bracket_start + 1..bracket_end];
1602+
1603+
return Some(AmpMutSugg {
1604+
has_sugg: true,
1605+
span: rhs_span,
1606+
suggestion: format!("{}.get_mut({}).unwrap()", map_part, key_part),
1607+
additional: None,
1608+
});
1609+
}
1610+
}
1611+
}
1612+
15561613
// shrink the span to just after the `&` in `&variable`
15571614
let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo();
15581615

tests/ui/borrowck/suggest-use-as-mut-for-map.stderr

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ LL | string.push_str("test");
66
|
77
help: consider changing this to be a mutable reference
88
|
9-
LL | let string = &mut map[&0];
10-
| +++
9+
LL - let string = &map[&0];
10+
LL + let string = map.get_mut(&0).unwrap();
11+
|
1112

1213
error[E0596]: cannot borrow `*string` as mutable, as it is behind a `&` reference
1314
--> $DIR/suggest-use-as-mut-for-map.rs:14:5
@@ -17,8 +18,9 @@ LL | string.push_str("test");
1718
|
1819
help: consider changing this to be a mutable reference
1920
|
20-
LL | let string = &mut map[&0];
21-
| +++
21+
LL - let string = &map[&0];
22+
LL + let string = map.get_mut(&0).unwrap();
23+
|
2224

2325
error: aborting due to 2 previous errors
2426

0 commit comments

Comments
 (0)