Skip to content

Commit 550f653

Browse files
committed
let_chains: Handle disallowing of let chains in places lowering won't support.
1 parent 6162a97 commit 550f653

File tree

2 files changed

+86
-73
lines changed

2 files changed

+86
-73
lines changed

src/librustc_passes/ast_validation.rs

Lines changed: 85 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@ use syntax::ast::*;
1616
use syntax::attr;
1717
use syntax::source_map::Spanned;
1818
use syntax::symbol::{keywords, sym};
19-
use syntax::ptr::P;
2019
use syntax::visit::{self, Visitor};
2120
use syntax::{span_err, struct_span_err, walk_list};
2221
use syntax_ext::proc_macro_decls::is_proc_macro_attr;
2322
use syntax_pos::{Span, MultiSpan};
2423
use errors::{Applicability, FatalError};
25-
use log::debug;
2624

2725
#[derive(Copy, Clone, Debug)]
2826
struct OuterImplTrait {
@@ -71,26 +69,42 @@ struct AstValidator<'a> {
7169
/// these booleans.
7270
warning_period_57979_didnt_record_next_impl_trait: bool,
7371
warning_period_57979_impl_trait_in_proj: bool,
72+
73+
/// Used to ban `let` expressions in inappropriate places.
74+
is_let_allowed: bool,
75+
}
76+
77+
/// With the `new` value in `store`,
78+
/// runs and returns the `scoped` computation,
79+
/// resetting the old value of `store` after,
80+
/// and returning the result of `scoped`.
81+
fn with<C, T, S>(
82+
this: &mut C,
83+
new: S,
84+
store: impl Fn(&mut C) -> &mut S,
85+
scoped: impl FnOnce(&mut C) -> T
86+
) -> T {
87+
let old = mem::replace(store(this), new);
88+
let ret = scoped(this);
89+
*store(this) = old;
90+
ret
7491
}
7592

7693
impl<'a> AstValidator<'a> {
7794
fn with_impl_trait_in_proj_warning<T>(&mut self, v: bool, f: impl FnOnce(&mut Self) -> T) -> T {
78-
let old = mem::replace(&mut self.warning_period_57979_impl_trait_in_proj, v);
79-
let ret = f(self);
80-
self.warning_period_57979_impl_trait_in_proj = old;
81-
ret
95+
with(self, v, |this| &mut this.warning_period_57979_impl_trait_in_proj, f)
8296
}
8397

8498
fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
85-
let old = mem::replace(&mut self.is_impl_trait_banned, true);
86-
f(self);
87-
self.is_impl_trait_banned = old;
99+
with(self, true, |this| &mut this.is_impl_trait_banned, f)
88100
}
89101

90102
fn with_impl_trait(&mut self, outer: Option<OuterImplTrait>, f: impl FnOnce(&mut Self)) {
91-
let old = mem::replace(&mut self.outer_impl_trait, outer);
92-
f(self);
93-
self.outer_impl_trait = old;
103+
with(self, outer, |this| &mut this.outer_impl_trait, f)
104+
}
105+
106+
fn with_let_allowed(&mut self, v: bool, f: impl FnOnce(&mut Self)) {
107+
with(self, v, |this| &mut this.is_let_allowed, f)
94108
}
95109

96110
fn visit_assoc_type_binding_from_generic_args(&mut self, type_binding: &'a TypeBinding) {
@@ -297,52 +311,60 @@ impl<'a> AstValidator<'a> {
297311
}
298312
}
299313

