Skip to content

Commit b448d7b

Browse files
committed
Suggest removal of borrow in index when appropriate
When encountering ```rust let a = std::collections::HashMap::<String,String>::new(); let s = "hello"; let _b = &a[&s]; ``` suggest `let _b = &a[s];`. Fix #66023.
1 parent d97bb19 commit b448d7b

File tree

6 files changed

+80
-5
lines changed

6 files changed

+80
-5
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+43-3
Original file line numberDiff line numberDiff line change
@@ -2877,7 +2877,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28772877
// two-phase not needed because index_ty is never mutable
28782878
self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
28792879
self.select_obligations_where_possible(|errors| {
2880-
self.point_at_index(errors, idx.span);
2880+
self.point_at_index(errors, idx, expr, base, base_t);
28812881
});
28822882
element_ty
28832883
}
@@ -3036,7 +3036,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30363036
.ok()
30373037
}
30383038

3039-
fn point_at_index(&self, errors: &mut Vec<traits::FulfillmentError<'tcx>>, span: Span) {
3039+
fn point_at_index(
3040+
&self,
3041+
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
3042+
idx: &'tcx hir::Expr<'tcx>,
3043+
expr: &'tcx hir::Expr<'tcx>,
3044+
base: &'tcx hir::Expr<'tcx>,
3045+
base_t: Ty<'tcx>,
3046+
) {
3047+
let idx_span = idx.span;
30403048
let mut seen_preds = FxHashSet::default();
30413049
// We re-sort here so that the outer most root obligations comes first, as we have the
30423050
// subsequent weird logic to identify *every* relevant obligation for proper deduplication
@@ -3060,7 +3068,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30603068
(root, pred) if seen_preds.contains(&pred) || seen_preds.contains(&root) => {}
30613069
_ => continue,
30623070
}
3063-
error.obligation.cause.span = span;
3071+
error.obligation.cause.span = idx_span;
3072+
3073+
// If the index value is a double borrow, that can cause typeck errors
3074+
// that can be easily resolved by removing the borrow from the expression.
3075+
// We check for that here and provide a suggestion in a custom obligation
3076+
// cause code.
3077+
if let hir::ExprKind::AddrOf(_, _, idx) = idx.kind {
3078+
let idx_t = self.typeck_results.borrow().expr_ty(idx);
3079+
if let Some((index_ty, _element_ty)) =
3080+
self.lookup_indexing(expr, base, base_t, idx, idx_t)
3081+
{
3082+
let (_ty, err) =
3083+
self.demand_coerce_diag(idx, idx_t, index_ty, None, AllowTwoPhase::No);
3084+
if let Some(err) = err {
3085+
err.cancel();
3086+
} else if self
3087+
.fulfillment_cx
3088+
.borrow_mut()
3089+
.select_where_possible(self)
3090+
.is_empty()
3091+
{
3092+
if let Some(pred) = error.obligation.predicate.to_opt_poly_trait_pred() {
3093+
error.obligation.cause =
3094+
error.obligation.cause.clone().derived_cause(pred, |cause| {
3095+
ObligationCauseCode::IndexExprDerivedObligation(Box::new((
3096+
cause,
3097+
idx_span.with_hi(idx.span.lo()),
3098+
)))
3099+
});
3100+
}
3101+
}
3102+
}
3103+
}
30643104
}
30653105
}
30663106

compiler/rustc_middle/src/traits/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ pub enum ObligationCauseCode<'tcx> {
334334

335335
DerivedObligation(DerivedObligationCause<'tcx>),
336336

337+
IndexExprDerivedObligation(Box<(DerivedObligationCause<'tcx>, Span)>),
338+
337339
FunctionArgumentObligation {
338340
/// The node of the relevant argument in the function call.
339341
arg_hir_id: hir::HirId,

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+19
Original file line numberDiff line numberDiff line change
@@ -3239,6 +3239,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
32393239
)
32403240
});
32413241
}
3242+
ObligationCauseCode::IndexExprDerivedObligation(ref data) => {
3243+
ensure_sufficient_stack(|| {
3244+
self.note_obligation_cause_code(
3245+
body_id,
3246+
err,
3247+
predicate,
3248+
param_env,
3249+
&data.0.parent_code,
3250+
obligated_types,
3251+
seen_requirements,
3252+
)
3253+
});
3254+
err.span_suggestion_verbose(
3255+
data.1,
3256+
"remove this borrow",
3257+
String::new(),
3258+
Applicability::MaybeIncorrect,
3259+
);
3260+
}
32423261
ObligationCauseCode::TypeAlias(ref nested, span, def_id) => {
32433262
// #74711: avoid a stack overflow
32443263
ensure_sufficient_stack(|| {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// run-rustfix
2+
fn main() {
3+
let a = std::collections::HashMap::<String,String>::new();
4+
let s = "hello";
5+
let _b = &a[
6+
s //~ ERROR E0277
7+
];
8+
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
// run-rustfix
12
fn main() {
23
let a = std::collections::HashMap::<String,String>::new();
34
let s = "hello";
4-
let _b = a[
5+
let _b = &a[
56
&s //~ ERROR E0277
67
];
78
}

tests/ui/indexing/point-at-index-for-obligation-failure.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
error[E0277]: the trait bound `String: Borrow<&str>` is not satisfied
2-
--> $DIR/point-at-index-for-obligation-failure.rs:5:9
2+
--> $DIR/point-at-index-for-obligation-failure.rs:6:9
33
|
44
LL | &s
55
| ^^ the trait `Borrow<&str>` is not implemented for `String`
66
|
77
= help: the trait `Borrow<str>` is implemented for `String`
88
= help: for that trait implementation, expected `str`, found `&str`
99
= note: required for `HashMap<String, String>` to implement `Index<&&str>`
10+
help: remove this borrow
11+
|
12+
LL - &s
13+
LL + s
14+
|
1015

1116
error: aborting due to previous error
1217

0 commit comments

Comments
 (0)