Skip to content

Commit 0e1c211

Browse files
committed
On type mismatch caused by assignment, point at assignee
* Do not emit unnecessary E0308 after E0070 * Show fewer errors on `while let` missing `let` * Hide redundant E0308 on `while let` missing `let` * Point at binding definition when possible on invalid assignment * do not point at closure twice * do not suggest `if let` for literals in lhs * account for parameter types
1 parent 2885c47 commit 0e1c211

39 files changed

+297
-117
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -915,14 +915,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
915915
);
916916
}
917917
if !self.sess.features_untracked().destructuring_assignment {
918-
feature_err(
918+
let mut err = feature_err(
919919
&self.sess.parse_sess,
920920
sym::destructuring_assignment,
921921
eq_sign_span,
922922
"destructuring assignments are unstable",
923-
)
924-
.span_label(lhs.span, "cannot assign to this expression")
925-
.emit();
923+
);
924+
err.span_label(lhs.span, "cannot assign to this expression");
925+
if self.is_in_loop_condition {
926+
err.span_suggestion_verbose(
927+
lhs.span.shrink_to_lo(),
928+
"you might have meant to use pattern destructuring",
929+
"let ".to_string(),
930+
rustc_errors::Applicability::MachineApplicable,
931+
);
932+
}
933+
err.emit();
926934
}
927935

928936
let mut assignments = vec![];

compiler/rustc_typeck/src/check/coercion.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -1458,7 +1458,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
14581458
cause,
14591459
expected,
14601460
found,
1461-
coercion_error,
1461+
coercion_error.clone(),
14621462
fcx,
14631463
parent_id,
14641464
expression.map(|expr| (expr, blk_id)),
@@ -1472,7 +1472,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
14721472
cause,
14731473
expected,
14741474
found,
1475-
coercion_error,
1475+
coercion_error.clone(),
14761476
fcx,
14771477
id,
14781478
None,
@@ -1483,7 +1483,12 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
14831483
}
14841484
}
14851485
_ => {
1486-
err = fcx.report_mismatched_types(cause, expected, found, coercion_error);
1486+
err = fcx.report_mismatched_types(
1487+
cause,
1488+
expected,
1489+
found,
1490+
coercion_error.clone(),
1491+
);
14871492
}
14881493
}
14891494

@@ -1492,7 +1497,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
14921497
}
14931498

14941499
if let Some(expr) = expression {
1495-
fcx.emit_coerce_suggestions(&mut err, expr, found, expected, None);
1500+
fcx.emit_coerce_suggestions(
1501+
&mut err,
1502+
expr,
1503+
found,
1504+
expected,
1505+
None,
1506+
coercion_error,
1507+
);
14961508
}
14971509

14981510
err.emit_unless(unsized_return);

compiler/rustc_typeck/src/check/demand.rs

+74-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem;
1010
use rustc_hir::{is_range_literal, Node};
1111
use rustc_middle::lint::in_external_macro;
1212
use rustc_middle::ty::adjustment::AllowTwoPhase;
13+
use rustc_middle::ty::error::{ExpectedFound, TypeError};
1314
use rustc_middle::ty::print::with_no_trimmed_paths;
1415
use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
1516
use rustc_span::symbol::sym;
@@ -27,8 +28,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2728
expr_ty: Ty<'tcx>,
2829
expected: Ty<'tcx>,
2930
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
31+
error: TypeError<'tcx>,
3032
) {
31-
self.annotate_expected_due_to_let_ty(err, expr);
33+
self.annotate_expected_due_to_let_ty(err, expr, error);
3234
self.suggest_box_deref(err, expr, expected, expr_ty);
3335
self.suggest_compatible_variants(err, expr, expected, expr_ty);
3436
self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
@@ -145,9 +147,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
145147
let expr = expr.peel_drop_temps();
146148
let cause = self.misc(expr.span);
147149
let expr_ty = self.resolve_vars_with_obligations(checked_ty);
148-
let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
150+
let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone());
149151

150-
self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr);
152+
self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr, e);
151153

