Skip to content

Commit 3ebb562

Browse files
committedSep 11, 2023
Auto merge of rust-lang#115595 - surechen:114896, r=davidtwco
Fix incorrect mutable suggestion information for binding in ref pattern like: `let &b = a;` fixes rust-lang#114896 I find we have to get pat_span but not local_decl.source_info.span for suggestion. In `let &b = a;` pat_span is &b. I think check `let &b = a` in hir to make sure it is hir::Node::Local(hir::Local {pat: hir::Pat{kind: hir::PatKind::Ref(....... can distinguish it from other situation, but I'm not sure. If my processing method is not accurate, please guide me to modify it, thank you. r? `@davidtwco`
2 parents 68c2f5b + a8f3c76 commit 3ebb562

File tree

3 files changed

+96
-6
lines changed

3 files changed

+96
-6
lines changed
 

‎compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+78-6
Original file line numberDiff line numberDiff line change
@@ -371,12 +371,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
371371
err.span_label(span, format!("cannot {act}"));
372372
}
373373
if suggest {
374-
err.span_suggestion_verbose(
375-
local_decl.source_info.span.shrink_to_lo(),
376-
"consider changing this to be mutable",
377-
"mut ",
378-
Applicability::MachineApplicable,
379-
);
374+
self.construct_mut_suggestion_for_local_binding_patterns(&mut err, local);
380375
let tcx = self.infcx.tcx;
381376
if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
382377
self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
@@ -712,6 +707,83 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
712707
)
713708
}
714709

710+
fn construct_mut_suggestion_for_local_binding_patterns(
711+
&self,
712+
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
713+
local: Local,
714+
) {
715+
let local_decl = &self.body.local_decls[local];
716+
debug!("local_decl: {:?}", local_decl);
717+
let pat_span = match *local_decl.local_info() {
718+
LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
719+
binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
720+
opt_ty_info: _,
721+
opt_match_place: _,
722+
pat_span,
723+
})) => pat_span,
724+
_ => local_decl.source_info.span,
725+
};
726+
727+
struct BindingFinder {
728+
span: Span,
729+
hir_id: Option<hir::HirId>,
730+
}
731+
732+
impl<'tcx> Visitor<'tcx> for BindingFinder {
733+
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
734+
if let hir::StmtKind::Local(local) = s.kind {
735+
if local.pat.span == self.span {
736+
self.hir_id = Some(local.hir_id);
737+
}
738+
}
739+
hir::intravisit::walk_stmt(self, s);
740+
}
741+
}
742+
743+
let hir_map = self.infcx.tcx.hir();
744+
let def_id = self.body.source.def_id();
745+
let hir_id = if let Some(local_def_id) = def_id.as_local()
746+
&& let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
747+
{
748+
let body = hir_map.body(body_id);
749+
let mut v = BindingFinder {
750+
span: pat_span,
751+
hir_id: None,
752+
};
753+
v.visit_body(body);
754+
v.hir_id
755+
} else {
756+
None
757+
};
758+
759+
// With ref-binding patterns, the mutability suggestion has to apply to
760+
// the binding, not the reference (which would be a type error):
761+
//
762+
// `let &b = a;` -> `let &(mut b) = a;`
763+
if let Some(hir_id) = hir_id
764+
&& let Some(hir::Node::Local(hir::Local {
765+
pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
766+
..
767+
})) = hir_map.find(hir_id)
768+
&& let Ok(name) = self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span)
769+
{
770+
err.span_suggestion(
771+
pat_span,
772+
"consider changing this to be mutable",
773+
format!("&(mut {name})"),
774+
Applicability::MachineApplicable,
775+
);
776+
return;
777+
}
778+
779+
err.span_suggestion_verbose(
780+
local_decl.source_info.span.shrink_to_lo(),
781+
"consider changing this to be mutable",
782+
"mut ",
783+
Applicability::MachineApplicable,
784+
);
785+
}
786+
715787
// point to span of upvar making closure call require mutable borrow
716788
fn show_mutating_upvar(
717789
&self,

‎tests/ui/pattern/issue-114896.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
fn x(a: &char) {
3+
let &b = a;
4+
b.make_ascii_uppercase();
5+
//~^ cannot borrow `b` as mutable, as it is not declared as mutable
6+
}
7+
}

‎tests/ui/pattern/issue-114896.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0596]: cannot borrow `b` as mutable, as it is not declared as mutable
2+
--> $DIR/issue-114896.rs:4:9
3+
|
4+
LL | let &b = a;
5+
| -- help: consider changing this to be mutable: `&(mut b)`
6+
LL | b.make_ascii_uppercase();
7+
| ^ cannot borrow as mutable
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0596`.

0 commit comments

Comments
 (0)
Please sign in to comment.