Skip to content

Fix linting false positive when block used as value #141987

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ early_lint_methods!(
pub BuiltinCombinedEarlyLintPass,
[
UnusedParens: UnusedParens::default(),
UnusedBraces: UnusedBraces,
UnusedBraces: UnusedBraces::default(),
UnusedImportBraces: UnusedImportBraces,
UnsafeCode: UnsafeCode,
SpecialModuleName: SpecialModuleName,
Expand Down
56 changes: 55 additions & 1 deletion compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1340,7 +1340,33 @@ declare_lint! {
"unnecessary braces around an expression"
}

declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
#[derive(Default)]
pub(crate) struct UnusedBraces {
parent_followed_by_block: Vec<bool>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parent_followed_by_block: Vec<bool>,
followed_by_block: Vec<bool>,

and please add a short comment why this is used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be informative enough?

// Used for tracking parent expressions that would immediately followed
// by block. Storing false here indicates that expression itself is Block
// expression. This is meant for to prevent report false positive cases where
// expressions with stronger block bind being linted.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing false here indicates that expression itself is Block expression.

that's for the current impl, I think storing false should mean: while we may be inside of an expression which has a trailing block expression, there exists an expression between that one and the current which acts like a block, but may also be a function call or a try-block

}

impl_lint_pass!(UnusedBraces => [UNUSED_BRACES]);

impl UnusedBraces {
#[inline]
fn should_mark_block(e: &ast::Expr) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn should_mark_block(e: &ast::Expr) -> bool {
fn impacts_followed_by_block(e: &ast::Expr) -> bool {

return match e.kind {
ExprKind::Block(..) => true,
_ => Self::is_followed_by_block(e),
};
}

#[inline]
fn is_followed_by_block(e: &ast::Expr) -> bool {
return match e.kind {
ExprKind::If(ref cond, ..) if !matches!(cond.kind, ExprKind::Let(..)) => true,
ExprKind::While(ref cond, ..) if !matches!(cond.kind, ExprKind::Let(..)) => true,
ExprKind::ForLoop { .. } => true,
ExprKind::Match(_, _, ast::MatchKind::Prefix) => true,
_ => false,
};
}
}

impl UnusedDelimLint for UnusedBraces {
const DELIM_STR: &'static str = "braces";
Expand Down Expand Up @@ -1370,6 +1396,11 @@ impl UnusedDelimLint for UnusedBraces {
// - the block does not have a label
// - the block is not `unsafe`
// - the block contains exactly one expression (do not lint `{ expr; }`)
// - however, this does not lint if block is immediately followed by parent
// expression's block, e.g. `if` and `match`, which may cause false positive.
// ```
// if return { return } {} else {}
// ```
// - `followed_by_block` is true and the internal expr may contain a `{`
// - the block is not multiline (do not lint multiline match arms)
// ```
Expand All @@ -1392,6 +1423,11 @@ impl UnusedDelimLint for UnusedBraces {
if let [stmt] = inner.stmts.as_slice() {
if let ast::StmtKind::Expr(ref expr) = stmt.kind {
if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
&& !Self::is_expr_delims_necessary(
expr,
ctx,
self.parent_followed_by_block.last().copied().unwrap_or(false),
)
&& (ctx != UnusedDelimsCtx::AnonConst
|| (matches!(expr.kind, ast::ExprKind::Lit(_))
&& !expr.span.from_expansion()))
Expand Down Expand Up @@ -1430,6 +1466,10 @@ impl EarlyLintPass for UnusedBraces {
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
<Self as UnusedDelimLint>::check_expr(self, cx, e);

if Self::should_mark_block(e) {
self.parent_followed_by_block.push(Self::is_followed_by_block(e));
}

if let ExprKind::Repeat(_, ref anon_const) = e.kind {
self.check_unused_delims_expr(
cx,
Expand All @@ -1443,6 +1483,20 @@ impl EarlyLintPass for UnusedBraces {
}
}

fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
if Self::should_mark_block(e) {
let followed_by_block = self
.parent_followed_by_block
.pop()
.expect("check_expr and check_expr_post must balance");
assert_eq!(
followed_by_block,
Self::is_followed_by_block(e),
"check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
);
}
}

fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
if let ast::GenericArg::Const(ct) = arg {
self.check_unused_delims_expr(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@ run-rustfix
//@ check-pass
#[allow(unreachable_code)]
#[warn(unused_braces)]

fn main() {
return return; //~ WARN: unnecessary braces
if return { return } { return } else { return }
match return { return } {
_ => { return }
}
while return { return } {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@ run-rustfix
//@ check-pass
#[allow(unreachable_code)]
#[warn(unused_braces)]

fn main() {
return { return }; //~ WARN: unnecessary braces
if return { return } { return } else { return }
match return { return } {
_ => { return }
}
while return { return } {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
warning: unnecessary braces around `return` value
--> $DIR/unused-braces-respect-parent-block-issue-141783.rs:7:12
|
LL | return { return };
| ^^ ^^
|
note: the lint level is defined here
--> $DIR/unused-braces-respect-parent-block-issue-141783.rs:4:8
|
LL | #[warn(unused_braces)]
| ^^^^^^^^^^^^^
help: remove these braces
|
LL - return { return };
LL + return return;
|

warning: 1 warning emitted

Loading