Skip to content

Commit 65c17ae

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 rust-lang#66023.
1 parent 9cf18e9 commit 65c17ae

File tree

6 files changed

+80
-5
lines changed

6 files changed

+80
-5
lines changed

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2958,7 +2958,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
29582958
// two-phase not needed because index_ty is never mutable
29592959
self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
29602960
self.select_obligations_where_possible(|errors| {
2961-
self.point_at_index(errors, idx.span);
2961+
self.point_at_index(errors, idx, expr, base, base_t);
29622962
});
29632963
element_ty
29642964
}
@@ -3131,7 +3131,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31313131
.ok()
31323132
}
31333133

3134-
fn point_at_index(&self, errors: &mut Vec<traits::FulfillmentError<'tcx>>, span: Span) {
3134+
fn point_at_index(
3135+
&self,
3136+
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
3137+
idx: &'tcx hir::Expr<'tcx>,
3138+
expr: &'tcx hir::Expr<'tcx>,
3139+
base: &'tcx hir::Expr<'tcx>,
3140+
base_t: Ty<'tcx>,
3141+
) {
3142+
let idx_span = idx.span;
31353143
let mut seen_preds = FxHashSet::default();
31363144
// We re-sort here so that the outer most root obligations comes first, as we have the
31373145
// subsequent weird logic to identify *every* relevant obligation for proper deduplication
@@ -3155,7 +3163,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31553163
(root, pred) if seen_preds.contains(&pred) || seen_preds.contains(&root) => {}
31563164
_ => continue,
31573165
}
3158-
error.obligation.cause.span = span;
3166+
error.obligation.cause.span = idx_span;
3167+
3168+
// If the index value is a double borrow, that can cause typeck errors
3169+
// that can be easily resolved by removing the borrow from the expression.
3170+
// We check for that here and provide a suggestion in a custom obligation
3171+
// cause code.
3172+
if let hir::ExprKind::AddrOf(_, _, idx) = idx.kind {
3173+
let idx_t = self.typeck_results.borrow().expr_ty(idx);
3174+
if let Some((index_ty, _element_ty)) =
3175+
self.lookup_indexing(expr, base, base_t, idx, idx_t)
3176+
{
3177+
let (_ty, err) =
3178+
self.demand_coerce_diag(idx, idx_t, index_ty, None, AllowTwoPhase::No);
3179+
if let Some(err) = err {
3180+
err.cancel();
3181+
} else if self
3182+
.fulfillment_cx
3183+
.borrow_mut()
3184+
.select_where_possible(self)
3185+
.is_empty()
3186+
{
3187+
if let Some(pred) = error.obligation.predicate.to_opt_poly_trait_pred() {
3188+
error.obligation.cause =
3189+
error.obligation.cause.clone().derived_cause(pred, |cause| {
3190+
ObligationCauseCode::IndexExprDerivedObligation(Box::new((
3191+
cause,
3192+
idx_span.with_hi(idx.span.lo()),
3193+
)))
3194+
});
3195+
}
3196+
}
3197+
}
3198+
}
31593199
}
31603200
}
31613201

compiler/rustc_middle/src/traits/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ pub enum ObligationCauseCode<'tcx> {
331331

332332
DerivedObligation(DerivedObligationCause<'tcx>),
333333

334+
IndexExprDerivedObligation(Box<(DerivedObligationCause<'tcx>, Span)>),
335+
334336
FunctionArgumentObligation {
335337
/// The node of the relevant argument in the function call.
336338
arg_hir_id: hir::HirId,

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3399,6 +3399,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33993399
)
34003400
});
34013401
}
3402+
ObligationCauseCode::IndexExprDerivedObligation(ref data) => {
3403+
ensure_sufficient_stack(|| {
3404+
self.note_obligation_cause_code(
3405+
body_id,
3406+
err,
3407+
predicate,
3408+
param_env,
3409+
&data.0.parent_code,
3410+
obligated_types,
3411+
seen_requirements,
3412+
)
3413+
});
3414+
err.span_suggestion_verbose(
3415+
data.1,
3416+
"remove this borrow",
3417+
String::new(),
3418+
Applicability::MaybeIncorrect,
3419+
);
3420+
}
34023421
ObligationCauseCode::TypeAlias(ref nested, span, def_id) => {
34033422
// #74711: avoid a stack overflow
34043423
ensure_sufficient_stack(|| {
Lines changed: 8 additions & 0 deletions
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+
}
Lines changed: 2 additions & 1 deletion
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

Lines changed: 6 additions & 1 deletion
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 1 previous error
1217

0 commit comments

Comments
 (0)