Skip to content

Commit 944cdfd

Browse files
authored
Rollup merge of rust-lang#94754 - c410-f3r:nice-error, r=lcnr
Warn users about `||` in let chain expressions Or more specifically, warn that `||` operators are forbidden. This PR is simple so I guess anyone can review 🤷 cc rust-lang#53667 cc `@matthewjasper`
2 parents cb90a53 + c7a5ad0 commit 944cdfd

File tree

3 files changed

+183
-96
lines changed

3 files changed

+183
-96
lines changed

compiler/rustc_ast_passes/src/ast_validation.rs

+69-39
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ struct AstValidator<'a> {
6464
/// certain positions.
6565
is_assoc_ty_bound_banned: bool,
6666

67-
/// Used to allow `let` expressions in certain syntactic locations.
68-
is_let_allowed: bool,
67+
/// See [ForbiddenLetReason]
68+
forbidden_let_reason: Option<ForbiddenLetReason>,
6969

7070
lint_buffer: &'a mut LintBuffer,
7171
}
@@ -103,20 +103,28 @@ impl<'a> AstValidator<'a> {
103103
self.is_tilde_const_allowed = old;
104104
}
105105

106-
fn with_let_allowed(&mut self, allowed: bool, f: impl FnOnce(&mut Self, bool)) {
107-
let old = mem::replace(&mut self.is_let_allowed, allowed);
106+
fn with_let_management(
107+
&mut self,
108+
forbidden_let_reason: Option<ForbiddenLetReason>,
109+
f: impl FnOnce(&mut Self, Option<ForbiddenLetReason>),
110+
) {
111+
let old = mem::replace(&mut self.forbidden_let_reason, forbidden_let_reason);
108112
f(self, old);
109-
self.is_let_allowed = old;
113+
self.forbidden_let_reason = old;
110114
}
111115

112116
/// Emits an error banning the `let` expression provided in the given location.
113-
fn ban_let_expr(&self, expr: &'a Expr) {
117+
fn ban_let_expr(&self, expr: &'a Expr, or_span: Option<Span>) {
114118
let sess = &self.session;
115119
if sess.opts.unstable_features.is_nightly_build() {
116-
sess.struct_span_err(expr.span, "`let` expressions are not supported here")
117-
.note("only supported directly in conditions of `if`- and `while`-expressions")
118-
.note("as well as when nested within `&&` and parentheses in those conditions")
119-
.emit();
120+
let err = "`let` expressions are not supported here";
121+
let mut diag = sess.struct_span_err(expr.span, err);
122+
diag.note("only supported directly in conditions of `if` and `while` expressions");
123+
diag.note("as well as when nested within `&&` and parentheses in those conditions");
124+
if let Some(elem) = or_span {
125+
diag.span_note(elem, "`||` operators are not allowed in let chain expressions");
126+
}
127+
diag.emit();
120128
} else {
121129
sess.struct_span_err(expr.span, "expected expression, found statement (`let`)")
122130
.note("variable declaration using `let` is a statement")
@@ -988,39 +996,52 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
988996
}
989997

990998
fn visit_expr(&mut self, expr: &'a Expr) {
991-
self.with_let_allowed(false, |this, let_allowed| match &expr.kind {
992-
ExprKind::If(cond, then, opt_else) => {
993-
this.visit_block(then);
994-
walk_list!(this, visit_expr, opt_else);
995-
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
996-
return;
997-
}
998-
ExprKind::Let(..) if !let_allowed => this.ban_let_expr(expr),
999-
ExprKind::Match(expr, arms) => {
1000-
this.visit_expr(expr);
1001-
for arm in arms {
1002-
this.visit_expr(&arm.body);
1003-
this.visit_pat(&arm.pat);
1004-
walk_list!(this, visit_attribute, &arm.attrs);
1005-
if let Some(ref guard) = arm.guard {
1006-
if let ExprKind::Let(_, ref expr, _) = guard.kind {
1007-
this.with_let_allowed(true, |this, _| this.visit_expr(expr));
999+
self.with_let_management(Some(ForbiddenLetReason::GenericForbidden), |this, forbidden_let_reason| {
1000+
match &expr.kind {
1001+
ExprKind::Binary(Spanned { node: BinOpKind::Or, span }, lhs, rhs) => {
1002+
let forbidden_let_reason = Some(ForbiddenLetReason::ForbiddenWithOr(*span));
1003+
this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(lhs));
1004+
this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(rhs));
1005+
}
1006+
ExprKind::If(cond, then, opt_else) => {
1007+
this.visit_block(then);
1008+
walk_list!(this, visit_expr, opt_else);
1009+
this.with_let_management(None, |this, _| this.visit_expr(cond));
1010+
return;
1011+
}
1012+
ExprKind::Let(..) if let Some(elem) = forbidden_let_reason => {
1013+
let or_span = match elem {
1014+
ForbiddenLetReason::ForbiddenWithOr(span) => Some(span),
1015+
ForbiddenLetReason::GenericForbidden => None,
1016+
};
1017+
this.ban_let_expr(expr, or_span);
1018+
},
1019+
ExprKind::Match(scrutinee, arms) => {
1020+
this.visit_expr(scrutinee);
1021+
for arm in arms {
1022+
this.visit_expr(&arm.body);
1023+
this.visit_pat(&arm.pat);
1024+
walk_list!(this, visit_attribute, &arm.attrs);
1025+
if let Some(guard) = &arm.guard && let ExprKind::Let(_, guard_expr, _) = &guard.kind {
1026+
this.with_let_management(None, |this, _| {
1027+
this.visit_expr(guard_expr)
1028+
});
10081029
return;
10091030
}
10101031
}
10111032
}
1033+
ExprKind::Paren(_) | ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
1034+
this.with_let_management(forbidden_let_reason, |this, _| visit::walk_expr(this, expr));
1035+
return;
1036+
}
1037+
ExprKind::While(cond, then, opt_label) => {
1038+
walk_list!(this, visit_label, opt_label);
1039+
this.visit_block(then);
1040+
this.with_let_management(None, |this, _| this.visit_expr(cond));
1041+
return;
1042+
}
1043+
_ => visit::walk_expr(this, expr),
10121044
}
1013-
ExprKind::Paren(_) | ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
1014-
this.with_let_allowed(let_allowed, |this, _| visit::walk_expr(this, expr));
1015-
return;
1016-
}
1017-
ExprKind::While(cond, then, opt_label) => {
1018-
walk_list!(this, visit_label, opt_label);
1019-
this.visit_block(then);
1020-
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
1021-
return;
1022-
}
1023-
_ => visit::walk_expr(this, expr),
10241045
});
10251046
}
10261047

@@ -1772,10 +1793,19 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
17721793
is_tilde_const_allowed: false,
17731794
is_impl_trait_banned: false,
17741795
is_assoc_ty_bound_banned: false,
1775-
is_let_allowed: false,
1796+
forbidden_let_reason: Some(ForbiddenLetReason::GenericForbidden),
17761797
lint_buffer: lints,
17771798
};
17781799
visit::walk_crate(&mut validator, krate);
17791800

17801801
validator.has_proc_macro_decls
17811802
}
1803+
1804+
/// Used to forbid `let` expressions in certain syntactic locations.
1805+
#[derive(Clone, Copy)]
1806+
enum ForbiddenLetReason {
1807+
/// A let chain with the `||` operator
1808+
ForbiddenWithOr(Span),
1809+
/// `let` is not valid and the source environment is not important
1810+
GenericForbidden,
1811+
}

compiler/rustc_ast_passes/src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
//!
55
//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`.
66
7-
#![feature(iter_is_partitioned)]
7+
#![allow(rustc::potential_query_instability)]
88
#![feature(box_patterns)]
9+
#![feature(if_let_guard)]
10+
#![feature(iter_is_partitioned)]
11+
#![feature(let_chains)]
912
#![feature(let_else)]
1013
#![recursion_limit = "256"]
11-
#![allow(rustc::potential_query_instability)]
1214

1315
pub mod ast_validation;
1416
pub mod feature_gate;

0 commit comments

Comments
 (0)