152154
(expected, Some(err))
153155
}
@@ -156,15 +158,80 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
156158
&self,
157159
err: &mut DiagnosticBuilder<'_>,
158160
expr: &hir::Expr<'_>,
161+
error: TypeError<'_>,
159162
) {
160163
let parent = self.tcx.hir().get_parent_node(expr.hir_id);
161-
if let Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })) =
162-
self.tcx.hir().find(parent)
163-
{
164-
if init.hir_id == expr.hir_id {
164+
match (self.tcx.hir().find(parent), error) {
165+
(Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })), _)
166+
if init.hir_id == expr.hir_id =>
167+
{
165168
// Point at `let` assignment type.
166169
err.span_label(ty.span, "expected due to this");
167170
}
171+
(
172+
Some(hir::Node::Expr(hir::Expr {
173+
kind: hir::ExprKind::Assign(lhs, rhs, _), ..
174+
})),
175+
TypeError::Sorts(ExpectedFound { expected, .. }),
176+
) if rhs.hir_id == expr.hir_id && !expected.is_closure() => {
177+
// We ignore closures explicitly because we already point at them elsewhere.
178+
// Point at the assigned-to binding.
179+
let mut primary_span = lhs.span;
180+
let mut secondary_span = lhs.span;
181+
let mut post_message = "";
182+
if let hir::ExprKind::Path(hir::QPath::Resolved(
183+
None,
184+
hir::Path { res: hir::def::Res::Local(hir_id), .. },
185+
)) = lhs.kind
186+
{
187+
if let Some(hir::Node::Binding(pat)) = self.tcx.hir().find(*hir_id) {
188+
let parent = self.tcx.hir().get_parent_node(pat.hir_id);
189+
primary_span = pat.span;
190+
secondary_span = pat.span;
191+
match self.tcx.hir().find(parent) {
192+
Some(hir::Node::Local(hir::Local { ty: Some(ty), .. })) => {
193+
primary_span = ty.span;
194+
post_message = " type";
195+
}
196+
Some(hir::Node::Local(hir::Local { init: Some(init), .. })) => {
197+
primary_span = init.span;
198+
post_message = " value";
199+
}
200+
Some(hir::Node::Param(hir::Param { ty_span, .. })) => {
201+
primary_span = *ty_span;
202+
post_message = " parameter type";
203+
}
204+
_ => {}
205+
}
206+
}
207+
}
208+
209+
if primary_span != secondary_span
210+
&& self
211+
.tcx
212+
.sess
213+
.source_map()
214+
.is_multiline(secondary_span.shrink_to_hi().until(primary_span))
215+
{
216+
// We are pointing at the binding's type or initializer value, but it's pattern
217+
// is in a different line, so we point at both.
218+
err.span_label(secondary_span, "expected due to the type of this binding");
219+
err.span_label(primary_span, &format!("expected due to this{}", post_message));
220+
} else if post_message == "" {
221+
// We are pointing at either the assignment lhs or the binding def pattern.
222+
err.span_label(primary_span, "expected due to the type of this binding");
223+
} else {
224+
// We are pointing at the binding's type or initializer value.
225+
err.span_label(primary_span, &format!("expected due to this{}", post_message));
226+
}
227+
228+
if !lhs.is_syntactic_place_expr() {
229+
// We already emitted E0070 "invalid left-hand side of assignment", so we
230+
// silence this.
231+
err.delay_as_bug();
232+
}
233+
}
234+
_ => {}
168235
}
169236
}
170237

compiler/rustc_typeck/src/check/expr.rs

+52-5
Original file line numberDiff line numberDiff line change
@@ -833,19 +833,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
833833
&self,
834834
lhs: &'tcx hir::Expr<'tcx>,
835835
err_code: &'static str,
836-
expr_span: &Span,
836+
op_span: Span,
837837
) {
838838
if lhs.is_syntactic_place_expr() {
839839
return;
840840
}
841841

842842
// FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
843843
let mut err = self.tcx.sess.struct_span_err_with_code(
844-
*expr_span,
844+
op_span,
845845
"invalid left-hand side of assignment",
846846
DiagnosticId::Error(err_code.into()),
847847
);
848848
err.span_label(lhs.span, "cannot assign to this expression");
849+
850+
let mut parent = self.tcx.hir().get_parent_node(lhs.hir_id);
851+
while let Some(node) = self.tcx.hir().find(parent) {
852+
match node {
853+
hir::Node::Expr(hir::Expr {
854+
kind:
855+
hir::ExprKind::Loop(
856+
hir::Block {
857+
expr:
858+
Some(hir::Expr {
859+
kind:
860+
hir::ExprKind::Match(expr, ..) | hir::ExprKind::If(expr, ..),
861+
..
862+
}),
863+
..
864+
},
865+
_,
866+
hir::LoopSource::While,
867+
_,
868+
),
869+
..
870+
}) => {
871+
// We have a situation like `while Some(0) = value.get(0) {`, where `while let`
872+
// was more likely intended.
873+
err.span_suggestion_verbose(
874+
expr.span.shrink_to_lo(),
875+
"you might have meant to use pattern destructuring",
876+
"let ".to_string(),
877+
Applicability::MachineApplicable,
878+
);
879+
if !self.sess().features_untracked().destructuring_assignment {
880+
// We already emit an E0658 with a suggestion for `while let`, this is
881+
// redundant output.
882+
err.delay_as_bug();
883+
}
884+
break;
885+
}
886+
hir::Node::Item(_)
887+
| hir::Node::ImplItem(_)
888+
| hir::Node::TraitItem(_)
889+
| hir::Node::Crate(_) => break,
890+
_ => {
891+
parent = self.tcx.hir().get_parent_node(parent);
892+
}
893+
}
894+
}
895+
849896
err.emit();
850897
}
851898