300-
/// With eRFC 2497, we need to check whether an expression is ambiguous and warn or error
301-
/// depending on the edition, this function handles that.
302-
fn while_if_let_ambiguity(&self, expr: &P<Expr>) {
303-
if let Some((span, op_kind)) = self.while_if_let_expr_ambiguity(&expr) {
304-
let mut err = self.err_handler().struct_span_err(
305-
span, &format!("ambiguous use of `{}`", op_kind.to_string())
306-
);
307-
308-
err.note(
309-
"this will be a error until the `let_chains` feature is stabilized"
310-
);
311-
err.note(
312-
"see rust-lang/rust#53668 for more information"
313-
);
314-
315-
if let Ok(snippet) = self.session.source_map().span_to_snippet(span) {
314+
fn obsolete_in_place(&self, expr: &Expr, place: &Expr, val: &Expr) {
315+
let mut err = self.err_handler().struct_span_err(
316+
expr.span,
317+
"emplacement syntax is obsolete (for now, anyway)",
318+
);
319+
err.note(
320+
"for more information, see \
321+
<https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>"
322+
);
323+
match val.node {
324+
ExprKind::Lit(ref v) if v.node.is_numeric() => {
316325
err.span_suggestion(
317-
span, "consider adding parentheses", format!("({})", snippet),
318-
Applicability::MachineApplicable,
326+
place.span.between(val.span),
327+
"if you meant to write a comparison against a negative value, add a \
328+
space in between `<` and `-`",
329+
"< -".to_string(),
330+
Applicability::MaybeIncorrect
319331
);
320332
}
321-
322-
err.emit();
333+
_ => {}
323334
}
335+
err.emit();
324336
}
325337

326-
/// With eRFC 2497 adding if-let chains, there is a requirement that the parsing of
327-
/// `&&` and `||` in a if-let statement be unambiguous. This function returns a span and
328-
/// a `BinOpKind` (either `&&` or `||` depending on what was ambiguous) if it is determined
329-
/// that the current expression parsed is ambiguous and will break in future.
330-
fn while_if_let_expr_ambiguity(&self, expr: &P<Expr>) -> Option<(Span, BinOpKind)> {
331-
debug!("while_if_let_expr_ambiguity: expr.node: {:?}", expr.node);
338+
/// Visits the `expr` and adjusts whether `let $pat = $expr` is allowed in decendants.
339+
/// Returns whether we walked into `expr` or not.
340+
/// If we did, walking should not happen again.
341+
fn visit_expr_with_let_maybe_allowed(&mut self, expr: &'a Expr) -> bool {
332342
match &expr.node {
333-
ExprKind::Binary(op, _, _) if op.node == BinOpKind::And || op.node == BinOpKind::Or => {
334-
Some((expr.span, op.node))
335-
},
336-
ExprKind::Range(ref lhs, ref rhs, _) => {
337-
let lhs_ambiguous = lhs.as_ref()
338-
.and_then(|lhs| self.while_if_let_expr_ambiguity(lhs));
339-
let rhs_ambiguous = rhs.as_ref()
340-
.and_then(|rhs| self.while_if_let_expr_ambiguity(rhs));
341-
342-
lhs_ambiguous.or(rhs_ambiguous)
343+
// Assuming the context permits, `($expr)` does not impose additional constraints.
344+
ExprKind::Paren(_) => visit::walk_expr(self, expr),
345+
// Assuming the context permits,
346+
// l && r` allows decendants in `l` and `r` to be `let` expressions.
347+
ExprKind::Binary(op, ..) if op.node == BinOpKind::And => visit::walk_expr(self, expr),
348+
// However, we do allow it in the condition of the `if` expression.
349+
// We do not allow `let` in `then` and `opt_else` directly.
350+
ExprKind::If(ref cond, ref then, ref opt_else) => {
351+
self.with_let_allowed(false, |this| {
352+
this.visit_block(then);
353+
walk_list!(this, visit_expr, opt_else);
354+
});
355+
self.with_let_allowed(true, |this| this.visit_expr(cond));
356+
}
357+
// The same logic applies to `While`.
358+
ExprKind::While(ref cond, ref then, ref opt_label) => {
359+
walk_list!(self, visit_label, opt_label);
360+
self.with_let_allowed(false, |this| this.visit_block(then));
361+
self.with_let_allowed(true, |this| this.visit_expr(cond));
343362
}
344-
_ => None,
363+
// Don't walk into `expr` and defer further checks to the caller.
364+
_ => return false,
345365
}
366+
367+
true
346368
}
347369
}
348370

@@ -449,38 +471,27 @@ fn validate_generics_order<'a>(
449471
impl<'a> Visitor<'a> for AstValidator<'a> {
450472
fn visit_expr(&mut self, expr: &'a Expr) {
451473
match expr.node {
452-
ExprKind::IfLet(_, ref expr, _, _) | ExprKind::WhileLet(_, ref expr, _, _) =>
453-
self.while_if_let_ambiguity(&expr),
474+
ExprKind::Let(_, _) if !self.is_let_allowed => {
475+
self.err_handler()
476+
.struct_span_err(expr.span, "`let` expressions are not supported here")
477+
.note("only supported directly in conditions of `if`- and `while`-expressions")
478+
.note("as well as when nested within `&&` and parenthesis in those conditions")
479+
.emit();
480+
}
481+
_ if self.visit_expr_with_let_maybe_allowed(&expr) => {
482+
// Prevent `walk_expr` to happen since we've already done that.
483+
return;
484+
}
454485
ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => {
455486
span_err!(self.session, expr.span, E0472, "asm! is unsupported on this target");
456487
}
457488
ExprKind::ObsoleteInPlace(ref place, ref val) => {
458-
let mut err = self.err_handler().struct_span_err(
459-
expr.span,
460-
"emplacement syntax is obsolete (for now, anyway)",
461-
);
462-
err.note(
463-
"for more information, see \
464-
<https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>"
465-
);
466-
match val.node {
467-
ExprKind::Lit(ref v) if v.node.is_numeric() => {
468-
err.span_suggestion(
469-
place.span.between(val.span),
470-
"if you meant to write a comparison against a negative value, add a \
471-
space in between `<` and `-`",
472-
"< -".to_string(),
473-
Applicability::MaybeIncorrect
474-
);
475-
}
476-
_ => {}
477-
}
478-
err.emit();
489+
self.obsolete_in_place(expr, place, val);
479490
}
480491
_ => {}
481492
}
482493

483-
visit::walk_expr(self, expr)
494+
self.with_let_allowed(false, |this| visit::walk_expr(this, expr));
484495
}
485496

486497
fn visit_ty(&mut self, ty: &'a Ty) {
@@ -862,6 +873,7 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
862873
is_impl_trait_banned: false,
863874
warning_period_57979_didnt_record_next_impl_trait: false,
864875
warning_period_57979_impl_trait_in_proj: false,
876+
is_let_allowed: false,
865877
};
866878
visit::walk_crate(&mut validator, krate);
867879

src/librustc_passes/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
88

99
#![feature(nll)]
10+
#![feature(bind_by_move_pattern_guards)]
1011
#![feature(rustc_diagnostic_macros)]
1112

1213
#![recursion_limit="256"]

0 commit comments

Comments
 (0)