use crate::Lint;
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
-use rustc_ast::util::parser;
+use rustc_ast::util::{classify, parser};
use rustc_ast::{ExprKind, StmtKind};
use rustc_ast_pretty::pprust;
use rustc_errors::{pluralize, Applicability};
FunctionArg,
MethodArg,
AssignedValue,
+ AssignedValueLetElse,
IfCond,
WhileCond,
ForIterExpr,
match ctx {
UnusedDelimsCtx::FunctionArg => "function argument",
UnusedDelimsCtx::MethodArg => "method argument",
- UnusedDelimsCtx::AssignedValue => "assigned value",
+ UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
+ "assigned value"
+ }
UnusedDelimsCtx::IfCond => "`if` condition",
UnusedDelimsCtx::WhileCond => "`while` condition",
UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
right_pos: Option<BytePos>,
);
- fn is_expr_delims_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool {
+ fn is_expr_delims_necessary(
+ inner: &ast::Expr,
+ followed_by_block: bool,
+ followed_by_else: bool,
+ ) -> bool {
+ if followed_by_else {
+ match inner.kind {
+ ast::ExprKind::Binary(op, ..) if op.node.lazy() => return true,
+ _ if classify::expr_trailing_brace(inner).is_some() => return true,
+ _ => {}
+ }
+ }
+
// Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`
let lhs_needs_parens = {
let mut innermost = inner;
loop {
if let ExprKind::Binary(_, lhs, _rhs) = &innermost.kind {
innermost = lhs;
- if !rustc_ast::util::classify::expr_requires_semi_to_be_stmt(innermost) {
+ if !classify::expr_requires_semi_to_be_stmt(innermost) {
break true;
}
} else {
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
match s.kind {
StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
- if let Some(value) = local.kind.init() {
- self.check_unused_delims_expr(
- cx,
- &value,
- UnusedDelimsCtx::AssignedValue,
- false,
- None,
- None,
- );
+ if let Some((init, els)) = local.kind.init_else_opt() {
+ let ctx = match els {
+ None => UnusedDelimsCtx::AssignedValue,
+ Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
+ };
+ self.check_unused_delims_expr(cx, init, ctx, false, None, None);
}
}
StmtKind::Expr(ref expr) => {
) {
match value.kind {
ast::ExprKind::Paren(ref inner) => {
- if !Self::is_expr_delims_necessary(inner, followed_by_block)
+ let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
+ if !Self::is_expr_delims_necessary(inner, followed_by_block, followed_by_else)
&& value.attrs.is_empty()
&& !value.span.from_expansion()
&& (ctx != UnusedDelimsCtx::LetScrutineeExpr
// FIXME(const_generics): handle paths when #67075 is fixed.
if let [stmt] = inner.stmts.as_slice() {
if let ast::StmtKind::Expr(ref expr) = stmt.kind {
- if !Self::is_expr_delims_necessary(expr, followed_by_block)
+ if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
&& (ctx != UnusedDelimsCtx::AnonConst
|| matches!(expr.kind, ast::ExprKind::Lit(_)))
&& !cx.sess().source_map().is_multiline(value.span)
Some(init) => {
if self.eat_keyword(kw::Else) {
let els = self.parse_block()?;
+ self.check_let_else_init_bool_expr(&init);
+ self.check_let_else_init_trailing_brace(&init);
LocalKind::InitElse(init, els)
} else {
LocalKind::Init(init)
Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None }))
}
+ fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
+ if let ast::ExprKind::Binary(op, ..) = init.kind {
+ if op.node.lazy() {
+ let suggs = vec![
+ (init.span.shrink_to_lo(), "(".to_string()),
+ (init.span.shrink_to_hi(), ")".to_string()),
+ ];
+ self.struct_span_err(
+ init.span,
+ &format!(
+ "a `{}` expression cannot be directly assigned in `let...else`",
+ op.node.to_string()
+ ),
+ )
+ .multipart_suggestion(
+ "wrap the expression in parenthesis",
+ suggs,
+ Applicability::MachineApplicable,
+ )
+ .emit();
+ }
+ }
+ }
+
+ fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
+ if let Some(trailing) = classify::expr_trailing_brace(init) {
+ let err_span = trailing.span.with_lo(trailing.span.hi() - BytePos(1));
+ let suggs = vec![
+ (trailing.span.shrink_to_lo(), "(".to_string()),
+ (trailing.span.shrink_to_hi(), ")".to_string()),
+ ];
+ self.struct_span_err(
+ err_span,
+ "right curly brace `}` before `else` in a `let...else` statement not allowed",
+ )
+ .multipart_suggestion(
+ "try wrapping the expression in parenthesis",
+ suggs,
+ Applicability::MachineApplicable,
+ )
+ .emit();
+ }
+ }
+
/// Parses the RHS of a local variable declaration (e.g., `= 14;`).
fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<P<Expr>>> {
let eq_consumed = match self.token.kind {