rust/compiler/rustc_lint/src/unused.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1624 lines
58 KiB
Rust
Raw Normal View History

2022-09-04 19:46:35 +00:00
use std::iter;
use std::ops::ControlFlow;
2020-04-27 17:56:11 +00:00
use rustc_ast as ast;
use rustc_ast::util::{classify, parser};
2020-04-27 17:56:11 +00:00
use rustc_ast::{ExprKind, StmtKind};
2022-09-04 19:46:35 +00:00
use rustc_errors::{pluralize, MultiSpan};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
2024-06-14 18:46:32 +00:00
use rustc_hir::{self as hir, LangItem};
2023-04-06 23:11:19 +00:00
use rustc_infer::traits::util::elaborate;
use rustc_middle::ty::{self, adjustment, Ty};
use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
2020-01-01 18:30:57 +00:00
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{BytePos, Span};
use tracing::instrument;
use crate::lints::{
2022-09-04 19:46:35 +00:00
PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
2023-10-19 16:06:43 +00:00
UnusedAllocationMutDiag, UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion,
UnusedDelim, UnusedDelimSuggestion, UnusedImportBracesDiag, UnusedOp, UnusedOpSuggestion,
UnusedResult,
};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Lint, LintContext};
declare_lint! {
2020-09-08 22:09:57 +00:00
/// The `unused_must_use` lint detects unused result of a type flagged as
/// `#[must_use]`.
///
/// ### Example
///
/// ```rust
/// fn returns_result() -> Result<(), ()> {
/// Ok(())
/// }
///
/// fn main() {
/// returns_result();
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// The `#[must_use]` attribute is an indicator that it is a mistake to
/// ignore the value. See [the reference] for more details.
///
/// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
pub UNUSED_MUST_USE,
Warn,
"unused result of a type flagged as `#[must_use]`",
report_in_external_macro
}
declare_lint! {
2020-09-08 22:09:57 +00:00
/// The `unused_results` lint checks for the unused result of an
/// expression in a statement.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(unused_results)]
/// fn foo<T>() -> T { panic!() }
///
/// fn main() {
/// foo::<usize>();
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Ignoring the return value of a function may indicate a mistake. In
/// cases were it is almost certain that the result should be used, it is
/// recommended to annotate the function with the [`must_use` attribute].
/// Failure to use such a return value will trigger the [`unused_must_use`
/// lint] which is warn-by-default. The `unused_results` lint is
/// essentially the same, but triggers for *all* return values.
///
/// This lint is "allow" by default because it can be noisy, and may not be
/// an actual problem. For example, calling the `remove` method of a `Vec`
/// or `HashMap` returns the previous value, which you may not care about.
/// Using this lint would require explicitly ignoring or discarding such
/// values.
///
/// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
/// [`unused_must_use` lint]: warn-by-default.html#unused-must-use
pub UNUSED_RESULTS,
Allow,
"unused result of an expression in a statement"
}
declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]);
impl<'tcx> LateLintPass<'tcx> for UnusedResults {
fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
let hir::StmtKind::Semi(mut expr) = s.kind else {
return;
};
let mut expr_is_from_block = false;
while let hir::ExprKind::Block(blk, ..) = expr.kind
&& let hir::Block { expr: Some(e), .. } = blk
{
expr = e;
expr_is_from_block = true;
}
if let hir::ExprKind::Ret(..) = expr.kind {
return;
}
if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
&& let ty = cx.typeck_results().expr_ty(await_expr)
&& let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind()
&& cx.tcx.ty_is_opaque_future(ty)
2022-08-17 15:21:43 +00:00
&& let async_fn_def_id = cx.tcx.parent(*future_def_id)
&& matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn)
// Check that this `impl Future` actually comes from an `async fn`
&& cx.tcx.asyncness(async_fn_def_id).is_async()
2022-08-17 15:21:43 +00:00
&& check_must_use_def(
cx,
async_fn_def_id,
expr.span,
"output of future returned by ",
"",
expr_is_from_block,
2022-08-17 15:21:43 +00:00
)
{
// We have a bare `foo().await;` on an opaque type from an async function that was
// annotated with `#[must_use]`.
return;
}
let ty = cx.typeck_results().expr_ty(expr);
let must_use_result = is_ty_must_use(cx, ty, expr, expr.span);
let type_lint_emitted_or_suppressed = match must_use_result {
Some(path) => {
emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);
true
}
None => false,
};
let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block);
if !fn_warned && type_lint_emitted_or_suppressed {
// We don't warn about unused unit or uninhabited types.
// (See https://github.com/rust-lang/rust/issues/43806 for details.)
return;
}
let must_use_op = match expr.kind {
// Hardcoding operators here seemed more expedient than the
// refactoring that would be needed to look up the `#[must_use]`
// attribute which does exist on the comparison trait methods
2018-07-11 12:05:29 +00:00
hir::ExprKind::Binary(bin_op, ..) => match bin_op.node {
2018-07-12 00:20:31 +00:00
hir::BinOpKind::Eq
| hir::BinOpKind::Lt
| hir::BinOpKind::Le
| hir::BinOpKind::Ne
| hir::BinOpKind::Ge
| hir::BinOpKind::Gt => Some("comparison"),
hir::BinOpKind::Add
| hir::BinOpKind::Sub
| hir::BinOpKind::Div
| hir::BinOpKind::Mul
| hir::BinOpKind::Rem => Some("arithmetic operation"),
2018-07-11 10:44:53 +00:00
hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),
2018-07-12 00:20:31 +00:00
hir::BinOpKind::BitXor
| hir::BinOpKind::BitAnd
| hir::BinOpKind::BitOr
| hir::BinOpKind::Shl
| hir::BinOpKind::Shr => Some("bitwise operation"),
},
hir::ExprKind::AddrOf(..) => Some("borrow"),
hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"),
2018-07-11 12:05:29 +00:00
hir::ExprKind::Unary(..) => Some("unary operation"),
_ => None,
};
let mut op_warned = false;
if let Some(must_use_op) = must_use_op {
cx.emit_span_lint(
2022-09-04 19:46:35 +00:00
UNUSED_MUST_USE,
expr.span,
UnusedOp {
op: must_use_op,
label: expr.span,
suggestion: if expr_is_from_block {
UnusedOpSuggestion::BlockTailExpr {
before_span: expr.span.shrink_to_lo(),
after_span: expr.span.shrink_to_hi(),
}
} else {
UnusedOpSuggestion::NormalExpr { span: expr.span.shrink_to_lo() }
},
2022-09-04 19:46:35 +00:00
},
);
op_warned = true;
}
if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) {
cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
}
fn check_fn_must_use(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
expr_is_from_block: bool,
) -> bool {
let maybe_def_id = match expr.kind {
hir::ExprKind::Call(callee, _) => {
match callee.kind {
hir::ExprKind::Path(ref qpath) => {
match cx.qpath_res(qpath, callee.hir_id) {
Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id),
// `Res::Local` if it was a closure, for which we
// do not currently support must-use linting
_ => None,
}
}
_ => None,
}
}
hir::ExprKind::MethodCall(..) => {
cx.typeck_results().type_dependent_def_id(expr.hir_id)
}
_ => None,
};
if let Some(def_id) = maybe_def_id {
check_must_use_def(
cx,
def_id,
expr.span,
"return value of ",
"",
expr_is_from_block,
)
} else {
false
}
}
/// A path through a type to a must_use source. Contains useful info for the lint.
#[derive(Debug)]
enum MustUsePath {
/// Suppress must_use checking.
Suppressed,
/// The root of the normal must_use lint with an optional message.
Def(Span, DefId, Option<Symbol>),
Boxed(Box<Self>),
Pinned(Box<Self>),
Opaque(Box<Self>),
TraitObject(Box<Self>),
TupleElement(Vec<(usize, Self)>),
Array(Box<Self>, u64),
/// The root of the unused_closures lint.
Closure(Span),
2023-10-19 21:46:28 +00:00
/// The root of the unused_coroutines lint.
2023-10-19 16:06:43 +00:00
Coroutine(Span),
}
#[instrument(skip(cx, expr), level = "debug", ret)]
fn is_ty_must_use<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
2019-11-30 14:08:22 +00:00
expr: &hir::Expr<'_>,
2019-05-23 21:56:23 +00:00
span: Span,
) -> Option<MustUsePath> {
if ty.is_unit()
2022-10-23 22:32:17 +00:00
|| !ty.is_inhabited_from(
cx.tcx,
cx.tcx.parent_module(expr.hir_id).to_def_id(),
cx.param_env,
)
{
return Some(MustUsePath::Suppressed);
}
2020-08-02 22:49:11 +00:00
match *ty.kind() {
ty::Adt(..) if ty.is_box() => {
let boxed_ty = ty.boxed_ty();
is_ty_must_use(cx, boxed_ty, expr, span)
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
}
2024-06-14 18:46:32 +00:00
ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
let pinned_ty = args.type_at(0);
is_ty_must_use(cx, pinned_ty, expr, span)
.map(|inner| MustUsePath::Pinned(Box::new(inner)))
}
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => {
elaborate(
cx.tcx,
2024-07-06 15:54:22 +00:00
cx.tcx.explicit_item_super_predicates(def).iter_identity_copied(),
)
// We only care about self bounds for the impl-trait
.filter_only_self()
.find_map(|(pred, _span)| {
// We only look at the `DefId`, so it is safe to skip the binder here.
if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
pred.kind().skip_binder()
{
let def_id = poly_trait_predicate.trait_ref.def_id;
is_def_must_use(cx, def_id, span)
} else {
None
}
})
.map(|inner| MustUsePath::Opaque(Box::new(inner)))
2019-05-23 21:56:23 +00:00
}
2022-12-10 20:31:01 +00:00
ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
{
let def_id = trait_ref.def_id;
is_def_must_use(cx, def_id, span)
2022-12-27 17:53:29 +00:00
.map(|inner| MustUsePath::TraitObject(Box::new(inner)))
2022-12-10 20:31:01 +00:00
} else {
None
}
}),
ty::Tuple(tys) => {
let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
debug_assert_eq!(elem_exprs.len(), tys.len());
elem_exprs
} else {
&[]
};
// Default to `expr`.
let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));
let nested_must_use = tys
.iter()
.zip(elem_exprs)
.enumerate()
.filter_map(|(i, (ty, expr))| {
is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path))
})
.collect::<Vec<_>>();
if !nested_must_use.is_empty() {
Some(MustUsePath::TupleElement(nested_must_use))
} else {
None
}
2019-05-23 21:56:23 +00:00
}
ty::Array(ty, len) => match len.try_eval_target_usize(cx.tcx, cx.param_env) {
// If the array is empty we don't lint, to avoid false positives
Some(0) | None => None,
2019-06-29 15:23:15 +00:00
// If the array is definitely non-empty, we can do `#[must_use]` checking.
Some(len) => is_ty_must_use(cx, ty, expr, span)
.map(|inner| MustUsePath::Array(Box::new(inner), len)),
2019-06-29 15:23:15 +00:00
},
ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)),
2023-10-19 16:06:43 +00:00
ty::Coroutine(def_id, ..) => {
// async fn should be treated as "implementor of `Future`"
2023-10-19 21:46:28 +00:00
let must_use = if cx.tcx.coroutine_is_async(def_id) {
2023-10-13 00:28:49 +00:00
let def_id = cx.tcx.lang_items().future_trait()?;
is_def_must_use(cx, def_id, span)
.map(|inner| MustUsePath::Opaque(Box::new(inner)))
} else {
None
};
2023-10-19 16:06:43 +00:00
must_use.or(Some(MustUsePath::Coroutine(span)))
}
_ => None,
}
}
fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) {
// check for #[must_use = "..."]
let reason = attr.value_str();
Some(MustUsePath::Def(span, def_id, reason))
} else {
None
}
}
// Returns whether further errors should be suppressed because either a lint has been
// emitted or the type should be ignored.
fn check_must_use_def(
cx: &LateContext<'_>,
def_id: DefId,
span: Span,
descr_pre_path: &str,
descr_post_path: &str,
expr_is_from_block: bool,
) -> bool {
is_def_must_use(cx, def_id, span)
.map(|must_use_path| {
emit_must_use_untranslated(
cx,
&must_use_path,
descr_pre_path,
descr_post_path,
1,
false,
expr_is_from_block,
)
})
.is_some()
}
#[instrument(skip(cx), level = "debug")]
fn emit_must_use_untranslated(
cx: &LateContext<'_>,
path: &MustUsePath,
descr_pre: &str,
descr_post: &str,
plural_len: usize,
is_inner: bool,
expr_is_from_block: bool,
) {
let plural_suffix = pluralize!(plural_len);
match path {
MustUsePath::Suppressed => {}
MustUsePath::Boxed(path) => {
let descr_pre = &format!("{descr_pre}boxed ");
emit_must_use_untranslated(
cx,
path,
descr_pre,
descr_post,
plural_len,
true,
expr_is_from_block,
);
}
MustUsePath::Pinned(path) => {
let descr_pre = &format!("{descr_pre}pinned ");
emit_must_use_untranslated(
cx,
path,
descr_pre,
descr_post,
plural_len,
true,
expr_is_from_block,
);
}
MustUsePath::Opaque(path) => {
let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
emit_must_use_untranslated(
cx,
path,
descr_pre,
descr_post,
plural_len,
true,
expr_is_from_block,
);
}
MustUsePath::TraitObject(path) => {
let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
emit_must_use_untranslated(
cx,
path,
descr_pre,
descr_post,
plural_len,
true,
expr_is_from_block,
);
}
MustUsePath::TupleElement(elems) => {
for (index, path) in elems {
let descr_post = &format!(" in tuple element {index}");
emit_must_use_untranslated(
cx,
path,
descr_pre,
descr_post,
plural_len,
true,
expr_is_from_block,
);
}
}
MustUsePath::Array(path, len) => {
let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
emit_must_use_untranslated(
cx,
path,
descr_pre,
descr_post,
plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
true,
expr_is_from_block,
);
}
MustUsePath::Closure(span) => {
cx.emit_span_lint(
2022-09-16 07:01:02 +00:00
UNUSED_MUST_USE,
*span,
2022-09-04 19:46:35 +00:00
UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
2022-09-16 07:01:02 +00:00
);
}
2023-10-19 16:06:43 +00:00
MustUsePath::Coroutine(span) => {
cx.emit_span_lint(
2022-09-16 07:01:02 +00:00
UNUSED_MUST_USE,
*span,
2023-10-19 16:06:43 +00:00
UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
2022-09-16 07:01:02 +00:00
);
}
MustUsePath::Def(span, def_id, reason) => {
cx.emit_span_lint(
2022-09-04 19:46:35 +00:00
UNUSED_MUST_USE,
*span,
UnusedDef {
pre: descr_pre,
post: descr_post,
cx,
def_id: *def_id,
note: *reason,
suggestion: (!is_inner).then_some(if expr_is_from_block {
UnusedDefSuggestion::BlockTailExpr {
before_span: span.shrink_to_lo(),
after_span: span.shrink_to_hi(),
}
} else {
UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
}),
2022-09-04 19:46:35 +00:00
},
);
}
}
}
}
}
declare_lint! {
2020-09-08 22:09:57 +00:00
/// The `path_statements` lint detects path statements with no effect.
///
/// ### Example
///
/// ```rust
/// let x = 42;
///
/// x;
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// It is usually a mistake to have a statement that has no effect.
pub PATH_STATEMENTS,
Warn,
"path statements with no effect"
}
declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
impl<'tcx> LateLintPass<'tcx> for PathStatements {
fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
if let hir::StmtKind::Semi(expr) = s.kind {
if let hir::ExprKind::Path(_) = expr.kind {
2022-09-16 07:01:02 +00:00
let ty = cx.typeck_results().expr_ty(expr);
if ty.needs_drop(cx.tcx, cx.param_env) {
2022-09-04 19:46:35 +00:00
let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
{
PathStatementDropSub::Suggestion { span: s.span, snippet }
} else {
PathStatementDropSub::Help { span: s.span }
};
cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
2022-09-16 07:01:02 +00:00
} else {
cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
2022-09-16 07:01:02 +00:00
}
}
}
}
}
2020-03-27 20:54:52 +00:00
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum UnusedDelimsCtx {
FunctionArg,
MethodArg,
AssignedValue,
AssignedValueLetElse,
2020-03-27 20:54:52 +00:00
IfCond,
WhileCond,
2020-03-31 16:42:54 +00:00
ForIterExpr,
MatchScrutineeExpr,
2020-03-27 20:54:52 +00:00
ReturnValue,
BlockRetValue,
2020-03-31 16:42:54 +00:00
LetScrutineeExpr,
2020-03-27 20:54:52 +00:00
ArrayLenExpr,
AnonConst,
2022-08-03 04:00:04 +00:00
MatchArmExpr,
IndexExpr,
}
2020-03-27 20:54:52 +00:00
impl From<UnusedDelimsCtx> for &'static str {
fn from(ctx: UnusedDelimsCtx) -> &'static str {
match ctx {
UnusedDelimsCtx::FunctionArg => "function argument",
UnusedDelimsCtx::MethodArg => "method argument",
UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
"assigned value"
}
2020-03-27 20:54:52 +00:00
UnusedDelimsCtx::IfCond => "`if` condition",
UnusedDelimsCtx::WhileCond => "`while` condition",
2020-03-31 16:42:54 +00:00
UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression",
2020-03-27 20:54:52 +00:00
UnusedDelimsCtx::ReturnValue => "`return` value",
UnusedDelimsCtx::BlockRetValue => "block return value",
2020-03-31 16:42:54 +00:00
UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
2020-03-27 20:54:52 +00:00
UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
2022-08-03 04:00:04 +00:00
UnusedDelimsCtx::MatchArmExpr => "match arm expression",
UnusedDelimsCtx::IndexExpr => "index expression",
2020-03-27 20:54:52 +00:00
}
}
}
2020-03-27 20:54:52 +00:00
/// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication.
trait UnusedDelimLint {
const DELIM_STR: &'static str;
/// Due to `ref` pattern, there can be a difference between using
/// `{ expr }` and `expr` in pattern-matching contexts. This means
/// that we should only lint `unused_parens` and not `unused_braces`
/// in this case.
///
/// ```rust
/// let mut a = 7;
/// let ref b = { a }; // We actually borrow a copy of `a` here.
/// a += 1; // By mutating `a` we invalidate any borrows of `a`.
/// assert_eq!(b + 1, a); // `b` does not borrow `a`, so we can still use it here.
/// ```
const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
2020-03-27 20:54:52 +00:00
// 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>,
is_kw: bool,
2020-03-27 20:54:52 +00:00
);
fn is_expr_delims_necessary(
inner: &ast::Expr,
ctx: UnusedDelimsCtx,
followed_by_block: bool,
) -> bool {
let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
if followed_by_else {
match inner.kind {
ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
_ if classify::expr_trailing_brace(inner).is_some() => return true,
_ => {}
}
}
// Check it's range in LetScrutineeExpr
if let ast::ExprKind::Range(..) = inner.kind
&& matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
{
return true;
}
// Check if LHS needs parens to prevent false-positives in cases like
// `fn x() -> u8 { ({ 0 } + 1) }`.
//
// FIXME: https://github.com/rust-lang/rust/issues/119426
// The syntax tree in this code is from after macro expansion, so the
// current implementation has both false negatives and false positives
// related to expressions containing macros.
//
// macro_rules! m1 {
// () => {
// 1
// };
// }
//
// fn f1() -> u8 {
// // Lint says parens are not needed, but they are.
// (m1! {} + 1)
// }
//
// macro_rules! m2 {
// () => {
// loop { break 1; }
// };
// }
//
// fn f2() -> u8 {
// // Lint says parens are needed, but they are not.
// (m2!() + 1)
// }
{
let mut innermost = inner;
loop {
innermost = match &innermost.kind {
ExprKind::Binary(_op, lhs, _rhs) => lhs,
ExprKind::Call(fn_, _params) => fn_,
ExprKind::Cast(expr, _ty) => expr,
ExprKind::Type(expr, _ty) => expr,
ExprKind::Index(base, _subscript, _) => base,
_ => break,
};
if !classify::expr_requires_semi_to_be_stmt(innermost) {
return true;
}
}
}
// Check if RHS needs parens to prevent false-positives in cases like `if (() == return)
// {}`.
if !followed_by_block {
return false;
}
// Check if we need parens for `match &( Struct { feild: }) {}`.
{
let mut innermost = inner;
loop {
innermost = match &innermost.kind {
ExprKind::AddrOf(_, _, expr) => expr,
_ => {
if parser::contains_exterior_struct_lit(innermost) {
return true;
} else {
break;
}
}
}
}
}
let mut innermost = inner;
loop {
innermost = match &innermost.kind {
ExprKind::Unary(_op, expr) => expr,
ExprKind::Binary(_op, _lhs, rhs) => rhs,
ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
ExprKind::Assign(_lhs, rhs, _span) => rhs,
ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
ExprKind::Break(_label, None) => return false,
ExprKind::Break(_label, Some(break_expr)) => {
return matches!(break_expr.kind, ExprKind::Block(..));
}
ExprKind::Range(_lhs, Some(rhs), _limits) => {
return matches!(rhs.kind, ExprKind::Block(..));
}
_ => return parser::contains_exterior_struct_lit(inner),
}
}
}
2020-03-27 20:54:52 +00:00
fn emit_unused_delims_expr(
&self,
cx: &EarlyContext<'_>,
value: &ast::Expr,
2020-03-27 20:54:52 +00:00
ctx: UnusedDelimsCtx,
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
is_kw: bool,
) {
// If `value` has `ExprKind::Err`, unused delim lint can be broken.
// For example, the following code caused ICE.
// This is because the `ExprKind::Call` in `value` has `ExprKind::Err` as its argument
// and this leads to wrong spans. #104897
//
// ```
// fn f(){(print!(á
// ```
use rustc_ast::visit::{walk_expr, Visitor};
2024-02-24 07:52:59 +00:00
struct ErrExprVisitor;
impl<'ast> Visitor<'ast> for ErrExprVisitor {
2024-02-24 07:52:59 +00:00
type Result = ControlFlow<()>;
fn visit_expr(&mut self, expr: &'ast ast::Expr) -> ControlFlow<()> {
if let ExprKind::Err(_) = expr.kind {
2024-02-24 07:52:59 +00:00
ControlFlow::Break(())
} else {
walk_expr(self, expr)
}
}
}
2024-02-24 07:52:59 +00:00
if ErrExprVisitor.visit_expr(value).is_break() {
return;
}
let spans = match value.kind {
ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => block.stmts[0]
.span
.find_ancestor_inside(value.span)
.map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))),
ast::ExprKind::Paren(ref expr) => {
expr.span.find_ancestor_inside(value.span).map(|expr_span| {
(value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi()))
})
}
_ => return,
2020-03-27 20:54:52 +00:00
};
let keep_space = (
left_pos.is_some_and(|s| s >= value.span.lo()),
right_pos.is_some_and(|s| s <= value.span.hi()),
2020-03-27 20:54:52 +00:00
);
self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
}
2020-03-27 20:54:52 +00:00
fn emit_unused_delims(
&self,
2019-08-02 18:17:20 +00:00
cx: &EarlyContext<'_>,
value_span: Span,
spans: Option<(Span, Span)>,
2019-08-02 18:17:20 +00:00
msg: &str,
keep_space: (bool, bool),
is_kw: bool,
2019-08-02 18:17:20 +00:00
) {
let primary_span = if let Some((lo, hi)) = spans {
if hi.is_empty() {
// do not point at delims that do not exist
return;
}
MultiSpan::from(vec![lo, hi])
} else {
MultiSpan::from(value_span)
};
2022-09-04 19:46:35 +00:00
let suggestion = spans.map(|(lo, hi)| {
let sm = cx.sess().source_map();
let lo_replace = if (keep_space.0 || is_kw)
2022-11-18 09:30:47 +00:00
&& let Ok(snip) = sm.span_to_prev_source(lo)
&& !snip.ends_with(' ')
{
2022-09-04 19:46:35 +00:00
" "
} else {
2022-09-04 19:46:35 +00:00
""
};
2022-09-04 19:46:35 +00:00
let hi_replace = if keep_space.1
2022-11-18 09:30:47 +00:00
&& let Ok(snip) = sm.span_to_next_source(hi)
&& !snip.starts_with(' ')
{
2022-09-04 19:46:35 +00:00
" "
} else {
2022-09-04 19:46:35 +00:00
""
};
2022-09-04 19:46:35 +00:00
UnusedDelimSuggestion {
start_span: lo,
start_replace: lo_replace,
end_span: hi,
end_replace: hi_replace,
}
});
cx.emit_span_lint(
2022-09-04 19:46:35 +00:00
self.lint(),
primary_span,
UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
);
2018-10-04 16:36:55 +00:00
}
2019-02-08 11:35:41 +00:00
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
2020-04-27 17:56:11 +00:00
use rustc_ast::ExprKind::*;
let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
// Do not lint `unused_braces` in `if let` expressions.
If(ref cond, ref block, _)
if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
{
let left = e.span.lo() + rustc_span::BytePos(2);
let right = block.span.lo();
(cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
}
// Do not lint `unused_braces` in `while let` expressions.
While(ref cond, ref block, ..)
if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
{
let left = e.span.lo() + rustc_span::BytePos(5);
let right = block.span.lo();
(cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
}
2023-12-08 22:51:50 +00:00
ForLoop { ref iter, ref body, .. } => {
(iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
}
Match(ref head, _, ast::MatchKind::Prefix)
if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
{
let left = e.span.lo() + rustc_span::BytePos(5);
(head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
}
Ret(Some(ref value)) => {
let left = e.span.lo() + rustc_span::BytePos(3);
(value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
}
Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
2020-03-27 20:54:52 +00:00
Assign(_, ref value, _) | AssignOp(.., ref value) => {
(value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
2020-03-27 20:54:52 +00:00
}
wherein the parens lint keeps its own counsel re args in nested macros In #46980 ("in which the unused-parens lint..." (14982db2d6)), the unused-parens lint was made to check function and method arguments, which it previously did not (seemingly due to oversight rather than willful design). However, in #47775 and discussion thereon, user–developers of Geal/nom and graphql-rust/juniper reported that the lint was seemingly erroneously triggering on certain complex macros in those projects. While this doesn't seem like a bug in the lint in the particular strict sense that the expanded code would, in fact, contain unncecessary parentheses, it also doesn't seem like the sort of thing macro authors should have to think about: the spirit of the unused-parens lint is to prevent needless clutter in code, not to give macro authors extra heartache in the handling of token trees. We propose the expediency of declining to lint unused parentheses in function or method args inside of nested expansions: we believe that this should eliminate the petty, troublesome lint warnings reported in the issue, without forgoing the benefits of the lint in simpler macros. It seemed like too much duplicated code for the `Call` and `MethodCall` match arms to duplicate the nested-macro check in addition to each having their own `for` loop, so this occasioned a slight refactor so that the function and method cases could share code—hopefully the overall intent is at least no less clear to the gentle reader. This is concerning #47775.
2018-01-31 01:13:05 +00:00
// either function/method call, or something this lint doesn't care about
ref call_or_other => {
2020-03-27 20:54:52 +00:00
let (args_to_check, ctx) = match *call_or_other {
Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
wherein the parens lint keeps its own counsel re args in nested macros In #46980 ("in which the unused-parens lint..." (14982db2d6)), the unused-parens lint was made to check function and method arguments, which it previously did not (seemingly due to oversight rather than willful design). However, in #47775 and discussion thereon, user–developers of Geal/nom and graphql-rust/juniper reported that the lint was seemingly erroneously triggering on certain complex macros in those projects. While this doesn't seem like a bug in the lint in the particular strict sense that the expanded code would, in fact, contain unncecessary parentheses, it also doesn't seem like the sort of thing macro authors should have to think about: the spirit of the unused-parens lint is to prevent needless clutter in code, not to give macro authors extra heartache in the handling of token trees. We propose the expediency of declining to lint unused parentheses in function or method args inside of nested expansions: we believe that this should eliminate the petty, troublesome lint warnings reported in the issue, without forgoing the benefits of the lint in simpler macros. It seemed like too much duplicated code for the `Call` and `MethodCall` match arms to duplicate the nested-macro check in addition to each having their own `for` loop, so this occasioned a slight refactor so that the function and method cases could share code—hopefully the overall intent is at least no less clear to the gentle reader. This is concerning #47775.
2018-01-31 01:13:05 +00:00
// actual catch-all arm
_ => {
return;
}
2018-10-01 21:15:22 +00:00
};
wherein the parens lint keeps its own counsel re args in nested macros In #46980 ("in which the unused-parens lint..." (14982db2d6)), the unused-parens lint was made to check function and method arguments, which it previously did not (seemingly due to oversight rather than willful design). However, in #47775 and discussion thereon, user–developers of Geal/nom and graphql-rust/juniper reported that the lint was seemingly erroneously triggering on certain complex macros in those projects. While this doesn't seem like a bug in the lint in the particular strict sense that the expanded code would, in fact, contain unncecessary parentheses, it also doesn't seem like the sort of thing macro authors should have to think about: the spirit of the unused-parens lint is to prevent needless clutter in code, not to give macro authors extra heartache in the handling of token trees. We propose the expediency of declining to lint unused parentheses in function or method args inside of nested expansions: we believe that this should eliminate the petty, troublesome lint warnings reported in the issue, without forgoing the benefits of the lint in simpler macros. It seemed like too much duplicated code for the `Call` and `MethodCall` match arms to duplicate the nested-macro check in addition to each having their own `for` loop, so this occasioned a slight refactor so that the function and method cases could share code—hopefully the overall intent is at least no less clear to the gentle reader. This is concerning #47775.
2018-01-31 01:13:05 +00:00
// 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;
wherein the parens lint keeps its own counsel re args in nested macros In #46980 ("in which the unused-parens lint..." (14982db2d6)), the unused-parens lint was made to check function and method arguments, which it previously did not (seemingly due to oversight rather than willful design). However, in #47775 and discussion thereon, user–developers of Geal/nom and graphql-rust/juniper reported that the lint was seemingly erroneously triggering on certain complex macros in those projects. While this doesn't seem like a bug in the lint in the particular strict sense that the expanded code would, in fact, contain unncecessary parentheses, it also doesn't seem like the sort of thing macro authors should have to think about: the spirit of the unused-parens lint is to prevent needless clutter in code, not to give macro authors extra heartache in the handling of token trees. We propose the expediency of declining to lint unused parentheses in function or method args inside of nested expansions: we believe that this should eliminate the petty, troublesome lint warnings reported in the issue, without forgoing the benefits of the lint in simpler macros. It seemed like too much duplicated code for the `Call` and `MethodCall` match arms to duplicate the nested-macro check in addition to each having their own `for` loop, so this occasioned a slight refactor so that the function and method cases could share code—hopefully the overall intent is at least no less clear to the gentle reader. This is concerning #47775.
2018-01-31 01:13:05 +00:00
}
for arg in args_to_check {
self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
}
return;
}
};
self.check_unused_delims_expr(
cx,
value,
ctx,
followed_by_block,
left_pos,
right_pos,
is_kw,
);
2020-03-27 20:54:52 +00:00
}
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
match s.kind {
StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
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, false);
2020-03-27 20:54:52 +00:00
}
}
StmtKind::Expr(ref expr) => {
self.check_unused_delims_expr(
cx,
expr,
2020-03-27 20:54:52 +00:00
UnusedDelimsCtx::BlockRetValue,
false,
None,
None,
false,
2020-03-27 20:54:52 +00:00
);
}
_ => {}
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
use ast::ItemKind::*;
2023-03-29 12:34:05 +00:00
if let Const(box ast::ConstItem { expr: Some(expr), .. })
| Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
{
2020-03-27 20:54:52 +00:00
self.check_unused_delims_expr(
cx,
expr,
UnusedDelimsCtx::AssignedValue,
false,
None,
None,
false,
2020-03-27 20:54:52 +00:00
);
}
}
}
declare_lint! {
2020-09-08 22:09:57 +00:00
/// The `unused_parens` lint detects `if`, `match`, `while` and `return`
/// with parentheses; they do not need them.
///
/// ### Examples
///
/// ```rust
/// if(true) {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// The parentheses are not needed, and should be removed. This is the
2020-09-08 22:09:57 +00:00
/// preferred style for writing these expressions.
2020-03-27 20:54:52 +00:00
pub(super) UNUSED_PARENS,
Warn,
"`if`, `match`, `while` and `return` do not need parentheses"
}
2023-01-14 08:52:46 +00:00
pub struct UnusedParens {
with_self_ty_parens: bool,
/// `1 as (i32) < 2` parses to ExprKind::Lt
/// `1 as i32 < 2` parses to i32::<2[missing angle bracket]
parens_in_cast_in_lt: Vec<ast::NodeId>,
2023-01-14 08:52:46 +00:00
}
impl UnusedParens {
pub fn new() -> Self {
Self { with_self_ty_parens: false, parens_in_cast_in_lt: Vec::new() }
2023-01-14 08:52:46 +00:00
}
}
impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
2020-03-27 20:54:52 +00:00
impl UnusedDelimLint for UnusedParens {
const DELIM_STR: &'static str = "parentheses";
const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
2020-03-27 20:54:52 +00:00
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>,
is_kw: bool,
2020-03-27 20:54:52 +00:00
) {
match value.kind {
ast::ExprKind::Paren(ref inner) => {
if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
2020-03-27 20:54:52 +00:00
&& value.attrs.is_empty()
&& !value.span.from_expansion()
&& (ctx != UnusedDelimsCtx::LetScrutineeExpr
2020-10-27 00:02:06 +00:00
|| !matches!(inner.kind, ast::ExprKind::Binary(
rustc_span::source_map::Spanned { node, .. },
_,
_,
) if node.is_lazy()))
2020-03-27 20:54:52 +00:00
{
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
2020-03-27 20:54:52 +00:00
}
}
ast::ExprKind::Let(_, ref expr, _, _) => {
2020-03-27 20:54:52 +00:00
self.check_unused_delims_expr(
cx,
expr,
2020-03-31 16:42:54 +00:00
UnusedDelimsCtx::LetScrutineeExpr,
2020-03-27 20:54:52 +00:00
followed_by_block,
None,
None,
false,
2020-03-27 20:54:52 +00:00
);
}
_ => {}
}
}
}
impl UnusedParens {
fn check_unused_parens_pat(
&self,
cx: &EarlyContext<'_>,
value: &ast::Pat,
avoid_or: bool,
avoid_mut: bool,
keep_space: (bool, bool),
2020-03-27 20:54:52 +00:00
) {
use ast::{BindingMode, PatKind};
2020-03-27 20:54:52 +00:00
if let PatKind::Paren(inner) = &value.kind {
match inner.kind {
// The lint visitor will visit each subpattern of `p`. We do not want to lint
// any range pattern no matter where it occurs in the pattern. For something like
// `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume
// that if there are unnecessary parens they serve a purpose of readability.
PatKind::Range(..) => return,
// Avoid `p0 | .. | pn` if we should.
PatKind::Or(..) if avoid_or => return,
// Avoid `mut x` and `mut x @ p` if we should:
PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
2022-08-30 22:34:35 +00:00
return;
}
2020-03-27 20:54:52 +00:00
// Otherwise proceed with linting.
_ => {}
}
let spans = if !value.span.from_expansion() {
inner
.span
.find_ancestor_inside(value.span)
.map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
} else {
None
};
self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
2020-03-27 20:54:52 +00:00
}
}
fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
&& (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
{
let mut cur = lhs;
while let ExprKind::Binary(_, _, rhs) = &cur.kind {
cur = rhs;
}
if let ExprKind::Cast(_, ty) = &cur.kind
&& let ast::TyKind::Paren(_) = &ty.kind
{
return Some(ty.id);
}
}
None
}
2020-03-27 20:54:52 +00:00
}
impl EarlyLintPass for UnusedParens {
#[inline]
2020-03-27 20:54:52 +00:00
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
if let Some(ty_id) = self.cast_followed_by_lt(e) {
self.parens_in_cast_in_lt.push(ty_id);
}
match e.kind {
2023-12-08 22:51:50 +00:00
ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
self.check_unused_parens_pat(cx, pat, false, false, (true, true));
}
// We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
// handle a hard error for them during AST lowering in `lower_expr_mut`, but we still
// want to complain about things like `if let 42 = (42)`.
ExprKind::If(ref cond, ref block, ref else_)
if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
{
self.check_unused_delims_expr(
cx,
cond.peel_parens(),
UnusedDelimsCtx::LetScrutineeExpr,
true,
None,
None,
true,
);
for stmt in &block.stmts {
<Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
}
if let Some(e) = else_ {
<Self as UnusedDelimLint>::check_expr(self, cx, e);
}
return;
}
ExprKind::Match(ref _expr, ref arm, _) => {
2022-08-03 04:00:04 +00:00
for a in arm {
2023-11-27 02:15:56 +00:00
if let Some(body) = &a.body {
self.check_unused_delims_expr(
cx,
body,
UnusedDelimsCtx::MatchArmExpr,
false,
None,
None,
true,
);
}
2022-08-03 04:00:04 +00:00
}
}
_ => {}
2020-03-27 20:54:52 +00:00
}
<Self as UnusedDelimLint>::check_expr(self, cx, e)
}
fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
if let Some(ty_id) = self.cast_followed_by_lt(e) {
let id = self
.parens_in_cast_in_lt
.pop()
.expect("check_expr and check_expr_post must balance");
2023-09-01 05:40:43 +00:00
assert_eq!(
id, ty_id,
2023-09-01 05:40:43 +00:00
"check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
);
}
}
fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
use ast::Mutability;
use ast::PatKind::*;
let keep_space = (false, false);
2019-09-26 15:18:31 +00:00
match &p.kind {
// Do not lint on `(..)` as that will result in the other arms being useless.
Paren(_)
// The other cases do not contain sub-patterns.
2024-01-17 02:14:16 +00:00
| Wild | Never | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {},
// These are list-like patterns; parens can always be removed.
TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
self.check_unused_parens_pat(cx, p, false, false, keep_space);
},
Struct(_, _, fps, _) => for f in fps {
self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
},
// Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
// Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
// Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
}
}
2020-03-27 20:54:52 +00:00
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
if let StmtKind::Let(ref local) = s.kind {
self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
2018-10-01 21:15:22 +00:00
}
2020-03-27 20:54:52 +00:00
<Self as UnusedDelimLint>::check_stmt(self, cx, s)
}
fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
self.check_unused_parens_pat(cx, &param.pat, true, false, (false, false));
}
fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
}
fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
if let ast::TyKind::Paren(_) = ty.kind
&& Some(&ty.id) == self.parens_in_cast_in_lt.last()
{
return;
}
2023-01-16 12:44:14 +00:00
match &ty.kind {
ast::TyKind::Array(_, len) => {
self.check_unused_delims_expr(
cx,
&len.value,
UnusedDelimsCtx::ArrayLenExpr,
false,
None,
None,
false,
2023-01-16 12:44:14 +00:00
);
}
ast::TyKind::Paren(r) => {
match &r.kind {
ast::TyKind::TraitObject(..) => {}
ast::TyKind::BareFn(b)
if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
2024-06-05 20:18:52 +00:00
ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
2023-01-16 12:44:14 +00:00
_ => {
let spans = if !ty.span.from_expansion() {
r.span
.find_ancestor_inside(ty.span)
.map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
} else {
None
};
self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
2023-01-16 12:44:14 +00:00
}
}
2023-01-16 12:44:14 +00:00
self.with_self_ty_parens = false;
}
2023-01-16 12:44:14 +00:00
_ => {}
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
2020-03-27 20:54:52 +00:00
<Self as UnusedDelimLint>::check_item(self, cx, item)
}
2023-01-14 08:52:46 +00:00
fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
use rustc_ast::{WhereBoundPredicate, WherePredicate};
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
bounded_ty,
bound_generic_params,
..
}) = pred
&& let ast::TyKind::Paren(_) = &bounded_ty.kind
&& bound_generic_params.is_empty()
{
self.with_self_ty_parens = true;
}
}
fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
2023-01-16 12:44:14 +00:00
assert!(!self.with_self_ty_parens);
2023-01-14 08:52:46 +00:00
}
2020-03-27 20:54:52 +00:00
}
2020-03-27 20:54:52 +00:00
declare_lint! {
2020-09-08 22:09:57 +00:00
/// The `unused_braces` lint detects unnecessary braces around an
/// expression.
///
/// ### Example
///
/// ```rust
/// if { true } {
/// // ...
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// The braces are not needed, and should be removed. This is the
/// preferred style for writing these expressions.
2020-03-27 20:54:52 +00:00
pub(super) UNUSED_BRACES,
Warn,
2020-03-31 16:42:54 +00:00
"unnecessary braces around an expression"
2020-03-27 20:54:52 +00:00
}
declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
impl UnusedDelimLint for UnusedBraces {
const DELIM_STR: &'static str = "braces";
const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
2020-03-27 20:54:52 +00:00
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>,
is_kw: bool,
2020-03-27 20:54:52 +00:00
) {
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
// not created by a macro, i.e. do not lint on:
// ```
// struct A<const N: usize>;
// let _: A<{ 2 + 3 }>;
// let _: A<{produces_literal!()}>;
// ```
2020-03-27 20:54:52 +00:00
// 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, ctx, followed_by_block)
2020-03-27 20:54:52 +00:00
&& (ctx != UnusedDelimsCtx::AnonConst
|| (matches!(expr.kind, ast::ExprKind::Lit(_))
&& !expr.span.from_expansion()))
2020-03-27 20:54:52 +00:00
&& !cx.sess().source_map().is_multiline(value.span)
&& value.attrs.is_empty()
&& !value.span.from_expansion()
2022-12-09 23:21:12 +00:00
&& !inner.span.from_expansion()
2020-03-27 20:54:52 +00:00
{
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
2020-03-27 20:54:52 +00:00
}
}
}
}
ast::ExprKind::Let(_, ref expr, _, _) => {
2020-03-27 20:54:52 +00:00
self.check_unused_delims_expr(
cx,
expr,
2020-03-31 16:42:54 +00:00
UnusedDelimsCtx::LetScrutineeExpr,
2020-03-27 20:54:52 +00:00
followed_by_block,
None,
None,
false,
2020-03-27 20:54:52 +00:00
);
}
_ => {}
}
}
}
impl EarlyLintPass for UnusedBraces {
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
<Self as UnusedDelimLint>::check_stmt(self, cx, s)
}
#[inline]
2020-03-27 20:54:52 +00:00
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
<Self as UnusedDelimLint>::check_expr(self, cx, e);
if let ExprKind::Repeat(_, ref anon_const) = e.kind {
self.check_unused_delims_expr(
cx,
&anon_const.value,
UnusedDelimsCtx::AnonConst,
false,
None,
None,
false,
);
}
2020-03-27 20:54:52 +00:00
}
fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
if let ast::GenericArg::Const(ct) = arg {
self.check_unused_delims_expr(
cx,
&ct.value,
UnusedDelimsCtx::AnonConst,
false,
None,
None,
false,
);
}
2020-03-27 20:54:52 +00:00
}
fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
if let Some(anon_const) = &v.disr_expr {
self.check_unused_delims_expr(
cx,
&anon_const.value,
UnusedDelimsCtx::AnonConst,
false,
None,
None,
false,
);
}
2020-03-27 20:54:52 +00:00
}
fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
match ty.kind {
ast::TyKind::Array(_, ref len) => {
2020-03-27 20:54:52 +00:00
self.check_unused_delims_expr(
cx,
&len.value,
UnusedDelimsCtx::ArrayLenExpr,
false,
None,
None,
false,
2020-03-27 20:54:52 +00:00
);
}
ast::TyKind::Typeof(ref anon_const) => {
self.check_unused_delims_expr(
cx,
&anon_const.value,
UnusedDelimsCtx::AnonConst,
false,
None,
None,
false,
);
}
_ => {}
}
}
2020-03-27 20:54:52 +00:00
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
<Self as UnusedDelimLint>::check_item(self, cx, item)
}
}
declare_lint! {
2020-09-08 22:09:57 +00:00
/// The `unused_import_braces` lint catches unnecessary braces around an
/// imported item.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(unused_import_braces)]
/// use test::{A};
///
/// pub mod test {
/// pub struct A;
/// }
/// # fn main() {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// If there is only a single item, then remove the braces (`use test::A;`
/// for example).
///
/// This lint is "allow" by default because it is only enforcing a
/// stylistic choice.
UNUSED_IMPORT_BRACES,
Allow,
"unnecessary braces around an imported item"
}
declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
impl UnusedImportBraces {
2019-02-08 11:35:41 +00:00
fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
// Recursively check nested UseTrees
for (tree, _) in items {
self.check_use_tree(cx, tree, item);
}
// Trigger the lint only if there is one nested item
if items.len() != 1 {
return;
}
// Trigger the lint if the nested item is a non-self single item
let node_name = match items[0].0.kind {
ast::UseTreeKind::Simple(rename) => {
let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
2019-05-11 14:41:37 +00:00
if orig_ident.name == kw::SelfLower {
return;
}
rename.unwrap_or(orig_ident).name
}
ast::UseTreeKind::Glob => Symbol::intern("*"),
ast::UseTreeKind::Nested { .. } => return,
};
cx.emit_span_lint(
2022-09-16 07:01:02 +00:00
UNUSED_IMPORT_BRACES,
item.span,
2022-09-04 19:46:35 +00:00
UnusedImportBracesDiag { node: node_name },
2022-09-16 07:01:02 +00:00
);
}
}
}
impl EarlyLintPass for UnusedImportBraces {
2019-02-08 11:35:41 +00:00
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
2019-09-26 16:51:36 +00:00
if let ast::ItemKind::Use(ref use_tree) = item.kind {
self.check_use_tree(cx, use_tree, item);
}
}
}
declare_lint! {
2020-09-08 22:09:57 +00:00
/// The `unused_allocation` lint detects unnecessary allocations that can
/// be eliminated.
///
/// ### Example
///
/// ```rust
/// fn main() {
/// let a = Box::new([1, 2, 3]).len();
2020-09-08 22:09:57 +00:00
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// When a `box` expression is immediately coerced to a reference, then
/// the allocation is unnecessary, and a reference (using `&` or `&mut`)
/// should be used instead to avoid the allocation.
pub(super) UNUSED_ALLOCATION,
Warn,
"detects unnecessary allocations that can be eliminated"
}
declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
match e.kind {
hir::ExprKind::Call(path_expr, [_])
if let hir::ExprKind::Path(qpath) = &path_expr.kind
&& let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
&& cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
2016-10-09 04:08:07 +00:00
_ => return,
}
2020-07-17 08:47:04 +00:00
for adj in cx.typeck_results().expr_adjustments(e) {
if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
2022-09-04 19:46:35 +00:00
match m {
adjustment::AutoBorrowMutability::Not => {
cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
2022-09-04 19:46:35 +00:00
}
adjustment::AutoBorrowMutability::Mut { .. } => {
cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
2022-09-04 19:46:35 +00:00
}
};
}
}
}
}