2 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}
;
4 use rustc_ast
::util
::{classify, parser}
;
5 use rustc_ast
::{ExprKind, StmtKind}
;
6 use rustc_errors
::{fluent, pluralize, Applicability, MultiSpan}
;
8 use rustc_hir
::def
::{DefKind, Res}
;
9 use rustc_hir
::def_id
::DefId
;
10 use rustc_infer
::traits
::util
::elaborate_predicates_with_span
;
11 use rustc_middle
::ty
::adjustment
;
12 use rustc_middle
::ty
::{self, DefIdTree, Ty}
;
13 use rustc_span
::symbol
::Symbol
;
14 use rustc_span
::symbol
::{kw, sym}
;
15 use rustc_span
::{BytePos, Span}
;
19 /// The `unused_must_use` lint detects unused result of a type flagged as
25 /// fn returns_result() -> Result<(), ()> {
38 /// The `#[must_use]` attribute is an indicator that it is a mistake to
39 /// ignore the value. See [the reference] for more details.
41 /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
44 "unused result of a type flagged as `#[must_use]`",
45 report_in_external_macro
49 /// The `unused_results` lint checks for the unused result of an
50 /// expression in a statement.
54 /// ```rust,compile_fail
55 /// #![deny(unused_results)]
56 /// fn foo<T>() -> T { panic!() }
67 /// Ignoring the return value of a function may indicate a mistake. In
68 /// cases were it is almost certain that the result should be used, it is
69 /// recommended to annotate the function with the [`must_use` attribute].
70 /// Failure to use such a return value will trigger the [`unused_must_use`
71 /// lint] which is warn-by-default. The `unused_results` lint is
72 /// essentially the same, but triggers for *all* return values.
74 /// This lint is "allow" by default because it can be noisy, and may not be
75 /// an actual problem. For example, calling the `remove` method of a `Vec`
76 /// or `HashMap` returns the previous value, which you may not care about.
77 /// Using this lint would require explicitly ignoring or discarding such
80 /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
81 /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use
84 "unused result of an expression in a statement"
87 declare_lint_pass
!(UnusedResults
=> [UNUSED_MUST_USE
, UNUSED_RESULTS
]);
89 impl<'tcx
> LateLintPass
<'tcx
> for UnusedResults
{
90 fn check_stmt(&mut self, cx
: &LateContext
<'_
>, s
: &hir
::Stmt
<'_
>) {
91 let hir
::StmtKind
::Semi(expr
) = s
.kind
else { return; }
;
93 if let hir
::ExprKind
::Ret(..) = expr
.kind
{
97 if let hir
::ExprKind
::Match(await_expr
, _arms
, hir
::MatchSource
::AwaitDesugar
) = expr
.kind
98 && let ty
= cx
.typeck_results().expr_ty(&await_expr
)
99 && let ty
::Opaque(future_def_id
, _
) = ty
.kind()
100 && cx
.tcx
.ty_is_opaque_future(ty
)
101 // FIXME: This also includes non-async fns that return `impl Future`.
102 && let async_fn_def_id
= cx
.tcx
.parent(*future_def_id
)
103 && check_must_use_def(
107 "output of future returned by ",
111 // We have a bare `foo().await;` on an opaque type from an async function that was
112 // annotated with `#[must_use]`.
116 let ty
= cx
.typeck_results().expr_ty(&expr
);
118 let must_use_result
= is_ty_must_use(cx
, ty
, &expr
, expr
.span
);
119 let type_lint_emitted_or_suppressed
= match must_use_result
{
121 emit_must_use_untranslated(cx
, &path
, "", "", 1);
127 let fn_warned
= check_fn_must_use(cx
, expr
);
129 if !fn_warned
&& type_lint_emitted_or_suppressed
{
130 // We don't warn about unused unit or uninhabited types.
131 // (See https://github.com/rust-lang/rust/issues/43806 for details.)
135 let must_use_op
= match expr
.kind
{
136 // Hardcoding operators here seemed more expedient than the
137 // refactoring that would be needed to look up the `#[must_use]`
138 // attribute which does exist on the comparison trait methods
139 hir
::ExprKind
::Binary(bin_op
, ..) => match bin_op
.node
{
145 | hir
::BinOpKind
::Gt
=> Some("comparison"),
147 | hir
::BinOpKind
::Sub
148 | hir
::BinOpKind
::Div
149 | hir
::BinOpKind
::Mul
150 | hir
::BinOpKind
::Rem
=> Some("arithmetic operation"),
151 hir
::BinOpKind
::And
| hir
::BinOpKind
::Or
=> Some("logical operation"),
152 hir
::BinOpKind
::BitXor
153 | hir
::BinOpKind
::BitAnd
154 | hir
::BinOpKind
::BitOr
155 | hir
::BinOpKind
::Shl
156 | hir
::BinOpKind
::Shr
=> Some("bitwise operation"),
158 hir
::ExprKind
::AddrOf(..) => Some("borrow"),
159 hir
::ExprKind
::Unary(..) => Some("unary operation"),
163 let mut op_warned
= false;
165 if let Some(must_use_op
) = must_use_op
{
166 cx
.struct_span_lint(UNUSED_MUST_USE
, expr
.span
, fluent
::lint_unused_op
, |lint
| {
167 lint
.set_arg("op", must_use_op
)
168 .span_label(expr
.span
, fluent
::label
)
169 .span_suggestion_verbose(
170 expr
.span
.shrink_to_lo(),
173 Applicability
::MachineApplicable
,
179 if !(type_lint_emitted_or_suppressed
|| fn_warned
|| op_warned
) {
180 cx
.struct_span_lint(UNUSED_RESULTS
, s
.span
, fluent
::lint_unused_result
, |lint
| {
181 lint
.set_arg("ty", ty
)
185 fn check_fn_must_use(cx
: &LateContext
<'_
>, expr
: &hir
::Expr
<'_
>) -> bool
{
186 let maybe_def_id
= match expr
.kind
{
187 hir
::ExprKind
::Call(ref callee
, _
) => {
189 hir
::ExprKind
::Path(ref qpath
) => {
190 match cx
.qpath_res(qpath
, callee
.hir_id
) {
191 Res
::Def(DefKind
::Fn
| DefKind
::AssocFn
, def_id
) => Some(def_id
),
192 // `Res::Local` if it was a closure, for which we
193 // do not currently support must-use linting
200 hir
::ExprKind
::MethodCall(..) => {
201 cx
.typeck_results().type_dependent_def_id(expr
.hir_id
)
205 if let Some(def_id
) = maybe_def_id
{
206 check_must_use_def(cx
, def_id
, expr
.span
, "return value of ", "")
212 /// A path through a type to a must_use source. Contains useful info for the lint.
215 /// Suppress must_use checking.
217 /// The root of the normal must_use lint with an optional message.
218 Def(Span
, DefId
, Option
<Symbol
>),
221 TraitObject(Box
<Self>),
222 TupleElement(Vec
<(usize, Self)>),
223 Array(Box
<Self>, u64),
224 /// The root of the unused_closures lint.
226 /// The root of the unused_generators lint.
230 #[instrument(skip(cx, expr), level = "debug", ret)]
231 fn is_ty_must_use
<'tcx
>(
232 cx
: &LateContext
<'tcx
>,
234 expr
: &hir
::Expr
<'_
>,
236 ) -> Option
<MustUsePath
> {
238 || !ty
.is_inhabited_from(
240 cx
.tcx
.parent_module(expr
.hir_id
).to_def_id(),
244 return Some(MustUsePath
::Suppressed
);
248 ty
::Adt(..) if ty
.is_box() => {
249 let boxed_ty
= ty
.boxed_ty();
250 is_ty_must_use(cx
, boxed_ty
, expr
, span
)
251 .map(|inner
| MustUsePath
::Boxed(Box
::new(inner
)))
253 ty
::Adt(def
, _
) => is_def_must_use(cx
, def
.did(), span
),
254 ty
::Opaque(def
, _
) => {
255 elaborate_predicates_with_span(
257 cx
.tcx
.explicit_item_bounds(def
).iter().cloned(),
259 .filter_map(|obligation
| {
260 // We only look at the `DefId`, so it is safe to skip the binder here.
261 if let ty
::PredicateKind
::Clause(ty
::Clause
::Trait(
262 ref poly_trait_predicate
,
263 )) = obligation
.predicate
.kind().skip_binder()
265 let def_id
= poly_trait_predicate
.trait_ref
.def_id
;
267 is_def_must_use(cx
, def_id
, span
)
272 .map(|inner
| MustUsePath
::Opaque(Box
::new(inner
)))
275 ty
::Dynamic(binders
, _
, _
) => binders
277 .filter_map(|predicate
| {
278 if let ty
::ExistentialPredicate
::Trait(ref trait_ref
) =
279 predicate
.skip_binder()
281 let def_id
= trait_ref
.def_id
;
282 is_def_must_use(cx
, def_id
, span
)
286 .map(|inner
| MustUsePath
::TraitObject(Box
::new(inner
)))
290 let elem_exprs
= if let hir
::ExprKind
::Tup(elem_exprs
) = expr
.kind
{
291 debug_assert_eq
!(elem_exprs
.len(), tys
.len());
297 // Default to `expr`.
298 let elem_exprs
= elem_exprs
.iter().chain(iter
::repeat(expr
));
300 let nested_must_use
= tys
304 .filter_map(|(i
, (ty
, expr
))| {
305 is_ty_must_use(cx
, ty
, expr
, expr
.span
).map(|path
| (i
, path
))
307 .collect
::<Vec
<_
>>();
309 if !nested_must_use
.is_empty() {
310 Some(MustUsePath
::TupleElement(nested_must_use
))
315 ty
::Array(ty
, len
) => match len
.try_eval_usize(cx
.tcx
, cx
.param_env
) {
316 // If the array is empty we don't lint, to avoid false positives
317 Some(0) | None
=> None
,
318 // If the array is definitely non-empty, we can do `#[must_use]` checking.
319 Some(len
) => is_ty_must_use(cx
, ty
, expr
, span
)
320 .map(|inner
| MustUsePath
::Array(Box
::new(inner
), len
)),
322 ty
::Closure(..) => Some(MustUsePath
::Closure(span
)),
323 ty
::Generator(def_id
, ..) => {
324 // async fn should be treated as "implementor of `Future`"
325 let must_use
= if cx
.tcx
.generator_is_async(def_id
) {
326 let def_id
= cx
.tcx
.lang_items().future_trait().unwrap();
327 is_def_must_use(cx
, def_id
, span
)
328 .map(|inner
| MustUsePath
::Opaque(Box
::new(inner
)))
332 must_use
.or(Some(MustUsePath
::Generator(span
)))
338 fn is_def_must_use(cx
: &LateContext
<'_
>, def_id
: DefId
, span
: Span
) -> Option
<MustUsePath
> {
339 if let Some(attr
) = cx
.tcx
.get_attr(def_id
, sym
::must_use
) {
340 // check for #[must_use = "..."]
341 let reason
= attr
.value_str();
342 Some(MustUsePath
::Def(span
, def_id
, reason
))
348 // Returns whether further errors should be suppressed because either a lint has been emitted or the type should be ignored.
349 fn check_must_use_def(
350 cx
: &LateContext
<'_
>,
353 descr_pre_path
: &str,
354 descr_post_path
: &str,
356 is_def_must_use(cx
, def_id
, span
)
357 .map(|must_use_path
| {
358 emit_must_use_untranslated(
369 #[instrument(skip(cx), level = "debug")]
370 fn emit_must_use_untranslated(
371 cx
: &LateContext
<'_
>,
377 let plural_suffix
= pluralize
!(plural_len
);
380 MustUsePath
::Suppressed
=> {}
381 MustUsePath
::Boxed(path
) => {
382 let descr_pre
= &format
!("{}boxed ", descr_pre
);
383 emit_must_use_untranslated(cx
, path
, descr_pre
, descr_post
, plural_len
);
385 MustUsePath
::Opaque(path
) => {
386 let descr_pre
= &format
!("{}implementer{} of ", descr_pre
, plural_suffix
);
387 emit_must_use_untranslated(cx
, path
, descr_pre
, descr_post
, plural_len
);
389 MustUsePath
::TraitObject(path
) => {
390 let descr_post
= &format
!(" trait object{}{}", plural_suffix
, descr_post
);
391 emit_must_use_untranslated(cx
, path
, descr_pre
, descr_post
, plural_len
);
393 MustUsePath
::TupleElement(elems
) => {
394 for (index
, path
) in elems
{
395 let descr_post
= &format
!(" in tuple element {}", index
);
396 emit_must_use_untranslated(cx
, path
, descr_pre
, descr_post
, plural_len
);
399 MustUsePath
::Array(path
, len
) => {
400 let descr_pre
= &format
!("{}array{} of ", descr_pre
, plural_suffix
);
401 emit_must_use_untranslated(
406 plural_len
.saturating_add(usize::try_from(*len
).unwrap_or(usize::MAX
)),
409 MustUsePath
::Closure(span
) => {
413 fluent
::lint_unused_closure
,
415 // FIXME(davidtwco): this isn't properly translatable because of the
417 lint
.set_arg("count", plural_len
)
418 .set_arg("pre", descr_pre
)
419 .set_arg("post", descr_post
)
424 MustUsePath
::Generator(span
) => {
428 fluent
::lint_unused_generator
,
430 // FIXME(davidtwco): this isn't properly translatable because of the
432 lint
.set_arg("count", plural_len
)
433 .set_arg("pre", descr_pre
)
434 .set_arg("post", descr_post
)
439 MustUsePath
::Def(span
, def_id
, reason
) => {
440 cx
.struct_span_lint(UNUSED_MUST_USE
, *span
, fluent
::lint_unused_def
, |lint
| {
441 // FIXME(davidtwco): this isn't properly translatable because of the pre/post
443 lint
.set_arg("pre", descr_pre
);
444 lint
.set_arg("post", descr_post
);
445 lint
.set_arg("def", cx
.tcx
.def_path_str(*def_id
));
446 if let Some(note
) = reason
{
447 lint
.note(note
.as_str());
458 /// The `path_statements` lint detects path statements with no effect.
472 /// It is usually a mistake to have a statement that has no effect.
475 "path statements with no effect"
478 declare_lint_pass
!(PathStatements
=> [PATH_STATEMENTS
]);
480 impl<'tcx
> LateLintPass
<'tcx
> for PathStatements
{
481 fn check_stmt(&mut self, cx
: &LateContext
<'_
>, s
: &hir
::Stmt
<'_
>) {
482 if let hir
::StmtKind
::Semi(expr
) = s
.kind
{
483 if let hir
::ExprKind
::Path(_
) = expr
.kind
{
484 let ty
= cx
.typeck_results().expr_ty(expr
);
485 if ty
.needs_drop(cx
.tcx
, cx
.param_env
) {
489 fluent
::lint_path_statement_drop
,
491 if let Ok(snippet
) = cx
.sess().source_map().span_to_snippet(expr
.span
) {
492 lint
.span_suggestion(
495 format
!("drop({});", snippet
),
496 Applicability
::MachineApplicable
,
499 lint
.span_help(s
.span
, fluent
::suggestion
);
508 fluent
::lint_path_statement_no_effect
,
517 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
518 enum UnusedDelimsCtx
{
522 AssignedValueLetElse
,
535 impl From
<UnusedDelimsCtx
> for &'
static str {
536 fn from(ctx
: UnusedDelimsCtx
) -> &'
static str {
538 UnusedDelimsCtx
::FunctionArg
=> "function argument",
539 UnusedDelimsCtx
::MethodArg
=> "method argument",
540 UnusedDelimsCtx
::AssignedValue
| UnusedDelimsCtx
::AssignedValueLetElse
=> {
543 UnusedDelimsCtx
::IfCond
=> "`if` condition",
544 UnusedDelimsCtx
::WhileCond
=> "`while` condition",
545 UnusedDelimsCtx
::ForIterExpr
=> "`for` iterator expression",
546 UnusedDelimsCtx
::MatchScrutineeExpr
=> "`match` scrutinee expression",
547 UnusedDelimsCtx
::ReturnValue
=> "`return` value",
548 UnusedDelimsCtx
::BlockRetValue
=> "block return value",
549 UnusedDelimsCtx
::LetScrutineeExpr
=> "`let` scrutinee expression",
550 UnusedDelimsCtx
::ArrayLenExpr
| UnusedDelimsCtx
::AnonConst
=> "const expression",
551 UnusedDelimsCtx
::MatchArmExpr
=> "match arm expression",
556 /// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication.
557 trait UnusedDelimLint
{
558 const DELIM_STR
: &'
static str;
560 /// Due to `ref` pattern, there can be a difference between using
561 /// `{ expr }` and `expr` in pattern-matching contexts. This means
562 /// that we should only lint `unused_parens` and not `unused_braces`
567 /// let ref b = { a }; // We actually borrow a copy of `a` here.
568 /// a += 1; // By mutating `a` we invalidate any borrows of `a`.
569 /// assert_eq!(b + 1, a); // `b` does not borrow `a`, so we can still use it here.
571 const LINT_EXPR_IN_PATTERN_MATCHING_CTX
: bool
;
573 // this cannot be a constant is it refers to a static.
574 fn lint(&self) -> &'
static Lint
;
576 fn check_unused_delims_expr(
578 cx
: &EarlyContext
<'_
>,
580 ctx
: UnusedDelimsCtx
,
581 followed_by_block
: bool
,
582 left_pos
: Option
<BytePos
>,
583 right_pos
: Option
<BytePos
>,
586 fn is_expr_delims_necessary(
588 followed_by_block
: bool
,
589 followed_by_else
: bool
,
591 if followed_by_else
{
593 ast
::ExprKind
::Binary(op
, ..) if op
.node
.lazy() => return true,
594 _
if classify
::expr_trailing_brace(inner
).is_some() => return true,
599 // Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`
600 let lhs_needs_parens
= {
601 let mut innermost
= inner
;
603 innermost
= match &innermost
.kind
{
604 ExprKind
::Binary(_
, lhs
, _rhs
) => lhs
,
605 ExprKind
::Call(fn_
, _params
) => fn_
,
606 ExprKind
::Cast(expr
, _ty
) => expr
,
607 ExprKind
::Type(expr
, _ty
) => expr
,
608 ExprKind
::Index(base
, _subscript
) => base
,
611 if !classify
::expr_requires_semi_to_be_stmt(innermost
) {
618 || (followed_by_block
619 && match &inner
.kind
{
620 ExprKind
::Ret(_
) | ExprKind
::Break(..) | ExprKind
::Yield(..) => true,
621 ExprKind
::Range(_lhs
, Some(rhs
), _limits
) => {
622 matches
!(rhs
.kind
, ExprKind
::Block(..))
624 _
=> parser
::contains_exterior_struct_lit(&inner
),
628 fn emit_unused_delims_expr(
630 cx
: &EarlyContext
<'_
>,
632 ctx
: UnusedDelimsCtx
,
633 left_pos
: Option
<BytePos
>,
634 right_pos
: Option
<BytePos
>,
636 // If `value` has `ExprKind::Err`, unused delim lint can be broken.
637 // For example, the following code caused ICE.
638 // This is because the `ExprKind::Call` in `value` has `ExprKind::Err` as its argument
639 // and this leads to wrong spans. #104897
644 use rustc_ast
::visit
::{walk_expr, Visitor}
;
645 struct ErrExprVisitor
{
648 impl<'ast
> Visitor
<'ast
> for ErrExprVisitor
{
649 fn visit_expr(&mut self, expr
: &'ast ast
::Expr
) {
650 if let ExprKind
::Err
= expr
.kind
{
651 self.has_error
= true;
654 walk_expr(self, expr
)
657 let mut visitor
= ErrExprVisitor { has_error: false }
;
658 visitor
.visit_expr(value
);
659 if visitor
.has_error
{
662 let spans
= match value
.kind
{
663 ast
::ExprKind
::Block(ref block
, None
) if block
.stmts
.len() == 1 => {
664 if let Some(span
) = block
.stmts
[0].span
.find_ancestor_inside(value
.span
) {
665 Some((value
.span
.with_hi(span
.lo()), value
.span
.with_lo(span
.hi())))
670 ast
::ExprKind
::Paren(ref expr
) => {
671 let expr_span
= expr
.span
.find_ancestor_inside(value
.span
);
672 if let Some(expr_span
) = expr_span
{
673 Some((value
.span
.with_hi(expr_span
.lo()), value
.span
.with_lo(expr_span
.hi())))
681 left_pos
.map_or(false, |s
| s
>= value
.span
.lo()),
682 right_pos
.map_or(false, |s
| s
<= value
.span
.hi()),
684 self.emit_unused_delims(cx
, value
.span
, spans
, ctx
.into(), keep_space
);
687 fn emit_unused_delims(
689 cx
: &EarlyContext
<'_
>,
691 spans
: Option
<(Span
, Span
)>,
693 keep_space
: (bool
, bool
),
695 let primary_span
= if let Some((lo
, hi
)) = spans
{
696 MultiSpan
::from(vec
![lo
, hi
])
698 MultiSpan
::from(value_span
)
700 cx
.struct_span_lint(self.lint(), primary_span
, fluent
::lint_unused_delim
, |lint
| {
701 lint
.set_arg("delim", Self::DELIM_STR
);
702 lint
.set_arg("item", msg
);
703 if let Some((lo
, hi
)) = spans
{
704 let sm
= cx
.sess().source_map();
707 let Ok(snip
) = sm
.span_to_prev_source(lo
) && !snip
.ends_with(' '
) {
715 let Ok(snip
) = sm
.span_to_next_source(hi
) && !snip
.starts_with(' '
) {
721 let replacement
= vec
![(lo
, lo_replace
), (hi
, hi_replace
)];
722 lint
.multipart_suggestion(
725 Applicability
::MachineApplicable
,
732 fn check_expr(&mut self, cx
: &EarlyContext
<'_
>, e
: &ast
::Expr
) {
733 use rustc_ast
::ExprKind
::*;
734 let (value
, ctx
, followed_by_block
, left_pos
, right_pos
) = match e
.kind
{
735 // Do not lint `unused_braces` in `if let` expressions.
736 If(ref cond
, ref block
, _
)
737 if !matches
!(cond
.kind
, Let(_
, _
, _
))
738 || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX
=>
740 let left
= e
.span
.lo() + rustc_span
::BytePos(2);
741 let right
= block
.span
.lo();
742 (cond
, UnusedDelimsCtx
::IfCond
, true, Some(left
), Some(right
))
745 // Do not lint `unused_braces` in `while let` expressions.
746 While(ref cond
, ref block
, ..)
747 if !matches
!(cond
.kind
, Let(_
, _
, _
))
748 || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX
=>
750 let left
= e
.span
.lo() + rustc_span
::BytePos(5);
751 let right
= block
.span
.lo();
752 (cond
, UnusedDelimsCtx
::WhileCond
, true, Some(left
), Some(right
))
755 ForLoop(_
, ref cond
, ref block
, ..) => {
756 (cond
, UnusedDelimsCtx
::ForIterExpr
, true, None
, Some(block
.span
.lo()))
759 Match(ref head
, _
) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX
=> {
760 let left
= e
.span
.lo() + rustc_span
::BytePos(5);
761 (head
, UnusedDelimsCtx
::MatchScrutineeExpr
, true, Some(left
), None
)
764 Ret(Some(ref value
)) => {
765 let left
= e
.span
.lo() + rustc_span
::BytePos(3);
766 (value
, UnusedDelimsCtx
::ReturnValue
, false, Some(left
), None
)
769 Assign(_
, ref value
, _
) | AssignOp(.., ref value
) => {
770 (value
, UnusedDelimsCtx
::AssignedValue
, false, None
, None
)
772 // either function/method call, or something this lint doesn't care about
773 ref call_or_other
=> {
774 let (args_to_check
, ctx
) = match *call_or_other
{
775 Call(_
, ref args
) => (&args
[..], UnusedDelimsCtx
::FunctionArg
),
776 MethodCall(ref call
) => (&call
.args
[..], UnusedDelimsCtx
::MethodArg
),
777 // actual catch-all arm
782 // Don't lint if this is a nested macro expansion: otherwise, the lint could
783 // trigger in situations that macro authors shouldn't have to care about, e.g.,
784 // when a parenthesized token tree matched in one macro expansion is matched as
785 // an expression in another and used as a fn/method argument (Issue #47775)
786 if e
.span
.ctxt().outer_expn_data().call_site
.from_expansion() {
789 for arg
in args_to_check
{
790 self.check_unused_delims_expr(cx
, arg
, ctx
, false, None
, None
);
795 self.check_unused_delims_expr(cx
, &value
, ctx
, followed_by_block
, left_pos
, right_pos
);
798 fn check_stmt(&mut self, cx
: &EarlyContext
<'_
>, s
: &ast
::Stmt
) {
800 StmtKind
::Local(ref local
) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX
=> {
801 if let Some((init
, els
)) = local
.kind
.init_else_opt() {
802 let ctx
= match els
{
803 None
=> UnusedDelimsCtx
::AssignedValue
,
804 Some(_
) => UnusedDelimsCtx
::AssignedValueLetElse
,
806 self.check_unused_delims_expr(cx
, init
, ctx
, false, None
, None
);
809 StmtKind
::Expr(ref expr
) => {
810 self.check_unused_delims_expr(
813 UnusedDelimsCtx
::BlockRetValue
,
823 fn check_item(&mut self, cx
: &EarlyContext
<'_
>, item
: &ast
::Item
) {
824 use ast
::ItemKind
::*;
826 if let Const(.., Some(expr
)) | Static(.., Some(expr
)) = &item
.kind
{
827 self.check_unused_delims_expr(
830 UnusedDelimsCtx
::AssignedValue
,
840 /// The `unused_parens` lint detects `if`, `match`, `while` and `return`
841 /// with parentheses; they do not need them.
853 /// The parentheses are not needed, and should be removed. This is the
854 /// preferred style for writing these expressions.
855 pub(super) UNUSED_PARENS
,
857 "`if`, `match`, `while` and `return` do not need parentheses"
860 declare_lint_pass
!(UnusedParens
=> [UNUSED_PARENS
]);
862 impl UnusedDelimLint
for UnusedParens
{
863 const DELIM_STR
: &'
static str = "parentheses";
865 const LINT_EXPR_IN_PATTERN_MATCHING_CTX
: bool
= true;
867 fn lint(&self) -> &'
static Lint
{
871 fn check_unused_delims_expr(
873 cx
: &EarlyContext
<'_
>,
875 ctx
: UnusedDelimsCtx
,
876 followed_by_block
: bool
,
877 left_pos
: Option
<BytePos
>,
878 right_pos
: Option
<BytePos
>,
881 ast
::ExprKind
::Paren(ref inner
) => {
882 let followed_by_else
= ctx
== UnusedDelimsCtx
::AssignedValueLetElse
;
883 if !Self::is_expr_delims_necessary(inner
, followed_by_block
, followed_by_else
)
884 && value
.attrs
.is_empty()
885 && !value
.span
.from_expansion()
886 && (ctx
!= UnusedDelimsCtx
::LetScrutineeExpr
887 || !matches
!(inner
.kind
, ast
::ExprKind
::Binary(
888 rustc_span
::source_map
::Spanned { node, .. }
,
893 self.emit_unused_delims_expr(cx
, value
, ctx
, left_pos
, right_pos
)
896 ast
::ExprKind
::Let(_
, ref expr
, _
) => {
897 self.check_unused_delims_expr(
900 UnusedDelimsCtx
::LetScrutineeExpr
,
912 fn check_unused_parens_pat(
914 cx
: &EarlyContext
<'_
>,
918 keep_space
: (bool
, bool
),
920 use ast
::{BindingAnnotation, PatKind}
;
922 if let PatKind
::Paren(inner
) = &value
.kind
{
924 // The lint visitor will visit each subpattern of `p`. We do not want to lint
925 // any range pattern no matter where it occurs in the pattern. For something like
926 // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume
927 // that if there are unnecessary parens they serve a purpose of readability.
928 PatKind
::Range(..) => return,
929 // Avoid `p0 | .. | pn` if we should.
930 PatKind
::Or(..) if avoid_or
=> return,
931 // Avoid `mut x` and `mut x @ p` if we should:
932 PatKind
::Ident(BindingAnnotation
::MUT
, ..) if avoid_mut
=> {
935 // Otherwise proceed with linting.
938 let spans
= if let Some(inner
) = inner
.span
.find_ancestor_inside(value
.span
) {
939 Some((value
.span
.with_hi(inner
.lo()), value
.span
.with_lo(inner
.hi())))
943 self.emit_unused_delims(cx
, value
.span
, spans
, "pattern", keep_space
);
948 impl EarlyLintPass
for UnusedParens
{
949 fn check_expr(&mut self, cx
: &EarlyContext
<'_
>, e
: &ast
::Expr
) {
951 ExprKind
::Let(ref pat
, _
, _
) | ExprKind
::ForLoop(ref pat
, ..) => {
952 self.check_unused_parens_pat(cx
, pat
, false, false, (true, true));
954 // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
955 // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still
956 // want to complain about things like `if let 42 = (42)`.
957 ExprKind
::If(ref cond
, ref block
, ref else_
)
958 if matches
!(cond
.peel_parens().kind
, ExprKind
::Let(..)) =>
960 self.check_unused_delims_expr(
963 UnusedDelimsCtx
::LetScrutineeExpr
,
968 for stmt
in &block
.stmts
{
969 <Self as UnusedDelimLint
>::check_stmt(self, cx
, stmt
);
971 if let Some(e
) = else_
{
972 <Self as UnusedDelimLint
>::check_expr(self, cx
, e
);
976 ExprKind
::Match(ref _expr
, ref arm
) => {
978 self.check_unused_delims_expr(
981 UnusedDelimsCtx
::MatchArmExpr
,
991 <Self as UnusedDelimLint
>::check_expr(self, cx
, e
)
994 fn check_pat(&mut self, cx
: &EarlyContext
<'_
>, p
: &ast
::Pat
) {
995 use ast
::{Mutability, PatKind::*}
;
996 let keep_space
= (false, false);
998 // Do not lint on `(..)` as that will result in the other arms being useless.
1000 // The other cases do not contain sub-patterns.
1001 | Wild
| Rest
| Lit(..) | MacCall(..) | Range(..) | Ident(.., None
) | Path(..) => {}
,
1002 // These are list-like patterns; parens can always be removed.
1003 TupleStruct(_
, _
, ps
) | Tuple(ps
) | Slice(ps
) | Or(ps
) => for p
in ps
{
1004 self.check_unused_parens_pat(cx
, p
, false, false, keep_space
);
1006 Struct(_
, _
, fps
, _
) => for f
in fps
{
1007 self.check_unused_parens_pat(cx
, &f
.pat
, false, false, keep_space
);
1009 // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
1010 Ident(.., Some(p
)) | Box(p
) => self.check_unused_parens_pat(cx
, p
, true, false, keep_space
),
1011 // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
1012 // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
1013 Ref(p
, m
) => self.check_unused_parens_pat(cx
, p
, true, *m
== Mutability
::Not
, keep_space
),
1017 fn check_stmt(&mut self, cx
: &EarlyContext
<'_
>, s
: &ast
::Stmt
) {
1018 if let StmtKind
::Local(ref local
) = s
.kind
{
1019 self.check_unused_parens_pat(cx
, &local
.pat
, true, false, (false, false));
1022 <Self as UnusedDelimLint
>::check_stmt(self, cx
, s
)
1025 fn check_param(&mut self, cx
: &EarlyContext
<'_
>, param
: &ast
::Param
) {
1026 self.check_unused_parens_pat(cx
, ¶m
.pat
, true, false, (false, false));
1029 fn check_arm(&mut self, cx
: &EarlyContext
<'_
>, arm
: &ast
::Arm
) {
1030 self.check_unused_parens_pat(cx
, &arm
.pat
, false, false, (false, false));
1033 fn check_ty(&mut self, cx
: &EarlyContext
<'_
>, ty
: &ast
::Ty
) {
1034 if let ast
::TyKind
::Paren(r
) = &ty
.kind
{
1036 ast
::TyKind
::TraitObject(..) => {}
1037 ast
::TyKind
::BareFn(b
) if b
.generic_params
.len() > 0 => {}
1038 ast
::TyKind
::ImplTrait(_
, bounds
) if bounds
.len() > 1 => {}
1039 ast
::TyKind
::Array(_
, len
) => {
1040 self.check_unused_delims_expr(
1043 UnusedDelimsCtx
::ArrayLenExpr
,
1050 let spans
= if let Some(r
) = r
.span
.find_ancestor_inside(ty
.span
) {
1051 Some((ty
.span
.with_hi(r
.lo()), ty
.span
.with_lo(r
.hi())))
1055 self.emit_unused_delims(cx
, ty
.span
, spans
, "type", (false, false));
1061 fn check_item(&mut self, cx
: &EarlyContext
<'_
>, item
: &ast
::Item
) {
1062 <Self as UnusedDelimLint
>::check_item(self, cx
, item
)
1067 /// The `unused_braces` lint detects unnecessary braces around an
1082 /// The braces are not needed, and should be removed. This is the
1083 /// preferred style for writing these expressions.
1084 pub(super) UNUSED_BRACES
,
1086 "unnecessary braces around an expression"
1089 declare_lint_pass
!(UnusedBraces
=> [UNUSED_BRACES
]);
1091 impl UnusedDelimLint
for UnusedBraces
{
1092 const DELIM_STR
: &'
static str = "braces";
1094 const LINT_EXPR_IN_PATTERN_MATCHING_CTX
: bool
= false;
1096 fn lint(&self) -> &'
static Lint
{
1100 fn check_unused_delims_expr(
1102 cx
: &EarlyContext
<'_
>,
1104 ctx
: UnusedDelimsCtx
,
1105 followed_by_block
: bool
,
1106 left_pos
: Option
<BytePos
>,
1107 right_pos
: Option
<BytePos
>,
1110 ast
::ExprKind
::Block(ref inner
, None
)
1111 if inner
.rules
== ast
::BlockCheckMode
::Default
=>
1113 // emit a warning under the following conditions:
1115 // - the block does not have a label
1116 // - the block is not `unsafe`
1117 // - the block contains exactly one expression (do not lint `{ expr; }`)
1118 // - `followed_by_block` is true and the internal expr may contain a `{`
1119 // - the block is not multiline (do not lint multiline match arms)
1123 // somewhat_long_expression
1128 // - the block has no attribute and was not created inside a macro
1129 // - if the block is an `anon_const`, the inner expr must be a literal
1130 // (do not lint `struct A<const N: usize>; let _: A<{ 2 + 3 }>;`)
1132 // FIXME(const_generics): handle paths when #67075 is fixed.
1133 if let [stmt
] = inner
.stmts
.as_slice() {
1134 if let ast
::StmtKind
::Expr(ref expr
) = stmt
.kind
{
1135 if !Self::is_expr_delims_necessary(expr
, followed_by_block
, false)
1136 && (ctx
!= UnusedDelimsCtx
::AnonConst
1137 || matches
!(expr
.kind
, ast
::ExprKind
::Lit(_
)))
1138 && !cx
.sess().source_map().is_multiline(value
.span
)
1139 && value
.attrs
.is_empty()
1140 && !value
.span
.from_expansion()
1142 self.emit_unused_delims_expr(cx
, value
, ctx
, left_pos
, right_pos
)
1147 ast
::ExprKind
::Let(_
, ref expr
, _
) => {
1148 self.check_unused_delims_expr(
1151 UnusedDelimsCtx
::LetScrutineeExpr
,
1162 impl EarlyLintPass
for UnusedBraces
{
1163 fn check_stmt(&mut self, cx
: &EarlyContext
<'_
>, s
: &ast
::Stmt
) {
1164 <Self as UnusedDelimLint
>::check_stmt(self, cx
, s
)
1167 fn check_expr(&mut self, cx
: &EarlyContext
<'_
>, e
: &ast
::Expr
) {
1168 <Self as UnusedDelimLint
>::check_expr(self, cx
, e
);
1170 if let ExprKind
::Repeat(_
, ref anon_const
) = e
.kind
{
1171 self.check_unused_delims_expr(
1174 UnusedDelimsCtx
::AnonConst
,
1182 fn check_generic_arg(&mut self, cx
: &EarlyContext
<'_
>, arg
: &ast
::GenericArg
) {
1183 if let ast
::GenericArg
::Const(ct
) = arg
{
1184 self.check_unused_delims_expr(
1187 UnusedDelimsCtx
::AnonConst
,
1195 fn check_variant(&mut self, cx
: &EarlyContext
<'_
>, v
: &ast
::Variant
) {
1196 if let Some(anon_const
) = &v
.disr_expr
{
1197 self.check_unused_delims_expr(
1200 UnusedDelimsCtx
::AnonConst
,
1208 fn check_ty(&mut self, cx
: &EarlyContext
<'_
>, ty
: &ast
::Ty
) {
1210 ast
::TyKind
::Array(_
, ref len
) => {
1211 self.check_unused_delims_expr(
1214 UnusedDelimsCtx
::ArrayLenExpr
,
1221 ast
::TyKind
::Typeof(ref anon_const
) => {
1222 self.check_unused_delims_expr(
1225 UnusedDelimsCtx
::AnonConst
,
1236 fn check_item(&mut self, cx
: &EarlyContext
<'_
>, item
: &ast
::Item
) {
1237 <Self as UnusedDelimLint
>::check_item(self, cx
, item
)
1242 /// The `unused_import_braces` lint catches unnecessary braces around an
1247 /// ```rust,compile_fail
1248 /// #![deny(unused_import_braces)]
1261 /// If there is only a single item, then remove the braces (`use test::A;`
1264 /// This lint is "allow" by default because it is only enforcing a
1265 /// stylistic choice.
1266 UNUSED_IMPORT_BRACES
,
1268 "unnecessary braces around an imported item"
1271 declare_lint_pass
!(UnusedImportBraces
=> [UNUSED_IMPORT_BRACES
]);
1273 impl UnusedImportBraces
{
1274 fn check_use_tree(&self, cx
: &EarlyContext
<'_
>, use_tree
: &ast
::UseTree
, item
: &ast
::Item
) {
1275 if let ast
::UseTreeKind
::Nested(ref items
) = use_tree
.kind
{
1276 // Recursively check nested UseTrees
1277 for &(ref tree
, _
) in items
{
1278 self.check_use_tree(cx
, tree
, item
);
1281 // Trigger the lint only if there is one nested item
1282 if items
.len() != 1 {
1286 // Trigger the lint if the nested item is a non-self single item
1287 let node_name
= match items
[0].0.kind
{
1288 ast
::UseTreeKind
::Simple(rename
) => {
1289 let orig_ident
= items
[0].0.prefix
.segments
.last().unwrap().ident
;
1290 if orig_ident
.name
== kw
::SelfLower
{
1293 rename
.unwrap_or(orig_ident
).name
1295 ast
::UseTreeKind
::Glob
=> Symbol
::intern("*"),
1296 ast
::UseTreeKind
::Nested(_
) => return,
1299 cx
.struct_span_lint(
1300 UNUSED_IMPORT_BRACES
,
1302 fluent
::lint_unused_import_braces
,
1303 |lint
| lint
.set_arg("node", node_name
),
1309 impl EarlyLintPass
for UnusedImportBraces
{
1310 fn check_item(&mut self, cx
: &EarlyContext
<'_
>, item
: &ast
::Item
) {
1311 if let ast
::ItemKind
::Use(ref use_tree
) = item
.kind
{
1312 self.check_use_tree(cx
, use_tree
, item
);
1318 /// The `unused_allocation` lint detects unnecessary allocations that can
1324 /// #![feature(box_syntax)]
1326 /// let a = (box [1, 2, 3]).len();
1334 /// When a `box` expression is immediately coerced to a reference, then
1335 /// the allocation is unnecessary, and a reference (using `&` or `&mut`)
1336 /// should be used instead to avoid the allocation.
1337 pub(super) UNUSED_ALLOCATION
,
1339 "detects unnecessary allocations that can be eliminated"
1342 declare_lint_pass
!(UnusedAllocation
=> [UNUSED_ALLOCATION
]);
1344 impl<'tcx
> LateLintPass
<'tcx
> for UnusedAllocation
{
1345 fn check_expr(&mut self, cx
: &LateContext
<'_
>, e
: &hir
::Expr
<'_
>) {
1347 hir
::ExprKind
::Box(_
) => {}
1351 for adj
in cx
.typeck_results().expr_adjustments(e
) {
1352 if let adjustment
::Adjust
::Borrow(adjustment
::AutoBorrow
::Ref(_
, m
)) = adj
.kind
{
1353 cx
.struct_span_lint(
1357 adjustment
::AutoBorrowMutability
::Not
=> fluent
::lint_unused_allocation
,
1358 adjustment
::AutoBorrowMutability
::Mut { .. }
=> {
1359 fluent
::lint_unused_allocation_mut