@@ -953,7 +1000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9531000
} else {
9541001
(Applicability::MaybeIncorrect, false)
9551002
};
956-
if !lhs.is_syntactic_place_expr() {
1003+
if !lhs.is_syntactic_place_expr() && !matches!(lhs.kind, hir::ExprKind::Lit(_)) {
9571004
// Do not suggest `if let x = y` as `==` is way more likely to be the intention.
9581005
let hir = self.tcx.hir();
9591006
if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
@@ -965,7 +1012,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9651012
"let ".to_string(),
9661013
applicability,
9671014
);
968-
}
1015+
};
9691016
}
9701017
if eq {
9711018
err.span_suggestion_verbose(
@@ -986,7 +1033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9861033
return self.tcx.ty_error();
9871034
}
9881035

989-
self.check_lhs_assignable(lhs, "E0070", span);
1036+
self.check_lhs_assignable(lhs, "E0070", *span);
9901037

9911038
let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
9921039
let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs));

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

+18
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
740740
&mut |err| {
741741
if let Some(expected_ty) = expected.only_has_type(self) {
742742
self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
743+
if expected_ty == self.tcx.types.bool {
744+
// If this is caused by a missing `let` in a `while let`,
745+
// silence this redundant error, as we already emit E0070.
746+
let parent = self.tcx.hir().get_parent_node(blk.hir_id);
747+
let parent = self.tcx.hir().get_parent_node(parent);
748+
let parent = self.tcx.hir().get_parent_node(parent);
749+
let parent = self.tcx.hir().get_parent_node(parent);
750+
let parent = self.tcx.hir().get_parent_node(parent);
751+
match self.tcx.hir().find(parent) {
752+
Some(hir::Node::Expr(hir::Expr {
753+
kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
754+
..
755+
})) => {
756+
err.delay_as_bug();
757+
}
758+
_ => {}
759+
}
760+
}
743761
}
744762
if let Some(fn_span) = fn_span {
745763
err.span_label(

compiler/rustc_typeck/src/check/op.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4242
return_ty
4343
};
4444

45-
self.check_lhs_assignable(lhs, "E0067", &op.span);
45+
self.check_lhs_assignable(lhs, "E0067", op.span);
4646

4747
ty
4848
}

src/test/ui/dst/dst-bad-assign-3.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ error[E0308]: mismatched types
22
--> $DIR/dst-bad-assign-3.rs:33:12
33
|
44
LL | f5.2 = Bar1 {f: 36};
5-
| ^^^^^^^^^^^^ expected trait object `dyn ToBar`, found struct `Bar1`
5+
| ---- ^^^^^^^^^^^^ expected trait object `dyn ToBar`, found struct `Bar1`
6+
| |
7+
| expected due to the type of this binding
68
|
79
= note: expected trait object `dyn ToBar`
810
found struct `Bar1`

src/test/ui/dst/dst-bad-assign.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ error[E0308]: mismatched types
22
--> $DIR/dst-bad-assign.rs:35:14
33
|
44
LL | f5.ptr = Bar1 {f: 36};
5-
| ^^^^^^^^^^^^ expected trait object `dyn ToBar`, found struct `Bar1`
5+
| ------ ^^^^^^^^^^^^ expected trait object `dyn ToBar`, found struct `Bar1`
6+
| |
7+
| expected due to the type of this binding
68
|
79
= note: expected trait object `dyn ToBar`
810
found struct `Bar1`

src/test/ui/error-codes/E0070.rs

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ fn some_function() {
66
SOME_CONST = 14; //~ ERROR E0070
77
1 = 3; //~ ERROR E0070
88
some_other_func() = 4; //~ ERROR E0070
9-
//~^ ERROR E0308
109
}
1110

1211
fn main() {

src/test/ui/error-codes/E0070.stderr

+2-9
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,6 @@ LL | some_other_func() = 4;
2222
| |
2323
| cannot assign to this expression
2424

25-
error[E0308]: mismatched types
26-
--> $DIR/E0070.rs:8:25
27-
|
28-
LL | some_other_func() = 4;
29-
| ^ expected `()`, found integer
30-
31-
error: aborting due to 4 previous errors
25+
error: aborting due to 3 previous errors
3226

33-
Some errors have detailed explanations: E0070, E0308.
34-
For more information about an error, try `rustc --explain E0070`.
27+
For more information about this error, try `rustc --explain E0070`.
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// Test that we use fully-qualified type names in error messages.
22

33
fn main() {
4-
let x: Option<usize>;
4+
let x: //~ NOTE expected due to the type of this binding
5+
Option<usize>; //~ NOTE expected due to this type
56
x = 5;
67
//~^ ERROR mismatched types
7-
//~| expected enum `Option<usize>`
8-
//~| found type `{integer}`
9-
//~| expected enum `Option`, found integer
8+
//~| NOTE expected enum `Option<usize>`
9+
//~| NOTE expected enum `Option`, found integer
1010
}

src/test/ui/fully-qualified-type/fully-qualified-type-name1.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
error[E0308]: mismatched types
2-
--> $DIR/fully-qualified-type-name1.rs:5:9
2+
--> $DIR/fully-qualified-type-name1.rs:6:9
33
|
4+
LL | let x:
5+
| - expected due to the type of this binding
6+
LL | Option<usize>;
7+
| ------------- expected due to this type
48
LL | x = 5;
59
| ^ expected enum `Option`, found integer
610
|

0 commit comments

Comments
 (0)