Skip to content

Commit 4bd40d6

Browse files
authored
Rollup merge of #91545 - compiler-errors:deref-suggestion-improvements, r=estebank
Generalize "remove `&`" and "add `*`" suggestions to more than one deref Suggest removing more than one `&` and `&mut`, along with suggesting adding more than one `*` (or a combination of the two). r? `@estebank` (since you're experienced with these types of suggestions, feel free to reassign)
2 parents 8d6f527 + 5ce3f56 commit 4bd40d6

File tree

7 files changed

+214
-60
lines changed

7 files changed

+214
-60
lines changed

compiler/rustc_typeck/src/check/demand.rs

+92-58
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
566566
expr: &hir::Expr<'tcx>,
567567
checked_ty: Ty<'tcx>,
568568
expected: Ty<'tcx>,
569-
) -> Option<(Span, &'static str, String, Applicability, bool /* verbose */)> {
569+
) -> Option<(Span, String, String, Applicability, bool /* verbose */)> {
570570
let sess = self.sess();
571571
let sp = expr.span;
572572

@@ -594,7 +594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
594594
let pos = sp.lo() + BytePos(1);
595595
return Some((
596596
sp.with_hi(pos),
597-
"consider removing the leading `b`",
597+
"consider removing the leading `b`".to_string(),
598598
String::new(),
599599
Applicability::MachineApplicable,
600600
true,
@@ -608,7 +608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
608608
{
609609
return Some((
610610
sp.shrink_to_lo(),
611-
"consider adding a leading `b`",
611+
"consider adding a leading `b`".to_string(),
612612
"b".to_string(),
613613
Applicability::MachineApplicable,
614614
true,
@@ -668,7 +668,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
668668
if let Some(sugg) = self.can_use_as_ref(expr) {
669669
return Some((
670670
sugg.0,
671-
sugg.1,
671+
sugg.1.to_string(),
672672
sugg.2,
673673
Applicability::MachineApplicable,
674674
false,
@@ -696,7 +696,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
696696
return Some((
697697
left_expr.span.shrink_to_lo(),
698698
"consider dereferencing here to assign to the mutable \
699-
borrowed piece of memory",
699+
borrowed piece of memory"
700+
.to_string(),
700701
"*".to_string(),
701702
Applicability::MachineApplicable,
702703
true,
@@ -708,14 +709,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
708709
return Some(match mutability {
709710
hir::Mutability::Mut => (
710711
sp,
711-
"consider mutably borrowing here",
712+
"consider mutably borrowing here".to_string(),
712713
format!("{}&mut {}", prefix, sugg_expr),
713714
Applicability::MachineApplicable,
714715
false,
715716
),
716717
hir::Mutability::Not => (
717718
sp,
718-
"consider borrowing here",
719+
"consider borrowing here".to_string(),
719720
format!("{}&{}", prefix, sugg_expr),
720721
Applicability::MachineApplicable,
721722
false,
@@ -744,7 +745,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
744745
if sm.span_to_snippet(call_span).is_ok() {
745746
return Some((
746747
sp.with_hi(call_span.lo()),
747-
"consider removing the borrow",
748+
"consider removing the borrow".to_string(),
748749
String::new(),
749750
Applicability::MachineApplicable,
750751
true,
@@ -757,7 +758,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
757758
if sm.span_to_snippet(expr.span).is_ok() {
758759
return Some((
759760
sp.with_hi(expr.span.lo()),
760-
"consider removing the borrow",
761+
"consider removing the borrow".to_string(),
761762
String::new(),
762763
Applicability::MachineApplicable,
763764
true,
@@ -823,7 +824,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
823824
} {
824825
return Some((
825826
span,
826-
"consider dereferencing",
827+
"consider dereferencing".to_string(),
827828
src,
828829
applicability,
829830
true,
@@ -834,60 +835,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
834835
}
835836
}
836837
_ if sp == expr.span => {
837-
if let Some(steps) = self.deref_steps(checked_ty, expected) {
838-
let expr = expr.peel_blocks();
838+
if let Some(mut steps) = self.deref_steps(checked_ty, expected) {
839+
let mut expr = expr.peel_blocks();
840+
let mut prefix_span = expr.span.shrink_to_lo();
841+
let mut remove = String::new();
839842

840-
if steps == 1 {
843+
// Try peeling off any existing `&` and `&mut` to reach our target type
844+
while steps > 0 {
841845
if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
842846
// If the expression has `&`, removing it would fix the error
843-
let prefix_span = expr.span.with_hi(inner.span.lo());
844-
let message = match mutbl {
845-
hir::Mutability::Not => "consider removing the `&`",
846-
hir::Mutability::Mut => "consider removing the `&mut`",
847+
prefix_span = prefix_span.with_hi(inner.span.lo());
848+
expr = inner;
849+
remove += match mutbl {
850+
hir::Mutability::Not => "&",
851+
hir::Mutability::Mut => "&mut ",
847852
};
848-
let suggestion = String::new();
849-
return Some((
850-
prefix_span,
851-
message,
852-
suggestion,
853-
Applicability::MachineApplicable,
854-
false,
855-
));
853+
steps -= 1;
854+
} else {
855+
break;
856856
}
857-
858-
// For this suggestion to make sense, the type would need to be `Copy`,
859-
// or we have to be moving out of a `Box<T>`
860-
if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp)
861-
|| checked_ty.is_box()
862-
{
863-
let message = if checked_ty.is_box() {
864-
"consider unboxing the value"
865-
} else if checked_ty.is_region_ptr() {
866-
"consider dereferencing the borrow"
867-
} else {
868-
"consider dereferencing the type"
869-
};
870-
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
871-
Some(ident) => format!("{}: ", ident),
872-
None => String::new(),
873-
};
874-
let (span, suggestion) = if self.is_else_if_block(expr) {
875-
// Don't suggest nonsense like `else *if`
876-
return None;
877-
} else if let Some(expr) = self.maybe_get_block_expr(expr) {
878-
// prefix should be empty here..
879-
(expr.span.shrink_to_lo(), "*".to_string())
857+
}
858+
// If we've reached our target type with just removing `&`, then just print now.
859+
if steps == 0 {
860+
return Some((
861+
prefix_span,
862+
format!("consider removing the `{}`", remove.trim()),
863+
String::new(),
864+
// Do not remove `&&` to get to bool, because it might be something like
865+
// { a } && b, which we have a separate fixup suggestion that is more
866+
// likely correct...
867+
if remove.trim() == "&&" && expected == self.tcx.types.bool {
868+
Applicability::MaybeIncorrect
880869
} else {
881-
(expr.span.shrink_to_lo(), format!("{}*", prefix))
882-
};
883-
return Some((
884-
span,
885-
message,
886-
suggestion,
887-
Applicability::MachineApplicable,
888-
true,
889-
));
890-
}
870+
Applicability::MachineApplicable
871+
},
872+
true,
873+
));
874+
}
875+
876+
// For this suggestion to make sense, the type would need to be `Copy`,
877+
// or we have to be moving out of a `Box<T>`
878+
if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp)
879+
// FIXME(compiler-errors): We can actually do this if the checked_ty is
880+
// `steps` layers of boxes, not just one, but this is easier and most likely.
881+
|| (checked_ty.is_box() && steps == 1)
882+
{
883+
let deref_kind = if checked_ty.is_box() {
884+
"unboxing the value"
885+
} else if checked_ty.is_region_ptr() {
886+
"dereferencing the borrow"
887+
} else {
888+
"dereferencing the type"
889+
};
890+
891+
// Suggest removing `&` if we have removed any, otherwise suggest just
892+
// dereferencing the remaining number of steps.
893+
let message = if remove.is_empty() {
894+
format!("consider {}", deref_kind)
895+
} else {
896+
format!(
897+
"consider removing the `{}` and {} instead",
898+
remove.trim(),
899+
deref_kind
900+
)
901+
};
902+
903+
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
904+
Some(ident) => format!("{}: ", ident),
905+
None => String::new(),
906+
};
907+
908+
let (span, suggestion) = if self.is_else_if_block(expr) {
909+
// Don't suggest nonsense like `else *if`
910+
return None;
911+
} else if let Some(expr) = self.maybe_get_block_expr(expr) {
912+
// prefix should be empty here..
913+
(expr.span.shrink_to_lo(), "*".to_string())
914+
} else {
915+
(prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
916+
};
917+
918+
return Some((
919+
span,
920+
message,
921+
suggestion,
922+
Applicability::MachineApplicable,
923+
true,
924+
));
891925
}
892926
}
893927
}

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
218218
self.check_ref(expr, found, expected)
219219
{
220220
if verbose {
221-
err.span_suggestion_verbose(sp, msg, suggestion, applicability);
221+
err.span_suggestion_verbose(sp, &msg, suggestion, applicability);
222222
} else {
223-
err.span_suggestion(sp, msg, suggestion, applicability);
223+
err.span_suggestion(sp, &msg, suggestion, applicability);
224224
}
225225
} else if let (ty::FnDef(def_id, ..), true) =
226226
(&found.kind(), self.suggest_fn_call(err, expr, expected, found))

src/test/ui/parser/expr-as-stmt-2.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ LL | / &&
3636
LL | | if let Some(y) = a { true } else { false }
3737
| |______________________________________________^ expected `bool`, found `&&bool`
3838
|
39+
help: consider removing the `&&`
40+
|
41+
LL - &&
42+
LL + if let Some(y) = a { true } else { false }
43+
|
3944
help: parentheses are required to parse this as an expression
4045
|
4146
LL | (if let Some(x) = a { true } else { false })

src/test/ui/parser/expr-as-stmt.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ LL | fn revenge_from_mars() -> bool {
170170
LL | { true } && { true }
171171
| ^^^^^^^^^^^ expected `bool`, found `&&bool`
172172
|
173+
help: consider removing the `&&`
174+
|
175+
LL - { true } && { true }
176+
LL + { true } { true }
177+
|
173178
help: parentheses are required to parse this as an expression
174179
|
175180
LL | ({ true }) && { true }

src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,12 @@ error[E0308]: mismatched types
676676
|
677677
LL | if let Range { start: true, end } = t..&&false {}
678678
| ^^^^^^^ expected `bool`, found `&&bool`
679+
|
680+
help: consider removing the `&&`
681+
|
682+
LL - if let Range { start: true, end } = t..&&false {}
683+
LL + if let Range { start: true, end } = t..false {}
684+
|
679685

680686
error[E0308]: mismatched types
681687
--> $DIR/disallowed-positions.rs:83:8
@@ -866,6 +872,12 @@ error[E0308]: mismatched types
866872
|
867873
LL | while let Range { start: true, end } = t..&&false {}
868874
| ^^^^^^^ expected `bool`, found `&&bool`
875+
|
876+
help: consider removing the `&&`
877+
|
878+
LL - while let Range { start: true, end } = t..&&false {}
879+
LL + while let Range { start: true, end } = t..false {}
880+
|
869881

870882
error[E0308]: mismatched types
871883
--> $DIR/disallowed-positions.rs:147:11

src/test/ui/typeck/deref-multi.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
fn a(x: &&i32) -> i32 {
2+
x
3+
//~^ ERROR mismatched types
4+
}
5+
6+
fn a2(x: &&&&&i32) -> i32 {
7+
x
8+
//~^ ERROR mismatched types
9+
}
10+
11+
fn b(x: &i32) -> i32 {
12+
&x
13+
//~^ ERROR mismatched types
14+
}
15+
16+
fn c(x: Box<i32>) -> i32 {
17+
&x
18+
//~^ ERROR mismatched types
19+
}
20+
21+
fn d(x: std::sync::Mutex<&i32>) -> i32 {
22+
x.lock().unwrap()
23+
//~^ ERROR mismatched types
24+
}
25+
26+
fn main() {}

src/test/ui/typeck/deref-multi.stderr

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/deref-multi.rs:2:5
3+
|
4+
LL | fn a(x: &&i32) -> i32 {
5+
| --- expected `i32` because of return type
6+
LL | x
7+
| ^ expected `i32`, found `&&i32`
8+
|
9+
help: consider dereferencing the borrow
10+
|
11+
LL | **x
12+
| ++
13+
14+
error[E0308]: mismatched types
15+
--> $DIR/deref-multi.rs:7:5
16+
|
17+
LL | fn a2(x: &&&&&i32) -> i32 {
18+
| --- expected `i32` because of return type
19+
LL | x
20+
| ^ expected `i32`, found `&&&&&i32`
21+
|
22+
help: consider dereferencing the borrow
23+
|
24+
LL | *****x
25+
| +++++
26+
27+
error[E0308]: mismatched types
28+
--> $DIR/deref-multi.rs:12:5
29+
|
30+
LL | fn b(x: &i32) -> i32 {
31+
| --- expected `i32` because of return type
32+
LL | &x
33+
| ^^ expected `i32`, found `&&i32`
34+
|
35+
help: consider removing the `&` and dereferencing the borrow instead
36+
|
37+
LL | *x
38+
| ~
39+
40+
error[E0308]: mismatched types
41+
--> $DIR/deref-multi.rs:17:5
42+
|
43+
LL | fn c(x: Box<i32>) -> i32 {
44+
| --- expected `i32` because of return type
45+
LL | &x
46+
| ^^ expected `i32`, found `&Box<i32>`
47+
|
48+
= note: expected type `i32`
49+
found reference `&Box<i32>`
50+
help: consider removing the `&` and dereferencing the borrow instead
51+
|
52+
LL | *x
53+
| ~
54+
55+
error[E0308]: mismatched types
56+
--> $DIR/deref-multi.rs:22:5
57+
|
58+
LL | fn d(x: std::sync::Mutex<&i32>) -> i32 {
59+
| --- expected `i32` because of return type
60+
LL | x.lock().unwrap()
61+
| ^^^^^^^^^^^^^^^^^ expected `i32`, found struct `MutexGuard`
62+
|
63+
= note: expected type `i32`
64+
found struct `MutexGuard<'_, &i32>`
65+
help: consider dereferencing the type
66+
|
67+
LL | **x.lock().unwrap()
68+
| ++
69+
70+
error: aborting due to 5 previous errors
71+
72+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)