Rollup merge of #70081 - lcnr:issue68387, r=varkor

add `unused_braces` lint

Add the lint `unused_braces` which is warn by default.

`unused_parens` is also extended and now checks anon consts.

closes #68387

r? @varkor
This commit is contained in:
Dylan DPC 2020-04-01 00:27:20 +02:00 committed by GitHub
commit 8993358e77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 600 additions and 217 deletions

View file

@ -39,7 +39,7 @@ pub struct IntoIter<T, const N: usize>
alive: Range<usize>,
}
impl<T, const N: usize> IntoIter<T, { N }>
impl<T, const N: usize> IntoIter<T, N>
where
[T; N]: LengthAtMost32,
{
@ -99,7 +99,7 @@ fn as_mut_slice(&mut self) -> &mut [T] {
}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl<T, const N: usize> Iterator for IntoIter<T, { N }>
impl<T, const N: usize> Iterator for IntoIter<T, N>
where
[T; N]: LengthAtMost32,
{
@ -146,7 +146,7 @@ fn last(mut self) -> Option<Self::Item> {
}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, { N }>
impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, N>
where
[T; N]: LengthAtMost32,
{
@ -182,7 +182,7 @@ fn next_back(&mut self) -> Option<Self::Item> {
}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl<T, const N: usize> Drop for IntoIter<T, { N }>
impl<T, const N: usize> Drop for IntoIter<T, N>
where
[T; N]: LengthAtMost32,
{
@ -195,7 +195,7 @@ fn drop(&mut self) {
}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl<T, const N: usize> ExactSizeIterator for IntoIter<T, { N }>
impl<T, const N: usize> ExactSizeIterator for IntoIter<T, N>
where
[T; N]: LengthAtMost32,
{
@ -210,17 +210,17 @@ fn is_empty(&self) -> bool {
}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl<T, const N: usize> FusedIterator for IntoIter<T, { N }> where [T; N]: LengthAtMost32 {}
impl<T, const N: usize> FusedIterator for IntoIter<T, N> where [T; N]: LengthAtMost32 {}
// The iterator indeed reports the correct length. The number of "alive"
// elements (that will still be yielded) is the length of the range `alive`.
// This range is decremented in length in either `next` or `next_back`. It is
// always decremented by 1 in those methods, but only if `Some(_)` is returned.
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, { N }> where [T; N]: LengthAtMost32 {}
unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, N> where [T; N]: LengthAtMost32 {}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl<T: Clone, const N: usize> Clone for IntoIter<T, { N }>
impl<T: Clone, const N: usize> Clone for IntoIter<T, N>
where
[T; N]: LengthAtMost32,
{
@ -249,7 +249,7 @@ fn clone(&self) -> Self {
}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl<T: fmt::Debug, const N: usize> fmt::Debug for IntoIter<T, { N }>
impl<T: fmt::Debug, const N: usize> fmt::Debug for IntoIter<T, N>
where
[T; N]: LengthAtMost32,
{

View file

@ -104,6 +104,11 @@ fn visit_pat(&mut self, p: &'a ast::Pat) {
run_early_pass!(self, check_pat_post, p);
}
fn visit_anon_const(&mut self, c: &'a ast::AnonConst) {
run_early_pass!(self, check_anon_const, c);
ast_visit::walk_anon_const(self, c);
}
fn visit_expr(&mut self, e: &'a ast::Expr) {
self.with_lint_attrs(e.id, &e.attrs, |cx| {
run_early_pass!(cx, check_expr, e);

View file

@ -104,6 +104,7 @@ macro_rules! early_lint_passes {
$args,
[
UnusedParens: UnusedParens,
UnusedBraces: UnusedBraces,
UnusedImportBraces: UnusedImportBraces,
UnsafeCode: UnsafeCode,
AnonymousParameters: AnonymousParameters,
@ -275,6 +276,7 @@ macro_rules! register_passes {
UNUSED_FEATURES,
UNUSED_LABELS,
UNUSED_PARENS,
UNUSED_BRACES,
REDUNDANT_SEMICOLONS
);

View file

@ -170,6 +170,7 @@ macro_rules! early_lint_methods {
fn check_stmt(a: &ast::Stmt);
fn check_arm(a: &ast::Arm);
fn check_pat(a: &ast::Pat);
fn check_anon_const(a: &ast::AnonConst);
fn check_pat_post(a: &ast::Pat);
fn check_expr(a: &ast::Expr);
fn check_expr_post(a: &ast::Expr);

View file

@ -1,5 +1,7 @@
use crate::Lint;
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast::ast;
use rustc_ast::ast::{ExprKind, StmtKind};
use rustc_ast::attr;
use rustc_ast::util::parser;
use rustc_ast_pretty::pprust;
@ -315,16 +317,58 @@ fn check_attribute(&mut self, cx: &LateContext<'_, '_>, attr: &ast::Attribute) {
}
}
declare_lint! {
pub(super) UNUSED_PARENS,
Warn,
"`if`, `match`, `while` and `return` do not need parentheses"
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum UnusedDelimsCtx {
FunctionArg,
MethodArg,
AssignedValue,
IfCond,
WhileCond,
ForIterExpr,
MatchScrutineeExpr,
ReturnValue,
BlockRetValue,
LetScrutineeExpr,
ArrayLenExpr,
AnonConst,
}
declare_lint_pass!(UnusedParens => [UNUSED_PARENS]);
impl From<UnusedDelimsCtx> for &'static str {
fn from(ctx: UnusedDelimsCtx) -> &'static str {
match ctx {
UnusedDelimsCtx::FunctionArg => "function argument",
UnusedDelimsCtx::MethodArg => "method argument",
UnusedDelimsCtx::AssignedValue => "assigned value",
UnusedDelimsCtx::IfCond => "`if` condition",
UnusedDelimsCtx::WhileCond => "`while` condition",
UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression",
UnusedDelimsCtx::ReturnValue => "`return` value",
UnusedDelimsCtx::BlockRetValue => "block return value",
UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
}
}
}
impl UnusedParens {
fn is_expr_parens_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool {
/// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication.
trait UnusedDelimLint {
const DELIM_STR: &'static str;
// this cannot be a constant is it refers to a static.
fn lint(&self) -> &'static Lint;
fn check_unused_delims_expr(
&self,
cx: &EarlyContext<'_>,
value: &ast::Expr,
ctx: UnusedDelimsCtx,
followed_by_block: bool,
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
);
fn is_expr_delims_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool {
followed_by_block
&& match inner.kind {
ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
@ -332,42 +376,160 @@ fn is_expr_parens_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool
}
}
fn check_unused_parens_expr(
fn emit_unused_delims_expr(
&self,
cx: &EarlyContext<'_>,
value: &ast::Expr,
msg: &str,
followed_by_block: bool,
ctx: UnusedDelimsCtx,
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
) {
match value.kind {
ast::ExprKind::Paren(ref inner) => {
if !Self::is_expr_parens_necessary(inner, followed_by_block)
&& value.attrs.is_empty()
&& !value.span.from_expansion()
{
let expr_text =
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) {
snippet
} else {
pprust::expr_to_string(value)
};
let keep_space = (
left_pos.map(|s| s >= value.span.lo()).unwrap_or(false),
right_pos.map(|s| s <= value.span.hi()).unwrap_or(false),
let expr_text = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) {
snippet
} else {
pprust::expr_to_string(value)
};
let keep_space = (
left_pos.map(|s| s >= value.span.lo()).unwrap_or(false),
right_pos.map(|s| s <= value.span.hi()).unwrap_or(false),
);
self.emit_unused_delims(cx, value.span, &expr_text, ctx.into(), keep_space);
}
fn emit_unused_delims(
&self,
cx: &EarlyContext<'_>,
span: Span,
pattern: &str,
msg: &str,
keep_space: (bool, bool),
) {
cx.struct_span_lint(self.lint(), span, |lint| {
let span_msg = format!("unnecessary {} around {}", Self::DELIM_STR, msg);
let mut err = lint.build(&span_msg);
let mut ate_left_paren = false;
let mut ate_right_paren = false;
let parens_removed = pattern.trim_matches(|c| match c {
'(' | '{' => {
if ate_left_paren {
false
} else {
ate_left_paren = true;
true
}
}
')' | '}' => {
if ate_right_paren {
false
} else {
ate_right_paren = true;
true
}
}
_ => false,
});
let replace = {
let mut replace = if keep_space.0 {
let mut s = String::from(" ");
s.push_str(parens_removed);
s
} else {
String::from(parens_removed)
};
if keep_space.1 {
replace.push(' ');
}
replace
};
let suggestion = format!("remove these {}", Self::DELIM_STR);
err.span_suggestion_short(span, &suggestion, replace, Applicability::MachineApplicable);
err.emit();
});
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
use rustc_ast::ast::ExprKind::*;
let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind {
If(ref cond, ref block, ..) => {
let left = e.span.lo() + rustc_span::BytePos(2);
let right = block.span.lo();
(cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right))
}
While(ref cond, ref block, ..) => {
let left = e.span.lo() + rustc_span::BytePos(5);
let right = block.span.lo();
(cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right))
}
ForLoop(_, ref cond, ref block, ..) => {
(cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo()))
}
Match(ref head, _) => {
let left = e.span.lo() + rustc_span::BytePos(5);
(head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None)
}
Ret(Some(ref value)) => {
let left = e.span.lo() + rustc_span::BytePos(3);
(value, UnusedDelimsCtx::ReturnValue, false, Some(left), None)
}
Assign(_, ref value, _) | AssignOp(.., ref value) => {
(value, UnusedDelimsCtx::AssignedValue, false, None, None)
}
// either function/method call, or something this lint doesn't care about
ref call_or_other => {
let (args_to_check, ctx) = match *call_or_other {
Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
// first "argument" is self (which sometimes needs delims)
MethodCall(_, ref args) => (&args[1..], UnusedDelimsCtx::MethodArg),
// actual catch-all arm
_ => {
return;
}
};
// Don't lint if this is a nested macro expansion: otherwise, the lint could
// trigger in situations that macro authors shouldn't have to care about, e.g.,
// when a parenthesized token tree matched in one macro expansion is matched as
// an expression in another and used as a fn/method argument (Issue #47775)
if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
return;
}
for arg in args_to_check {
self.check_unused_delims_expr(cx, arg, ctx, false, None, None);
}
return;
}
};
self.check_unused_delims_expr(cx, &value, ctx, followed_by_block, left_pos, right_pos);
}
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
match s.kind {
StmtKind::Local(ref local) => {
if let Some(ref value) = local.init {
self.check_unused_delims_expr(
cx,
&value,
UnusedDelimsCtx::AssignedValue,
false,
None,
None,
);
Self::remove_outer_parens(cx, value.span, &expr_text, msg, keep_space);
}
}
ast::ExprKind::Let(_, ref expr) => {
// FIXME(#60336): Properly handle `let true = (false && true)`
// actually needing the parenthesis.
self.check_unused_parens_expr(
StmtKind::Expr(ref expr) => {
self.check_unused_delims_expr(
cx,
expr,
"`let` head expression",
followed_by_block,
&expr,
UnusedDelimsCtx::BlockRetValue,
false,
None,
None,
);
@ -376,6 +538,73 @@ fn check_unused_parens_expr(
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
use ast::ItemKind::*;
if let Const(.., Some(expr)) | Static(.., Some(expr)) = &item.kind {
self.check_unused_delims_expr(
cx,
expr,
UnusedDelimsCtx::AssignedValue,
false,
None,
None,
);
}
}
}
declare_lint! {
pub(super) UNUSED_PARENS,
Warn,
"`if`, `match`, `while` and `return` do not need parentheses"
}
declare_lint_pass!(UnusedParens => [UNUSED_PARENS]);
impl UnusedDelimLint for UnusedParens {
const DELIM_STR: &'static str = "parentheses";
fn lint(&self) -> &'static Lint {
UNUSED_PARENS
}
fn check_unused_delims_expr(
&self,
cx: &EarlyContext<'_>,
value: &ast::Expr,
ctx: UnusedDelimsCtx,
followed_by_block: bool,
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
) {
match value.kind {
ast::ExprKind::Paren(ref inner) => {
if !Self::is_expr_delims_necessary(inner, followed_by_block)
&& value.attrs.is_empty()
&& !value.span.from_expansion()
{
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos)
}
}
ast::ExprKind::Let(_, ref expr) => {
// FIXME(#60336): Properly handle `let true = (false && true)`
// actually needing the parenthesis.
self.check_unused_delims_expr(
cx,
expr,
UnusedDelimsCtx::LetScrutineeExpr,
followed_by_block,
None,
None,
);
}
_ => {}
}
}
}
impl UnusedParens {
fn check_unused_parens_pat(
&self,
cx: &EarlyContext<'_>,
@ -406,132 +635,18 @@ fn check_unused_parens_pat(
} else {
pprust::pat_to_string(value)
};
Self::remove_outer_parens(cx, value.span, &pattern_text, "pattern", (false, false));
self.emit_unused_delims(cx, value.span, &pattern_text, "pattern", (false, false));
}
}
fn remove_outer_parens(
cx: &EarlyContext<'_>,
span: Span,
pattern: &str,
msg: &str,
keep_space: (bool, bool),
) {
cx.struct_span_lint(UNUSED_PARENS, span, |lint| {
let span_msg = format!("unnecessary parentheses around {}", msg);
let mut err = lint.build(&span_msg);
let mut ate_left_paren = false;
let mut ate_right_paren = false;
let parens_removed = pattern.trim_matches(|c| match c {
'(' => {
if ate_left_paren {
false
} else {
ate_left_paren = true;
true
}
}
')' => {
if ate_right_paren {
false
} else {
ate_right_paren = true;
true
}
}
_ => false,
});
let replace = {
let mut replace = if keep_space.0 {
let mut s = String::from(" ");
s.push_str(parens_removed);
s
} else {
String::from(parens_removed)
};
if keep_space.1 {
replace.push(' ');
}
replace
};
err.span_suggestion_short(
span,
"remove these parentheses",
replace,
Applicability::MachineApplicable,
);
err.emit();
});
}
}
impl EarlyLintPass for UnusedParens {
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
use rustc_ast::ast::ExprKind::*;
let (value, msg, followed_by_block, left_pos, right_pos) = match e.kind {
Let(ref pat, ..) => {
self.check_unused_parens_pat(cx, pat, false, false);
return;
}
if let ExprKind::Let(ref pat, ..) | ExprKind::ForLoop(ref pat, ..) = e.kind {
self.check_unused_parens_pat(cx, pat, false, false);
}
If(ref cond, ref block, ..) => {
let left = e.span.lo() + rustc_span::BytePos(2);
let right = block.span.lo();
(cond, "`if` condition", true, Some(left), Some(right))
}
While(ref cond, ref block, ..) => {
let left = e.span.lo() + rustc_span::BytePos(5);
let right = block.span.lo();
(cond, "`while` condition", true, Some(left), Some(right))
}
ForLoop(ref pat, ref cond, ref block, ..) => {
self.check_unused_parens_pat(cx, pat, false, false);
(cond, "`for` head expression", true, None, Some(block.span.lo()))
}
Match(ref head, _) => {
let left = e.span.lo() + rustc_span::BytePos(5);
(head, "`match` head expression", true, Some(left), None)
}
Ret(Some(ref value)) => {
let left = e.span.lo() + rustc_span::BytePos(3);
(value, "`return` value", false, Some(left), 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 => {
let (args_to_check, call_kind) = match *call_or_other {
Call(_, ref args) => (&args[..], "function"),
// first "argument" is self (which sometimes needs parens)
MethodCall(_, ref args) => (&args[1..], "method"),
// actual catch-all arm
_ => {
return;
}
};
// Don't lint if this is a nested macro expansion: otherwise, the lint could
// trigger in situations that macro authors shouldn't have to care about, e.g.,
// when a parenthesized token tree matched in one macro expansion is matched as
// an expression in another and used as a fn/method argument (Issue #47775)
if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
return;
}
let msg = format!("{} argument", call_kind);
for arg in args_to_check {
self.check_unused_parens_expr(cx, arg, &msg, false, None, None);
}
return;
}
};
self.check_unused_parens_expr(cx, &value, msg, followed_by_block, left_pos, right_pos);
<Self as UnusedDelimLint>::check_expr(self, cx, e)
}
fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
@ -556,22 +671,16 @@ fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
}
}
fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) {
self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None);
}
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
use ast::StmtKind::*;
match s.kind {
Local(ref local) => {
self.check_unused_parens_pat(cx, &local.pat, false, false);
if let Some(ref value) = local.init {
self.check_unused_parens_expr(cx, &value, "assigned value", false, None, None);
}
}
Expr(ref expr) => {
self.check_unused_parens_expr(cx, &expr, "block return value", false, None, None);
}
_ => {}
if let StmtKind::Local(ref local) = s.kind {
self.check_unused_parens_pat(cx, &local.pat, false, false);
}
<Self as UnusedDelimLint>::check_stmt(self, cx, s)
}
fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
@ -587,6 +696,16 @@ fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
match &r.kind {
&ast::TyKind::TraitObject(..) => {}
&ast::TyKind::ImplTrait(_, ref bounds) if bounds.len() > 1 => {}
&ast::TyKind::Array(_, ref len) => {
self.check_unused_delims_expr(
cx,
&len.value,
UnusedDelimsCtx::ArrayLenExpr,
false,
None,
None,
);
}
_ => {
let pattern_text =
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(ty.span) {
@ -595,21 +714,136 @@ fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
pprust::ty_to_string(ty)
};
Self::remove_outer_parens(cx, ty.span, &pattern_text, "type", (false, false));
self.emit_unused_delims(cx, ty.span, &pattern_text, "type", (false, false));
}
}
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
use ast::ItemKind::*;
<Self as UnusedDelimLint>::check_item(self, cx, item)
}
}
if let Const(.., Some(expr)) | Static(.., Some(expr)) = &item.kind {
self.check_unused_parens_expr(cx, expr, "assigned value", false, None, None);
declare_lint! {
pub(super) UNUSED_BRACES,
Warn,
"unnecessary braces around an expression"
}
declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
impl UnusedDelimLint for UnusedBraces {
const DELIM_STR: &'static str = "braces";
fn lint(&self) -> &'static Lint {
UNUSED_BRACES
}
fn check_unused_delims_expr(
&self,
cx: &EarlyContext<'_>,
value: &ast::Expr,
ctx: UnusedDelimsCtx,
followed_by_block: bool,
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
) {
match value.kind {
ast::ExprKind::Block(ref inner, None)
if inner.rules == ast::BlockCheckMode::Default =>
{
// emit a warning under the following conditions:
//
// - the block does not have a label
// - the block is not `unsafe`
// - the block contains exactly one expression (do not lint `{ expr; }`)
// - `followed_by_block` is true and the internal expr may contain a `{`
// - the block is not multiline (do not lint multiline match arms)
// ```
// match expr {
// Pattern => {
// somewhat_long_expression
// }
// // ...
// }
// ```
// - the block has no attribute and was not created inside a macro
// - if the block is an `anon_const`, the inner expr must be a literal
// (do not lint `struct A<const N: usize>; let _: A<{ 2 + 3 }>;`)
//
// 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)
&& (ctx != UnusedDelimsCtx::AnonConst
|| matches!(expr.kind, ast::ExprKind::Lit(_)))
// array length expressions are checked during `check_anon_const` and `check_ty`,
// once as `ArrayLenExpr` and once as `AnonConst`.
//
// As we do not want to lint this twice, we do not emit an error for
// `ArrayLenExpr` if `AnonConst` would do the same.
&& (ctx != UnusedDelimsCtx::ArrayLenExpr
|| !matches!(expr.kind, ast::ExprKind::Lit(_)))
&& !cx.sess().source_map().is_multiline(value.span)
&& value.attrs.is_empty()
&& !value.span.from_expansion()
{
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos)
}
}
}
}
ast::ExprKind::Let(_, ref expr) => {
// FIXME(#60336): Properly handle `let true = (false && true)`
// actually needing the parenthesis.
self.check_unused_delims_expr(
cx,
expr,
UnusedDelimsCtx::LetScrutineeExpr,
followed_by_block,
None,
None,
);
}
_ => {}
}
}
}
impl EarlyLintPass for UnusedBraces {
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
<Self as UnusedDelimLint>::check_expr(self, cx, e)
}
fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) {
self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None);
}
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
<Self as UnusedDelimLint>::check_stmt(self, cx, s)
}
fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
if let &ast::TyKind::Paren(ref r) = &ty.kind {
if let ast::TyKind::Array(_, ref len) = r.kind {
self.check_unused_delims_expr(
cx,
&len.value,
UnusedDelimsCtx::ArrayLenExpr,
false,
None,
None,
);
}
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
<Self as UnusedDelimLint>::check_item(self, cx, item)
}
}
declare_lint! {
UNUSED_IMPORT_BRACES,
Allow,

View file

@ -356,9 +356,11 @@ fn try_load_from_disk(
quote! { #t }
})
.unwrap_or(quote! { _ });
// expr is a `Block`, meaning that `{ #expr }` gets expanded
// to `{ { stmts... } }`, which triggers the `unused_braces` lint.
quote! {
#[inline]
#[allow(unused_variables)]
#[allow(unused_variables, unused_braces)]
fn cache_on_disk(
#tcx: TyCtxt<'tcx>,
#key: Self::Key,

View file

@ -2808,7 +2808,7 @@ pub fn resolve_str_path_error(
ast::Path {
span,
segments: iter::once(Ident::with_dummy_span(kw::PathRoot))
.chain({ path_str.split("::").skip(1).map(Ident::from_str) })
.chain(path_str.split("::").skip(1).map(Ident::from_str))
.map(|i| self.new_ast_path_segment(i))
.collect(),
}

View file

@ -115,8 +115,7 @@ pub unsafe fn read_overlapped(
) -> io::Result<Option<usize>> {
let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD;
let mut amt = 0;
let res =
cvt({ c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped) });
let res = cvt(c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped));
match res {
Ok(_) => Ok(Some(amt as usize)),
Err(e) => {
@ -139,7 +138,7 @@ pub fn overlapped_result(
unsafe {
let mut bytes = 0;
let wait = if wait { c::TRUE } else { c::FALSE };
let res = cvt({ c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait) });
let res = cvt(c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait));
match res {
Ok(_) => Ok(bytes as usize),
Err(e) => {

View file

@ -4,7 +4,7 @@
struct FakeArray<T, const N: usize>(T);
impl<T, const N: usize> FakeArray<T, { N }> {
impl<T, const N: usize> FakeArray<T, N> {
fn len(&self) -> usize {
N
}

View file

@ -9,7 +9,7 @@ fn test_big_vec() {}
#[cfg(target_pointer_width = "64")]
fn test_big_vec()
{
assert_eq!(size_of::<[u8; (1 << 32)]>(), (1 << 32));
assert_eq!(size_of::<[u8; 1 << 32]>(), (1 << 32));
}
fn main() {

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_braces)]
fn force<F>(f: F) -> isize where F: FnOnce() -> isize { return f(); }

View file

@ -1,5 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![allow(non_snake_case)]
#![allow(unused_variables)]
// Test that destructors for rvalue temporaries run either at end of

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![feature(box_syntax)]
use std::cell::RefCell;

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![allow(dead_code)]
// pretty-expanded FIXME #23616

View file

@ -7,13 +7,13 @@ trait HasSize {
const SIZE: usize;
}
impl<const X: usize> HasSize for ArrayHolder<{ X }> {
impl<const X: usize> HasSize for ArrayHolder<X> {
const SIZE: usize = X;
}
struct ArrayHolder<const X: usize>([u32; X]);
impl<const X: usize> ArrayHolder<{ X }> {
impl<const X: usize> ArrayHolder<X> {
pub const fn new() -> Self {
ArrayHolder([0; Self::SIZE])
//~^ ERROR: mismatched types

View file

@ -13,4 +13,4 @@ fn foo() -> usize {
}
}
impl Foo<{3}> for () {}
impl Foo<3> for () {}

View file

@ -0,0 +1,13 @@
// check-pass
#![warn(unused_braces)]
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
struct A<const N: usize>;
fn main() {
let _: A<7>; // ok
let _: A<{ 7 }>; //~ WARN unnecessary braces
let _: A<{ 3 + 5 }>; // ok
}

View file

@ -0,0 +1,20 @@
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
--> $DIR/unused_braces.rs:4:12
|
LL | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
warning: unnecessary braces around const expression
--> $DIR/unused_braces.rs:11:14
|
LL | let _: A<{ 7 }>;
| ^^^^^ help: remove these braces
|
note: the lint level is defined here
--> $DIR/unused_braces.rs:2:9
|
LL | #![warn(unused_braces)]
| ^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![allow(dead_code)]
#![allow(unused_unsafe)]

View file

@ -1,5 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![feature(box_syntax)]
fn test_generic<T, F>(expected: Box<T>, eq: F) where T: Clone, F: FnOnce(Box<T>, Box<T>) -> bool {

View file

@ -1,5 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![feature(box_syntax)]
fn test_generic<T, F>(expected: T, eq: F) where T: Clone, F: FnOnce(T, T) -> bool {

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_braces)]
fn test_generic<T: Clone, F>(expected: T, eq: F) where F: FnOnce(T, T) -> bool {
let actual: T = { expected.clone() };

View file

@ -1,5 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![feature(box_syntax)]
pub fn main() { let x: Box<_> = { box 100 }; assert_eq!(*x, 100); }

View file

@ -1,10 +1,7 @@
// run-pass
#![allow(unused_braces)]
#![allow(dead_code)]
// Tests for standalone blocks as expressions
fn test_basic() { let rs: bool = { true }; assert!((rs)); }

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_braces)]
fn test_int() {
fn f() -> isize { 10 }

View file

@ -1,5 +1,5 @@
// run-pass
#![allow(unused_braces)]
fn foo(i: isize) -> isize { i + 1 }

View file

@ -1,6 +1,6 @@
// run-pass
// Test a rather underspecified example:
#![allow(unused_braces)]
pub fn main() {
let f = {|i| i};

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![allow(unused_unsafe)]
#![allow(unreachable_code)]
// ignore-emscripten no threads support

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_parens)]
#![allow(non_camel_case_types)]
// Note: This test was used to demonstrate #5873 (now #23898).

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_braces)]
fn main() {
let v1 = { 1 + {2} * {3} };
let v2 = 1 + {2} * {3} ;

View file

@ -48,11 +48,11 @@ fn main() {
if (true) {} //~ ERROR unnecessary parentheses around `if` condition
while (true) {} //~ ERROR unnecessary parentheses around `while` condition
//~^ WARN denote infinite loops with
match (true) { //~ ERROR unnecessary parentheses around `match` head expression
match (true) { //~ ERROR unnecessary parentheses around `match` scrutinee expression
_ => {}
}
if let 1 = (1) {} //~ ERROR unnecessary parentheses around `let` head expression
while let 1 = (2) {} //~ ERROR unnecessary parentheses around `let` head expression
if let 1 = (1) {} //~ ERROR unnecessary parentheses around `let` scrutinee expression
while let 1 = (2) {} //~ ERROR unnecessary parentheses around `let` scrutinee expression
let v = X { y: false };
// struct lits needs parens, so these shouldn't warn.
if (v == X { y: true }) {}

View file

@ -72,19 +72,19 @@ LL | while (true) {}
|
= note: `#[warn(while_true)]` on by default
error: unnecessary parentheses around `match` head expression
error: unnecessary parentheses around `match` scrutinee expression
--> $DIR/lint-unnecessary-parens.rs:51:11
|
LL | match (true) {
| ^^^^^^ help: remove these parentheses
error: unnecessary parentheses around `let` head expression
error: unnecessary parentheses around `let` scrutinee expression
--> $DIR/lint-unnecessary-parens.rs:54:16
|
LL | if let 1 = (1) {}
| ^^^ help: remove these parentheses
error: unnecessary parentheses around `let` head expression
error: unnecessary parentheses around `let` scrutinee expression
--> $DIR/lint-unnecessary-parens.rs:55:19
|
LL | while let 1 = (2) {}

View file

@ -0,0 +1,31 @@
// check-pass
#![warn(unused_braces, unused_parens)]
fn main() {
let _ = (7);
//~^WARN unnecessary parentheses
let _ = { 7 };
//~^ WARN unnecessary braces
if let 7 = { 7 } {
//~^ WARN unnecessary braces
}
let _: [u8; { 3 }];
//~^ WARN unnecessary braces
// do not emit error for multiline blocks.
let _ = {
7
};
// do not emit error for unsafe blocks.
let _ = unsafe { 7 };
// do not emit error, as the `{` would then
// be parsed as part of the `return`.
if { return } {
}
}

View file

@ -0,0 +1,36 @@
warning: unnecessary parentheses around assigned value
--> $DIR/unused_braces.rs:5:13
|
LL | let _ = (7);
| ^^^ help: remove these parentheses
|
note: the lint level is defined here
--> $DIR/unused_braces.rs:2:24
|
LL | #![warn(unused_braces, unused_parens)]
| ^^^^^^^^^^^^^
warning: unnecessary braces around assigned value
--> $DIR/unused_braces.rs:8:13
|
LL | let _ = { 7 };
| ^^^^^ help: remove these braces
|
note: the lint level is defined here
--> $DIR/unused_braces.rs:2:9
|
LL | #![warn(unused_braces, unused_parens)]
| ^^^^^^^^^^^^^
warning: unnecessary braces around `let` scrutinee expression
--> $DIR/unused_braces.rs:11:16
|
LL | if let 7 = { 7 } {
| ^^^^^ help: remove these braces
warning: unnecessary braces around const expression
--> $DIR/unused_braces.rs:15:17
|
LL | let _: [u8; { 3 }];
| ^^^^^ help: remove these braces

View file

@ -0,0 +1,22 @@
// check-pass
#![warn(unused_braces)]
// changing `&{ expr }` to `&expr` changes the semantic of the program
// so we should not warn this case
#[repr(packed)]
struct A {
a: u8,
b: u32,
}
fn main() {
let a = A {
a: 42,
b: 1729,
};
let _ = &{ a.b };
let _ = { a.b };
//~^ WARN unnecessary braces
}

View file

@ -0,0 +1,12 @@
warning: unnecessary braces around assigned value
--> $DIR/unused_parens_borrow.rs:20:13
|
LL | let _ = { a.b };
| ^^^^^^^ help: remove these braces
|
note: the lint level is defined here
--> $DIR/unused_parens_borrow.rs:2:9
|
LL | #![warn(unused_braces)]
| ^^^^^^^^^^^^^

View file

@ -46,14 +46,14 @@ LL | while(true && false) {
| ^^^^^^^^^^^^^^^ help: remove these parentheses
"}
{"message":"unnecessary parentheses around `for` head expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":987,"byte_end":995,"line_start":44,"line_end":44,"column_start":18,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){
{"message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":987,"byte_end":995,"line_start":44,"line_end":44,"column_start":18,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){
--> $DIR/unused_parens_remove_json_suggestion.rs:44:18
|
LL | for _ in (0 .. 3){
| ^^^^^^^^ help: remove these parentheses
"}
{"message":"unnecessary parentheses around `for` head expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1088,"byte_end":1096,"line_start":49,"line_end":49,"column_start":14,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {
{"message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1088,"byte_end":1096,"line_start":49,"line_end":49,"column_start":14,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {
--> $DIR/unused_parens_remove_json_suggestion.rs:49:14
|
LL | for _ in (0 .. 3) {

View file

@ -1,5 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![allow(unused_comparisons)]
#![allow(dead_code)]
#![allow(unused_mut)]

View file

@ -1,7 +1,7 @@
// run-pass
// Test inclusive range syntax.
#![feature(range_is_empty)]
#![allow(unused_braces)]
#![allow(unused_comparisons)]
use std::ops::RangeToInclusive;

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug)]

View file

@ -18,16 +18,16 @@ trait MyTrait<'a, const C: usize> {
const MY_CONST: usize;
}
impl<'a, const C: usize> MyTrait<'a, { C }> for MyStruct<{ C }> {
impl<'a, const C: usize> MyTrait<'a, C> for MyStruct<C> {
type MyItem = u8;
const MY_CONST: usize = C;
}
impl<'a, I, const C: usize> UnwrapItemsExt<'a, { C }> for I {
type Iter = impl MyTrait<'a, { C }>;
impl<'a, I, const C: usize> UnwrapItemsExt<'a, C> for I {
type Iter = impl MyTrait<'a, C>;
fn unwrap_items(self) -> Self::Iter {
MyStruct::<{ C }> {}
MyStruct::<C> {}
}
}

View file

@ -1,5 +1,5 @@
// run-pass
#![allow(unused_braces, unused_parens)]
#![feature(unsized_tuple_coercion, unsized_locals)]
struct A<X: ?Sized>(X);
@ -30,7 +30,6 @@ fn main() {
*foo()
});
udrop::<[u8]>({*foo()});
#[allow(unused_parens)]
udrop::<[u8]>((*foo()));
udrop::<[u8]>((*tfoo()).1);
*afoo() + 42;

View file

@ -5,7 +5,7 @@
#![allow(non_camel_case_types)]
#![allow(dead_code)]
#![allow(unreachable_code)]
#![allow(unused_parens)]
#![allow(unused_braces, unused_parens)]
#![recursion_limit = "256"]

View file

@ -1,4 +1,5 @@
// run-pass
#![allow(unused_braces)]
#![allow(unused_assignments)]
// Make sure that the constructor args are codegened for zero-sized tuple structs