Skip to content

Commit

Permalink
Rollup merge of rust-lang#67538 - varkor:lhs-assign-diagnostics, r=Ce…
Browse files Browse the repository at this point in the history
…ntril

Improve diagnostics for invalid assignment

- Improve wording and span information for invalid assignment diagnostics.
- Link to rust-lang/rfcs#372 when it appears the user is trying a destructuring assignment.
- Make the equality constraint in `where` clauses error consistent with the invalid assignment error.
  • Loading branch information
Centril authored Dec 22, 2019
2 parents e1e5348 + 770725c commit f8992d5
Show file tree
Hide file tree
Showing 37 changed files with 339 additions and 106 deletions.
6 changes: 3 additions & 3 deletions src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,9 +1056,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
walk_list!(visitor, visit_label, opt_label);
visitor.visit_block(block);
}
ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => {
visitor.visit_expr(right_hand_expression);
visitor.visit_expr(left_hand_expression)
ExprKind::Assign(ref lhs, ref rhs, _) => {
visitor.visit_expr(rhs);
visitor.visit_expr(lhs)
}
ExprKind::AssignOp(_, ref left_expression, ref right_expression) => {
visitor.visit_expr(right_expression);
Expand Down
6 changes: 3 additions & 3 deletions src/librustc/hir/lowering/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ impl LoweringContext<'_, '_> {
opt_label.is_some()),
self.lower_label(opt_label))
}
ExprKind::Assign(ref el, ref er) => {
hir::ExprKind::Assign(P(self.lower_expr(el)), P(self.lower_expr(er)))
ExprKind::Assign(ref el, ref er, span) => {
hir::ExprKind::Assign(P(self.lower_expr(el)), P(self.lower_expr(er)), span)
}
ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
self.lower_binop(op),
Expand Down Expand Up @@ -1084,7 +1084,7 @@ impl LoweringContext<'_, '_> {
let next_expr = P(self.expr_ident(pat.span, next_ident, next_pat_hid));
let assign = P(self.expr(
pat.span,
hir::ExprKind::Assign(next_expr, val_expr),
hir::ExprKind::Assign(next_expr, val_expr, pat.span),
ThinVec::new(),
));
let some_pat = self.pat_some(pat.span, val_pat);
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1722,7 +1722,8 @@ pub enum ExprKind {
Block(P<Block>, Option<Label>),

/// An assignment (e.g., `a = foo()`).
Assign(P<Expr>, P<Expr>),
/// The `Span` argument is the span of the `=` token.
Assign(P<Expr>, P<Expr>, Span),
/// An assignment with an operator.
///
/// E.g., `a += 1`.
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,7 @@ impl<'a> State<'a> {
self.ibox(0);
self.print_block(&blk);
}
hir::ExprKind::Assign(ref lhs, ref rhs) => {
hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
let prec = AssocOp::Assign.precedence() as i8;
self.print_expr_maybe_paren(&lhs, prec + 1);
self.s.space();
Expand Down Expand Up @@ -2282,7 +2282,7 @@ fn contains_exterior_struct_lit(value: &hir::Expr) -> bool {
match value.kind {
hir::ExprKind::Struct(..) => true,

hir::ExprKind::Assign(ref lhs, ref rhs) |
hir::ExprKind::Assign(ref lhs, ref rhs, _) |
hir::ExprKind::AssignOp(_, ref lhs, ref rhs) |
hir::ExprKind::Binary(_, ref lhs, ref rhs) => {
// `X { y: 1 } + X { y: 2 }`
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_lint/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ impl EarlyLintPass for UnusedParens {
(value, "`return` value", false, Some(left), None)
}

Assign(_, ref value) => (value, "assigned value", false, None, None),
Assign(_, ref value, _) => (value, "assigned value", false, None, None),
AssignOp(.., ref value) => (value, "assigned value", false, None, None),
// either function/method call, or something this lint doesn't care about
ref call_or_other => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/hair/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(

hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: &blk },

hir::ExprKind::Assign(ref lhs, ref rhs) => {
hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
ExprKind::Assign {
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_parse/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,9 @@ impl<'a> Parser<'a> {
let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs);
self.mk_expr(span, binary, AttrVec::new())
}
AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs), AttrVec::new()),
AssocOp::Assign => {
self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span), AttrVec::new())
}
AssocOp::AssignOp(k) => {
let aop = match k {
token::Plus => BinOpKind::Add,
Expand Down
10 changes: 8 additions & 2 deletions src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,8 +737,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
for predicate in &generics.where_clause.predicates {
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
self.err_handler()
.span_err(predicate.span, "equality constraints are not yet \
supported in where clauses (see #20041)");
.struct_span_err(
predicate.span,
"equality constraints are not yet supported in `where` clauses",
)
.note(
"for more information, see https://github.com/rust-lang/rust/issues/20041",
)
.emit();
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/librustc_passes/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1096,7 +1096,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
span_bug!(expr.span, "continue to unknown label"))
}

hir::ExprKind::Assign(ref l, ref r) => {
hir::ExprKind::Assign(ref l, ref r, _) => {
// see comment on places in
// propagate_through_place_components()
let succ = self.write_place(&l, succ, ACC_WRITE);
Expand Down Expand Up @@ -1389,7 +1389,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {

fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr) {
match expr.kind {
hir::ExprKind::Assign(ref l, _) => {
hir::ExprKind::Assign(ref l, ..) => {
this.check_place(&l);
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_privacy/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
return;
}
match expr.kind {
hir::ExprKind::Assign(.., ref rhs) | hir::ExprKind::Match(ref rhs, ..) => {
hir::ExprKind::Assign(_, ref rhs, _) | hir::ExprKind::Match(ref rhs, ..) => {
// Do not report duplicate errors for `x = y` and `match x { ... }`.
if self.check_expr_pat_type(rhs.hir_id, rhs.span) {
return;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
String::new()
};
if let Some(hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Assign(left_expr, _),
kind: hir::ExprKind::Assign(left_expr, ..),
..
})) = self.tcx.hir().find(
self.tcx.hir().get_parent_node(expr.hir_id),
Expand Down
52 changes: 43 additions & 9 deletions src/librustc_typeck/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::util::common::ErrorReported;
use crate::util::nodemap::FxHashMap;
use crate::astconv::AstConv as _;

use errors::{Applicability, DiagnosticBuilder, pluralize};
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, pluralize};
use syntax_pos::hygiene::DesugaringKind;
use syntax::ast;
use syntax::symbol::{Symbol, kw, sym};
Expand Down Expand Up @@ -230,6 +230,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::Binary(op, ref lhs, ref rhs) => {
self.check_binop(expr, op, lhs, rhs)
}
ExprKind::Assign(ref lhs, ref rhs, ref span) => {
self.check_expr_assign(expr, expected, lhs, rhs, span)
}
ExprKind::AssignOp(op, ref lhs, ref rhs) => {
self.check_binop_assign(expr, op, lhs, rhs)
}
Expand Down Expand Up @@ -262,9 +265,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::Ret(ref expr_opt) => {
self.check_expr_return(expr_opt.as_deref(), expr)
}
ExprKind::Assign(ref lhs, ref rhs) => {
self.check_expr_assign(expr, expected, lhs, rhs)
}
ExprKind::Loop(ref body, _, source) => {
self.check_expr_loop(body, source, expected, expr)
}
Expand Down Expand Up @@ -759,6 +759,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}

fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr) -> bool {
match &expr.kind {
ExprKind::Array(comps) | ExprKind::Tup(comps) => {
comps.iter().all(|e| self.is_destructuring_place_expr(e))
}
ExprKind::Struct(_path, fields, rest) => {
rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true) &&
fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr))
}
_ => expr.is_syntactic_place_expr(),
}
}

pub(crate) fn check_lhs_assignable(
&self,
lhs: &'tcx hir::Expr,
err_code: &'static str,
expr_span: &Span,
) {
if !lhs.is_syntactic_place_expr() {
let mut err = self.tcx.sess.struct_span_err_with_code(
*expr_span,
"invalid left-hand side of assignment",
DiagnosticId::Error(err_code.into()),
);
err.span_label(lhs.span, "cannot assign to this expression");
if self.is_destructuring_place_expr(lhs) {
err.note("destructuring assignments are not currently supported");
err.note(
"for more information, see https://github.com/rust-lang/rfcs/issues/372",
);
}
err.emit();
}
}

/// Type check assignment expression `expr` of form `lhs = rhs`.
/// The expected type is `()` and is passsed to the function for the purposes of diagnostics.
fn check_expr_assign(
Expand All @@ -767,6 +803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Expectation<'tcx>,
lhs: &'tcx hir::Expr,
rhs: &'tcx hir::Expr,
span: &Span,
) -> Ty<'tcx> {
let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty);
Expand All @@ -788,11 +825,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.help(msg);
}
err.emit();
} else if !lhs.is_syntactic_place_expr() {
struct_span_err!(self.tcx.sess, expr.span, E0070,
"invalid left-hand side expression")
.span_label(expr.span, "left-hand of expression not valid")
.emit();
} else {
self.check_lhs_assignable(lhs, "E0070", span);
}

self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
Expand Down
19 changes: 6 additions & 13 deletions src/librustc_typeck/check/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
expr: &'tcx hir::Expr,
op: hir::BinOp,
lhs_expr: &'tcx hir::Expr,
rhs_expr: &'tcx hir::Expr,
lhs: &'tcx hir::Expr,
rhs: &'tcx hir::Expr,
) -> Ty<'tcx> {
let (lhs_ty, rhs_ty, return_ty) =
self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::Yes);
self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes);

let ty = if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var()
&& is_builtin_binop(lhs_ty, rhs_ty, op) {
self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
self.enforce_builtin_binop_types(lhs, lhs_ty, rhs, rhs_ty, op);
self.tcx.mk_unit()
} else {
return_ty
};

if !lhs_expr.is_syntactic_place_expr() {
struct_span_err!(
self.tcx.sess, lhs_expr.span,
E0067, "invalid left-hand side expression")
.span_label(
lhs_expr.span,
"invalid expression for left-hand side")
.emit();
}
self.check_lhs_assignable(lhs, "E0067", &op.span);

ty
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
}
}

hir::ExprKind::Assign(ref lhs, ref rhs) => {
hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
self.mutate_expr(lhs);
self.consume_expr(rhs);
}
Expand Down
3 changes: 2 additions & 1 deletion src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1228,7 +1228,8 @@ pub enum ExprKind {
TryBlock(P<Block>),

/// An assignment (`a = foo()`).
Assign(P<Expr>, P<Expr>),
/// The `Span` argument is the span of the `=` token.
Assign(P<Expr>, P<Expr>, Span),
/// An assignment with an operator.
///
/// E.g., `a += 1`.
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,7 @@ pub fn noop_visit_expr<T: MutVisitor>(Expr { kind, id, span, attrs }: &mut Expr,
vis.visit_block(body);
}
ExprKind::Await(expr) => vis.visit_expr(expr),
ExprKind::Assign(el, er) => {
ExprKind::Assign(el, er, _) => {
vis.visit_expr(el);
vis.visit_expr(er);
}
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2077,7 +2077,7 @@ impl<'a> State<'a> {
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
self.s.word(".await");
}
ast::ExprKind::Assign(ref lhs, ref rhs) => {
ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
let prec = AssocOp::Assign.precedence() as i8;
self.print_expr_maybe_paren(lhs, prec + 1);
self.s.space();
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/util/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
match value.kind {
ast::ExprKind::Struct(..) => true,

ast::ExprKind::Assign(ref lhs, ref rhs) |
ast::ExprKind::Assign(ref lhs, ref rhs, _) |
ast::ExprKind::AssignOp(_, ref lhs, ref rhs) |
ast::ExprKind::Binary(_, ref lhs, ref rhs) => {
// X { y: 1 } + X { y: 2 }
Expand Down
6 changes: 3 additions & 3 deletions src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -743,9 +743,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
visitor.visit_block(body);
}
ExprKind::Await(ref expr) => visitor.visit_expr(expr),
ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => {
visitor.visit_expr(left_hand_expression);
visitor.visit_expr(right_hand_expression);
ExprKind::Assign(ref lhs, ref rhs, _) => {
visitor.visit_expr(lhs);
visitor.visit_expr(rhs);
}
ExprKind::AssignOp(_, ref left_expression, ref right_expression) => {
visitor.visit_expr(left_expression);
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui-fulldeps/pprust-expr-roundtrip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
DUMMY_SP)));
},
12 => {
iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x())));
iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e)));
iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x(), DUMMY_SP)));
iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e, DUMMY_SP)));
},
13 => {
iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, Ident::from_str("f"))));
Expand Down
10 changes: 5 additions & 5 deletions src/test/ui/bad/bad-expr-lhs.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
fn main() {
1 = 2; //~ ERROR invalid left-hand side expression
1 += 2; //~ ERROR invalid left-hand side expression
(1, 2) = (3, 4); //~ ERROR invalid left-hand side expression
1 = 2; //~ ERROR invalid left-hand side of assignment
1 += 2; //~ ERROR invalid left-hand side of assignment
(1, 2) = (3, 4); //~ ERROR invalid left-hand side of assignment

let (a, b) = (1, 2);
(a, b) = (3, 4); //~ ERROR invalid left-hand side expression
(a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment

None = Some(3); //~ ERROR invalid left-hand side expression
None = Some(3); //~ ERROR invalid left-hand side of assignment
}
Loading

0 comments on commit f8992d5

Please sign in to comment.