rustc_lint/
unused.rs

1use std::iter;
2
3use rustc_ast::util::{classify, parser};
4use rustc_ast::{self as ast, ExprKind, HasAttrs as _, StmtKind};
5use rustc_attr_data_structures::{AttributeKind, find_attr};
6use rustc_errors::{MultiSpan, pluralize};
7use rustc_hir::def::{DefKind, Res};
8use rustc_hir::def_id::DefId;
9use rustc_hir::{self as hir, LangItem};
10use rustc_infer::traits::util::elaborate;
11use rustc_middle::ty::{self, Ty, adjustment};
12use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
13use rustc_span::{BytePos, Span, Symbol, kw, sym};
14use tracing::instrument;
15
16use crate::lints::{
17    PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
18    UnusedAllocationMutDiag, UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion,
19    UnusedDelim, UnusedDelimSuggestion, UnusedImportBracesDiag, UnusedOp, UnusedOpSuggestion,
20    UnusedResult,
21};
22use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Lint, LintContext};
23
24declare_lint! {
25    /// The `unused_must_use` lint detects unused result of a type flagged as
26    /// `#[must_use]`.
27    ///
28    /// ### Example
29    ///
30    /// ```rust
31    /// fn returns_result() -> Result<(), ()> {
32    ///     Ok(())
33    /// }
34    ///
35    /// fn main() {
36    ///     returns_result();
37    /// }
38    /// ```
39    ///
40    /// {{produces}}
41    ///
42    /// ### Explanation
43    ///
44    /// The `#[must_use]` attribute is an indicator that it is a mistake to
45    /// ignore the value. See [the reference] for more details.
46    ///
47    /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
48    pub UNUSED_MUST_USE,
49    Warn,
50    "unused result of a type flagged as `#[must_use]`",
51    report_in_external_macro
52}
53
54declare_lint! {
55    /// The `unused_results` lint checks for the unused result of an
56    /// expression in a statement.
57    ///
58    /// ### Example
59    ///
60    /// ```rust,compile_fail
61    /// #![deny(unused_results)]
62    /// fn foo<T>() -> T { panic!() }
63    ///
64    /// fn main() {
65    ///     foo::<usize>();
66    /// }
67    /// ```
68    ///
69    /// {{produces}}
70    ///
71    /// ### Explanation
72    ///
73    /// Ignoring the return value of a function may indicate a mistake. In
74    /// cases were it is almost certain that the result should be used, it is
75    /// recommended to annotate the function with the [`must_use` attribute].
76    /// Failure to use such a return value will trigger the [`unused_must_use`
77    /// lint] which is warn-by-default. The `unused_results` lint is
78    /// essentially the same, but triggers for *all* return values.
79    ///
80    /// This lint is "allow" by default because it can be noisy, and may not be
81    /// an actual problem. For example, calling the `remove` method of a `Vec`
82    /// or `HashMap` returns the previous value, which you may not care about.
83    /// Using this lint would require explicitly ignoring or discarding such
84    /// values.
85    ///
86    /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
87    /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use
88    pub UNUSED_RESULTS,
89    Allow,
90    "unused result of an expression in a statement"
91}
92
93declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]);
94
95impl<'tcx> LateLintPass<'tcx> for UnusedResults {
96    fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
97        let hir::StmtKind::Semi(mut expr) = s.kind else {
98            return;
99        };
100
101        let mut expr_is_from_block = false;
102        while let hir::ExprKind::Block(blk, ..) = expr.kind
103            && let hir::Block { expr: Some(e), .. } = blk
104        {
105            expr = e;
106            expr_is_from_block = true;
107        }
108
109        if let hir::ExprKind::Ret(..) = expr.kind {
110            return;
111        }
112
113        if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
114            && let ty = cx.typeck_results().expr_ty(await_expr)
115            && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind()
116            && cx.tcx.ty_is_opaque_future(ty)
117            && let async_fn_def_id = cx.tcx.parent(*future_def_id)
118            && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn)
119            // Check that this `impl Future` actually comes from an `async fn`
120            && cx.tcx.asyncness(async_fn_def_id).is_async()
121            && check_must_use_def(
122                cx,
123                async_fn_def_id,
124                expr.span,
125                "output of future returned by ",
126                "",
127                expr_is_from_block,
128            )
129        {
130            // We have a bare `foo().await;` on an opaque type from an async function that was
131            // annotated with `#[must_use]`.
132            return;
133        }
134
135        let ty = cx.typeck_results().expr_ty(expr);
136
137        let must_use_result = is_ty_must_use(cx, ty, expr, expr.span);
138        let type_lint_emitted_or_suppressed = match must_use_result {
139            Some(path) => {
140                emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);
141                true
142            }
143            None => false,
144        };
145
146        let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block);
147
148        if !fn_warned && type_lint_emitted_or_suppressed {
149            // We don't warn about unused unit or uninhabited types.
150            // (See https://github.com/rust-lang/rust/issues/43806 for details.)
151            return;
152        }
153
154        let must_use_op = match expr.kind {
155            // Hardcoding operators here seemed more expedient than the
156            // refactoring that would be needed to look up the `#[must_use]`
157            // attribute which does exist on the comparison trait methods
158            hir::ExprKind::Binary(bin_op, ..) => match bin_op.node {
159                hir::BinOpKind::Eq
160                | hir::BinOpKind::Lt
161                | hir::BinOpKind::Le
162                | hir::BinOpKind::Ne
163                | hir::BinOpKind::Ge
164                | hir::BinOpKind::Gt => Some("comparison"),
165                hir::BinOpKind::Add
166                | hir::BinOpKind::Sub
167                | hir::BinOpKind::Div
168                | hir::BinOpKind::Mul
169                | hir::BinOpKind::Rem => Some("arithmetic operation"),
170                hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),
171                hir::BinOpKind::BitXor
172                | hir::BinOpKind::BitAnd
173                | hir::BinOpKind::BitOr
174                | hir::BinOpKind::Shl
175                | hir::BinOpKind::Shr => Some("bitwise operation"),
176            },
177            hir::ExprKind::AddrOf(..) => Some("borrow"),
178            hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"),
179            hir::ExprKind::Unary(..) => Some("unary operation"),
180            _ => None,
181        };
182
183        let mut op_warned = false;
184
185        if let Some(must_use_op) = must_use_op {
186            cx.emit_span_lint(
187                UNUSED_MUST_USE,
188                expr.span,
189                UnusedOp {
190                    op: must_use_op,
191                    label: expr.span,
192                    suggestion: if expr_is_from_block {
193                        UnusedOpSuggestion::BlockTailExpr {
194                            before_span: expr.span.shrink_to_lo(),
195                            after_span: expr.span.shrink_to_hi(),
196                        }
197                    } else {
198                        UnusedOpSuggestion::NormalExpr { span: expr.span.shrink_to_lo() }
199                    },
200                },
201            );
202            op_warned = true;
203        }
204
205        if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) {
206            cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
207        }
208
209        fn check_fn_must_use(
210            cx: &LateContext<'_>,
211            expr: &hir::Expr<'_>,
212            expr_is_from_block: bool,
213        ) -> bool {
214            let maybe_def_id = match expr.kind {
215                hir::ExprKind::Call(callee, _) => {
216                    match callee.kind {
217                        hir::ExprKind::Path(ref qpath) => {
218                            match cx.qpath_res(qpath, callee.hir_id) {
219                                Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id),
220                                // `Res::Local` if it was a closure, for which we
221                                // do not currently support must-use linting
222                                _ => None,
223                            }
224                        }
225                        _ => None,
226                    }
227                }
228                hir::ExprKind::MethodCall(..) => {
229                    cx.typeck_results().type_dependent_def_id(expr.hir_id)
230                }
231                _ => None,
232            };
233            if let Some(def_id) = maybe_def_id {
234                check_must_use_def(
235                    cx,
236                    def_id,
237                    expr.span,
238                    "return value of ",
239                    "",
240                    expr_is_from_block,
241                )
242            } else {
243                false
244            }
245        }
246
247        /// A path through a type to a must_use source. Contains useful info for the lint.
248        #[derive(Debug)]
249        enum MustUsePath {
250            /// Suppress must_use checking.
251            Suppressed,
252            /// The root of the normal must_use lint with an optional message.
253            Def(Span, DefId, Option<Symbol>),
254            Boxed(Box<Self>),
255            Pinned(Box<Self>),
256            Opaque(Box<Self>),
257            TraitObject(Box<Self>),
258            TupleElement(Vec<(usize, Self)>),
259            Array(Box<Self>, u64),
260            /// The root of the unused_closures lint.
261            Closure(Span),
262            /// The root of the unused_coroutines lint.
263            Coroutine(Span),
264        }
265
266        #[instrument(skip(cx, expr), level = "debug", ret)]
267        fn is_ty_must_use<'tcx>(
268            cx: &LateContext<'tcx>,
269            ty: Ty<'tcx>,
270            expr: &hir::Expr<'_>,
271            span: Span,
272        ) -> Option<MustUsePath> {
273            if ty.is_unit()
274                || !ty.is_inhabited_from(
275                    cx.tcx,
276                    cx.tcx.parent_module(expr.hir_id).to_def_id(),
277                    cx.typing_env(),
278                )
279            {
280                return Some(MustUsePath::Suppressed);
281            }
282
283            match *ty.kind() {
284                ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
285                    is_ty_must_use(cx, boxed, expr, span)
286                        .map(|inner| MustUsePath::Boxed(Box::new(inner)))
287                }
288                ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
289                    let pinned_ty = args.type_at(0);
290                    is_ty_must_use(cx, pinned_ty, expr, span)
291                        .map(|inner| MustUsePath::Pinned(Box::new(inner)))
292                }
293                ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
294                ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => {
295                    elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied())
296                        // We only care about self bounds for the impl-trait
297                        .filter_only_self()
298                        .find_map(|(pred, _span)| {
299                            // We only look at the `DefId`, so it is safe to skip the binder here.
300                            if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
301                                pred.kind().skip_binder()
302                            {
303                                let def_id = poly_trait_predicate.trait_ref.def_id;
304
305                                is_def_must_use(cx, def_id, span)
306                            } else {
307                                None
308                            }
309                        })
310                        .map(|inner| MustUsePath::Opaque(Box::new(inner)))
311                }
312                ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
313                    if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
314                    {
315                        let def_id = trait_ref.def_id;
316                        is_def_must_use(cx, def_id, span)
317                            .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
318                    } else {
319                        None
320                    }
321                }),
322                ty::Tuple(tys) => {
323                    let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
324                        debug_assert_eq!(elem_exprs.len(), tys.len());
325                        elem_exprs
326                    } else {
327                        &[]
328                    };
329
330                    // Default to `expr`.
331                    let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));
332
333                    let nested_must_use = tys
334                        .iter()
335                        .zip(elem_exprs)
336                        .enumerate()
337                        .filter_map(|(i, (ty, expr))| {
338                            is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path))
339                        })
340                        .collect::<Vec<_>>();
341
342                    if !nested_must_use.is_empty() {
343                        Some(MustUsePath::TupleElement(nested_must_use))
344                    } else {
345                        None
346                    }
347                }
348                ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) {
349                    // If the array is empty we don't lint, to avoid false positives
350                    Some(0) | None => None,
351                    // If the array is definitely non-empty, we can do `#[must_use]` checking.
352                    Some(len) => is_ty_must_use(cx, ty, expr, span)
353                        .map(|inner| MustUsePath::Array(Box::new(inner), len)),
354                },
355                ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)),
356                ty::Coroutine(def_id, ..) => {
357                    // async fn should be treated as "implementor of `Future`"
358                    let must_use = if cx.tcx.coroutine_is_async(def_id) {
359                        let def_id = cx.tcx.lang_items().future_trait()?;
360                        is_def_must_use(cx, def_id, span)
361                            .map(|inner| MustUsePath::Opaque(Box::new(inner)))
362                    } else {
363                        None
364                    };
365                    must_use.or(Some(MustUsePath::Coroutine(span)))
366                }
367                _ => None,
368            }
369        }
370
371        fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
372            if let Some(reason) = find_attr!(
373                cx.tcx.get_all_attrs(def_id),
374                AttributeKind::MustUse { reason, .. } => reason
375            ) {
376                // check for #[must_use = "..."]
377                Some(MustUsePath::Def(span, def_id, *reason))
378            } else {
379                None
380            }
381        }
382
383        // Returns whether further errors should be suppressed because either a lint has been
384        // emitted or the type should be ignored.
385        fn check_must_use_def(
386            cx: &LateContext<'_>,
387            def_id: DefId,
388            span: Span,
389            descr_pre_path: &str,
390            descr_post_path: &str,
391            expr_is_from_block: bool,
392        ) -> bool {
393            is_def_must_use(cx, def_id, span)
394                .map(|must_use_path| {
395                    emit_must_use_untranslated(
396                        cx,
397                        &must_use_path,
398                        descr_pre_path,
399                        descr_post_path,
400                        1,
401                        false,
402                        expr_is_from_block,
403                    )
404                })
405                .is_some()
406        }
407
408        #[instrument(skip(cx), level = "debug")]
409        fn emit_must_use_untranslated(
410            cx: &LateContext<'_>,
411            path: &MustUsePath,
412            descr_pre: &str,
413            descr_post: &str,
414            plural_len: usize,
415            is_inner: bool,
416            expr_is_from_block: bool,
417        ) {
418            let plural_suffix = pluralize!(plural_len);
419
420            match path {
421                MustUsePath::Suppressed => {}
422                MustUsePath::Boxed(path) => {
423                    let descr_pre = &format!("{descr_pre}boxed ");
424                    emit_must_use_untranslated(
425                        cx,
426                        path,
427                        descr_pre,
428                        descr_post,
429                        plural_len,
430                        true,
431                        expr_is_from_block,
432                    );
433                }
434                MustUsePath::Pinned(path) => {
435                    let descr_pre = &format!("{descr_pre}pinned ");
436                    emit_must_use_untranslated(
437                        cx,
438                        path,
439                        descr_pre,
440                        descr_post,
441                        plural_len,
442                        true,
443                        expr_is_from_block,
444                    );
445                }
446                MustUsePath::Opaque(path) => {
447                    let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
448                    emit_must_use_untranslated(
449                        cx,
450                        path,
451                        descr_pre,
452                        descr_post,
453                        plural_len,
454                        true,
455                        expr_is_from_block,
456                    );
457                }
458                MustUsePath::TraitObject(path) => {
459                    let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
460                    emit_must_use_untranslated(
461                        cx,
462                        path,
463                        descr_pre,
464                        descr_post,
465                        plural_len,
466                        true,
467                        expr_is_from_block,
468                    );
469                }
470                MustUsePath::TupleElement(elems) => {
471                    for (index, path) in elems {
472                        let descr_post = &format!(" in tuple element {index}");
473                        emit_must_use_untranslated(
474                            cx,
475                            path,
476                            descr_pre,
477                            descr_post,
478                            plural_len,
479                            true,
480                            expr_is_from_block,
481                        );
482                    }
483                }
484                MustUsePath::Array(path, len) => {
485                    let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
486                    emit_must_use_untranslated(
487                        cx,
488                        path,
489                        descr_pre,
490                        descr_post,
491                        plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
492                        true,
493                        expr_is_from_block,
494                    );
495                }
496                MustUsePath::Closure(span) => {
497                    cx.emit_span_lint(
498                        UNUSED_MUST_USE,
499                        *span,
500                        UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
501                    );
502                }
503                MustUsePath::Coroutine(span) => {
504                    cx.emit_span_lint(
505                        UNUSED_MUST_USE,
506                        *span,
507                        UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
508                    );
509                }
510                MustUsePath::Def(span, def_id, reason) => {
511                    cx.emit_span_lint(
512                        UNUSED_MUST_USE,
513                        *span,
514                        UnusedDef {
515                            pre: descr_pre,
516                            post: descr_post,
517                            cx,
518                            def_id: *def_id,
519                            note: *reason,
520                            suggestion: (!is_inner).then_some(if expr_is_from_block {
521                                UnusedDefSuggestion::BlockTailExpr {
522                                    before_span: span.shrink_to_lo(),
523                                    after_span: span.shrink_to_hi(),
524                                }
525                            } else {
526                                UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
527                            }),
528                        },
529                    );
530                }
531            }
532        }
533    }
534}
535
536declare_lint! {
537    /// The `path_statements` lint detects path statements with no effect.
538    ///
539    /// ### Example
540    ///
541    /// ```rust
542    /// let x = 42;
543    ///
544    /// x;
545    /// ```
546    ///
547    /// {{produces}}
548    ///
549    /// ### Explanation
550    ///
551    /// It is usually a mistake to have a statement that has no effect.
552    pub PATH_STATEMENTS,
553    Warn,
554    "path statements with no effect"
555}
556
557declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
558
559impl<'tcx> LateLintPass<'tcx> for PathStatements {
560    fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
561        if let hir::StmtKind::Semi(expr) = s.kind {
562            if let hir::ExprKind::Path(_) = expr.kind {
563                let ty = cx.typeck_results().expr_ty(expr);
564                if ty.needs_drop(cx.tcx, cx.typing_env()) {
565                    let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
566                    {
567                        PathStatementDropSub::Suggestion { span: s.span, snippet }
568                    } else {
569                        PathStatementDropSub::Help { span: s.span }
570                    };
571                    cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
572                } else {
573                    cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
574                }
575            }
576        }
577    }
578}
579
580#[derive(Copy, Clone, Debug, PartialEq, Eq)]
581enum UnusedDelimsCtx {
582    FunctionArg,
583    MethodArg,
584    AssignedValue,
585    AssignedValueLetElse,
586    IfCond,
587    WhileCond,
588    ForIterExpr,
589    MatchScrutineeExpr,
590    ReturnValue,
591    BlockRetValue,
592    BreakValue,
593    LetScrutineeExpr,
594    ArrayLenExpr,
595    AnonConst,
596    MatchArmExpr,
597    IndexExpr,
598}
599
600impl From<UnusedDelimsCtx> for &'static str {
601    fn from(ctx: UnusedDelimsCtx) -> &'static str {
602        match ctx {
603            UnusedDelimsCtx::FunctionArg => "function argument",
604            UnusedDelimsCtx::MethodArg => "method argument",
605            UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
606                "assigned value"
607            }
608            UnusedDelimsCtx::IfCond => "`if` condition",
609            UnusedDelimsCtx::WhileCond => "`while` condition",
610            UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
611            UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression",
612            UnusedDelimsCtx::ReturnValue => "`return` value",
613            UnusedDelimsCtx::BlockRetValue => "block return value",
614            UnusedDelimsCtx::BreakValue => "`break` value",
615            UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
616            UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
617            UnusedDelimsCtx::MatchArmExpr => "match arm expression",
618            UnusedDelimsCtx::IndexExpr => "index expression",
619        }
620    }
621}
622
623/// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication.
624trait UnusedDelimLint {
625    const DELIM_STR: &'static str;
626
627    /// Due to `ref` pattern, there can be a difference between using
628    /// `{ expr }` and `expr` in pattern-matching contexts. This means
629    /// that we should only lint `unused_parens` and not `unused_braces`
630    /// in this case.
631    ///
632    /// ```rust
633    /// let mut a = 7;
634    /// let ref b = { a }; // We actually borrow a copy of `a` here.
635    /// a += 1; // By mutating `a` we invalidate any borrows of `a`.
636    /// assert_eq!(b + 1, a); // `b` does not borrow `a`, so we can still use it here.
637    /// ```
638    const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
639
640    // this cannot be a constant is it refers to a static.
641    fn lint(&self) -> &'static Lint;
642
643    fn check_unused_delims_expr(
644        &self,
645        cx: &EarlyContext<'_>,
646        value: &ast::Expr,
647        ctx: UnusedDelimsCtx,
648        followed_by_block: bool,
649        left_pos: Option<BytePos>,
650        right_pos: Option<BytePos>,
651        is_kw: bool,
652    );
653
654    fn is_expr_delims_necessary(
655        inner: &ast::Expr,
656        ctx: UnusedDelimsCtx,
657        followed_by_block: bool,
658    ) -> bool {
659        let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
660
661        if followed_by_else {
662            match inner.kind {
663                ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
664                _ if classify::expr_trailing_brace(inner).is_some() => return true,
665                _ => {}
666            }
667        }
668
669        // Check it's range in LetScrutineeExpr
670        if let ast::ExprKind::Range(..) = inner.kind
671            && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
672        {
673            return true;
674        }
675
676        // Do not lint against parentheses around `&raw [const|mut] expr`.
677        // These parentheses will have to be added e.g. when calling a method on the result of this
678        // expression, and we want to avoid churn wrt adding and removing parentheses.
679        if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) {
680            return true;
681        }
682
683        // Check if LHS needs parens to prevent false-positives in cases like
684        // `fn x() -> u8 { ({ 0 } + 1) }`.
685        //
686        // FIXME: https://github.com/rust-lang/rust/issues/119426
687        // The syntax tree in this code is from after macro expansion, so the
688        // current implementation has both false negatives and false positives
689        // related to expressions containing macros.
690        //
691        //     macro_rules! m1 {
692        //         () => {
693        //             1
694        //         };
695        //     }
696        //
697        //     fn f1() -> u8 {
698        //         // Lint says parens are not needed, but they are.
699        //         (m1! {} + 1)
700        //     }
701        //
702        //     macro_rules! m2 {
703        //         () => {
704        //             loop { break 1; }
705        //         };
706        //     }
707        //
708        //     fn f2() -> u8 {
709        //         // Lint says parens are needed, but they are not.
710        //         (m2!() + 1)
711        //     }
712        {
713            let mut innermost = inner;
714            loop {
715                innermost = match &innermost.kind {
716                    ExprKind::Binary(_op, lhs, _rhs) => lhs,
717                    ExprKind::Call(fn_, _params) => fn_,
718                    ExprKind::Cast(expr, _ty) => expr,
719                    ExprKind::Type(expr, _ty) => expr,
720                    ExprKind::Index(base, _subscript, _) => base,
721                    _ => break,
722                };
723                if !classify::expr_requires_semi_to_be_stmt(innermost) {
724                    return true;
725                }
726            }
727        }
728
729        // Check if RHS needs parens to prevent false-positives in cases like `if (() == return)
730        // {}`.
731        if !followed_by_block {
732            return false;
733        }
734
735        // Check if we need parens for `match &( Struct { field:  }) {}`.
736        {
737            let mut innermost = inner;
738            loop {
739                innermost = match &innermost.kind {
740                    ExprKind::AddrOf(_, _, expr) => expr,
741                    _ => {
742                        if parser::contains_exterior_struct_lit(innermost) {
743                            return true;
744                        } else {
745                            break;
746                        }
747                    }
748                }
749            }
750        }
751
752        let mut innermost = inner;
753        loop {
754            innermost = match &innermost.kind {
755                ExprKind::Unary(_op, expr) => expr,
756                ExprKind::Binary(_op, _lhs, rhs) => rhs,
757                ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
758                ExprKind::Assign(_lhs, rhs, _span) => rhs,
759
760                ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
761
762                ExprKind::Break(_label, None) => return false,
763                ExprKind::Break(_label, Some(break_expr)) => {
764                    return matches!(break_expr.kind, ExprKind::Block(..));
765                }
766
767                ExprKind::Range(_lhs, Some(rhs), _limits) => {
768                    return matches!(rhs.kind, ExprKind::Block(..));
769                }
770
771                _ => return parser::contains_exterior_struct_lit(inner),
772            }
773        }
774    }
775
776    fn emit_unused_delims_expr(
777        &self,
778        cx: &EarlyContext<'_>,
779        value: &ast::Expr,
780        ctx: UnusedDelimsCtx,
781        left_pos: Option<BytePos>,
782        right_pos: Option<BytePos>,
783        is_kw: bool,
784    ) {
785        let span_with_attrs = match value.kind {
786            ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => {
787                // For the statements with attributes, like `{ #[allow()] println!("Hello!") }`,
788                // the span should contains the attributes, or the suggestion will remove them.
789                if let Some(attr_lo) = stmt.attrs().iter().map(|attr| attr.span.lo()).min() {
790                    stmt.span.with_lo(attr_lo)
791                } else {
792                    stmt.span
793                }
794            }
795            ast::ExprKind::Paren(ref expr) => {
796                // For the expr with attributes, like `let _ = (#[inline] || println!("Hello!"));`,
797                // the span should contains the attributes, or the suggestion will remove them.
798                if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() {
799                    expr.span.with_lo(attr_lo)
800                } else {
801                    expr.span
802                }
803            }
804            _ => return,
805        };
806        let spans = span_with_attrs
807            .find_ancestor_inside(value.span)
808            .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi())));
809        let keep_space = (
810            left_pos.is_some_and(|s| s >= value.span.lo()),
811            right_pos.is_some_and(|s| s <= value.span.hi()),
812        );
813        self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
814    }
815
816    fn emit_unused_delims(
817        &self,
818        cx: &EarlyContext<'_>,
819        value_span: Span,
820        spans: Option<(Span, Span)>,
821        msg: &str,
822        keep_space: (bool, bool),
823        is_kw: bool,
824    ) {
825        let primary_span = if let Some((lo, hi)) = spans {
826            if hi.is_empty() {
827                // do not point at delims that do not exist
828                return;
829            }
830            MultiSpan::from(vec![lo, hi])
831        } else {
832            MultiSpan::from(value_span)
833        };
834        let suggestion = spans.map(|(lo, hi)| {
835            let sm = cx.sess().source_map();
836            let lo_replace = if (keep_space.0 || is_kw)
837                && let Ok(snip) = sm.span_to_prev_source(lo)
838                && !snip.ends_with(' ')
839            {
840                " "
841            } else {
842                ""
843            };
844
845            let hi_replace = if keep_space.1
846                && let Ok(snip) = sm.span_to_next_source(hi)
847                && !snip.starts_with(' ')
848            {
849                " "
850            } else {
851                ""
852            };
853            UnusedDelimSuggestion {
854                start_span: lo,
855                start_replace: lo_replace,
856                end_span: hi,
857                end_replace: hi_replace,
858            }
859        });
860        cx.emit_span_lint(
861            self.lint(),
862            primary_span,
863            UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
864        );
865    }
866
867    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
868        use rustc_ast::ExprKind::*;
869        let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
870            // Do not lint `unused_braces` in `if let` expressions.
871            If(ref cond, ref block, _)
872                if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
873            {
874                let left = e.span.lo() + rustc_span::BytePos(2);
875                let right = block.span.lo();
876                (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
877            }
878
879            // Do not lint `unused_braces` in `while let` expressions.
880            While(ref cond, ref block, ..)
881                if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
882            {
883                let left = e.span.lo() + rustc_span::BytePos(5);
884                let right = block.span.lo();
885                (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
886            }
887
888            ForLoop { ref iter, ref body, .. } => {
889                (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
890            }
891
892            Match(ref head, _, ast::MatchKind::Prefix)
893                if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
894            {
895                let left = e.span.lo() + rustc_span::BytePos(5);
896                (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
897            }
898
899            Ret(Some(ref value)) => {
900                let left = e.span.lo() + rustc_span::BytePos(3);
901                (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
902            }
903
904            Break(_, Some(ref value)) => {
905                (value, UnusedDelimsCtx::BreakValue, false, None, None, true)
906            }
907
908            Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
909
910            Assign(_, ref value, _) | AssignOp(.., ref value) => {
911                (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
912            }
913            // either function/method call, or something this lint doesn't care about
914            ref call_or_other => {
915                let (args_to_check, ctx) = match *call_or_other {
916                    Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
917                    MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
918                    // actual catch-all arm
919                    _ => {
920                        return;
921                    }
922                };
923                // Don't lint if this is a nested macro expansion: otherwise, the lint could
924                // trigger in situations that macro authors shouldn't have to care about, e.g.,
925                // when a parenthesized token tree matched in one macro expansion is matched as
926                // an expression in another and used as a fn/method argument (Issue #47775)
927                if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
928                    return;
929                }
930                for arg in args_to_check {
931                    self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
932                }
933                return;
934            }
935        };
936        self.check_unused_delims_expr(
937            cx,
938            value,
939            ctx,
940            followed_by_block,
941            left_pos,
942            right_pos,
943            is_kw,
944        );
945    }
946
947    fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
948        match s.kind {
949            StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
950                if let Some((init, els)) = local.kind.init_else_opt() {
951                    if els.is_some()
952                        && let ExprKind::Paren(paren) = &init.kind
953                        && !init.span.eq_ctxt(paren.span)
954                    {
955                        // This branch prevents cases where parentheses wrap an expression
956                        // resulting from macro expansion, such as:
957                        // ```
958                        // macro_rules! x {
959                        // () => { None::<i32> };
960                        // }
961                        // let Some(_) = (x!{}) else { return };
962                        // // -> let Some(_) = (None::<i32>) else { return };
963                        // //                  ~           ~ No Lint
964                        // ```
965                        return;
966                    }
967                    let ctx = match els {
968                        None => UnusedDelimsCtx::AssignedValue,
969                        Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
970                    };
971                    self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
972                }
973            }
974            StmtKind::Expr(ref expr) => {
975                self.check_unused_delims_expr(
976                    cx,
977                    expr,
978                    UnusedDelimsCtx::BlockRetValue,
979                    false,
980                    None,
981                    None,
982                    false,
983                );
984            }
985            _ => {}
986        }
987    }
988
989    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
990        use ast::ItemKind::*;
991
992        if let Const(box ast::ConstItem { expr: Some(expr), .. })
993        | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
994        {
995            self.check_unused_delims_expr(
996                cx,
997                expr,
998                UnusedDelimsCtx::AssignedValue,
999                false,
1000                None,
1001                None,
1002                false,
1003            );
1004        }
1005    }
1006}
1007
1008declare_lint! {
1009    /// The `unused_parens` lint detects `if`, `match`, `while` and `return`
1010    /// with parentheses; they do not need them.
1011    ///
1012    /// ### Examples
1013    ///
1014    /// ```rust
1015    /// if(true) {}
1016    /// ```
1017    ///
1018    /// {{produces}}
1019    ///
1020    /// ### Explanation
1021    ///
1022    /// The parentheses are not needed, and should be removed. This is the
1023    /// preferred style for writing these expressions.
1024    pub(super) UNUSED_PARENS,
1025    Warn,
1026    "`if`, `match`, `while` and `return` do not need parentheses"
1027}
1028
1029#[derive(Default)]
1030pub(crate) struct UnusedParens {
1031    with_self_ty_parens: bool,
1032    /// `1 as (i32) < 2` parses to ExprKind::Lt
1033    /// `1 as i32 < 2` parses to i32::<2[missing angle bracket]
1034    parens_in_cast_in_lt: Vec<ast::NodeId>,
1035}
1036
1037impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
1038
1039impl UnusedDelimLint for UnusedParens {
1040    const DELIM_STR: &'static str = "parentheses";
1041
1042    const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
1043
1044    fn lint(&self) -> &'static Lint {
1045        UNUSED_PARENS
1046    }
1047
1048    fn check_unused_delims_expr(
1049        &self,
1050        cx: &EarlyContext<'_>,
1051        value: &ast::Expr,
1052        ctx: UnusedDelimsCtx,
1053        followed_by_block: bool,
1054        left_pos: Option<BytePos>,
1055        right_pos: Option<BytePos>,
1056        is_kw: bool,
1057    ) {
1058        match value.kind {
1059            ast::ExprKind::Paren(ref inner) => {
1060                if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
1061                    && value.attrs.is_empty()
1062                    && !value.span.from_expansion()
1063                    && (ctx != UnusedDelimsCtx::LetScrutineeExpr
1064                        || !matches!(inner.kind, ast::ExprKind::Binary(
1065                                rustc_span::source_map::Spanned { node, .. },
1066                                _,
1067                                _,
1068                            ) if node.is_lazy()))
1069                    && !((ctx == UnusedDelimsCtx::ReturnValue
1070                        || ctx == UnusedDelimsCtx::BreakValue)
1071                        && matches!(inner.kind, ast::ExprKind::Assign(_, _, _)))
1072                {
1073                    self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1074                }
1075            }
1076            ast::ExprKind::Let(_, ref expr, _, _) => {
1077                self.check_unused_delims_expr(
1078                    cx,
1079                    expr,
1080                    UnusedDelimsCtx::LetScrutineeExpr,
1081                    followed_by_block,
1082                    None,
1083                    None,
1084                    false,
1085                );
1086            }
1087            _ => {}
1088        }
1089    }
1090}
1091
1092impl UnusedParens {
1093    fn check_unused_parens_pat(
1094        &self,
1095        cx: &EarlyContext<'_>,
1096        value: &ast::Pat,
1097        avoid_or: bool,
1098        avoid_mut: bool,
1099        keep_space: (bool, bool),
1100    ) {
1101        use ast::{BindingMode, PatKind};
1102
1103        if let PatKind::Paren(inner) = &value.kind {
1104            match inner.kind {
1105                // The lint visitor will visit each subpattern of `p`. We do not want to lint
1106                // any range pattern no matter where it occurs in the pattern. For something like
1107                // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume
1108                // that if there are unnecessary parens they serve a purpose of readability.
1109                PatKind::Range(..) => return,
1110                // Avoid `p0 | .. | pn` if we should.
1111                PatKind::Or(..) if avoid_or => return,
1112                // Avoid `mut x` and `mut x @ p` if we should:
1113                PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1114                    return;
1115                }
1116                // Otherwise proceed with linting.
1117                _ => {}
1118            }
1119            let spans = if !value.span.from_expansion() {
1120                inner
1121                    .span
1122                    .find_ancestor_inside(value.span)
1123                    .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1124            } else {
1125                None
1126            };
1127            self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1128        }
1129    }
1130
1131    fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1132        if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1133            && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1134        {
1135            let mut cur = lhs;
1136            while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1137                cur = rhs;
1138            }
1139
1140            if let ExprKind::Cast(_, ty) = &cur.kind
1141                && let ast::TyKind::Paren(_) = &ty.kind
1142            {
1143                return Some(ty.id);
1144            }
1145        }
1146        None
1147    }
1148}
1149
1150impl EarlyLintPass for UnusedParens {
1151    #[inline]
1152    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1153        if let Some(ty_id) = self.cast_followed_by_lt(e) {
1154            self.parens_in_cast_in_lt.push(ty_id);
1155        }
1156
1157        match e.kind {
1158            ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1159                self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1160            }
1161            // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
1162            // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still
1163            // want to complain about things like `if let 42 = (42)`.
1164            ExprKind::If(ref cond, ref block, ref else_)
1165                if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1166            {
1167                self.check_unused_delims_expr(
1168                    cx,
1169                    cond.peel_parens(),
1170                    UnusedDelimsCtx::LetScrutineeExpr,
1171                    true,
1172                    None,
1173                    None,
1174                    true,
1175                );
1176                for stmt in &block.stmts {
1177                    <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1178                }
1179                if let Some(e) = else_ {
1180                    <Self as UnusedDelimLint>::check_expr(self, cx, e);
1181                }
1182                return;
1183            }
1184            ExprKind::Match(ref _expr, ref arm, _) => {
1185                for a in arm {
1186                    if let Some(body) = &a.body {
1187                        self.check_unused_delims_expr(
1188                            cx,
1189                            body,
1190                            UnusedDelimsCtx::MatchArmExpr,
1191                            false,
1192                            None,
1193                            None,
1194                            true,
1195                        );
1196                    }
1197                }
1198            }
1199            _ => {}
1200        }
1201
1202        <Self as UnusedDelimLint>::check_expr(self, cx, e)
1203    }
1204
1205    fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1206        if let Some(ty_id) = self.cast_followed_by_lt(e) {
1207            let id = self
1208                .parens_in_cast_in_lt
1209                .pop()
1210                .expect("check_expr and check_expr_post must balance");
1211            assert_eq!(
1212                id, ty_id,
1213                "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1214            );
1215        }
1216    }
1217
1218    fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1219        use ast::Mutability;
1220        use ast::PatKind::*;
1221        let keep_space = (false, false);
1222        match &p.kind {
1223            // Do not lint on `(..)` as that will result in the other arms being useless.
1224            Paren(_)
1225            // The other cases do not contain sub-patterns.
1226            | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1227            | Path(..) | Err(_) => {},
1228            // These are list-like patterns; parens can always be removed.
1229            TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1230                self.check_unused_parens_pat(cx, p, false, false, keep_space);
1231            },
1232            Struct(_, _, fps, _) => for f in fps {
1233                self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1234            },
1235            // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
1236            Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1237            // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
1238            // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
1239            Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1240        }
1241    }
1242
1243    fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1244        if let StmtKind::Let(ref local) = s.kind {
1245            self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1246        }
1247
1248        <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1249    }
1250
1251    fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1252        self.check_unused_parens_pat(cx, &param.pat, true, false, (false, false));
1253    }
1254
1255    fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1256        self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1257    }
1258
1259    fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1260        if let ast::TyKind::Paren(_) = ty.kind
1261            && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1262        {
1263            return;
1264        }
1265        match &ty.kind {
1266            ast::TyKind::Array(_, len) => {
1267                self.check_unused_delims_expr(
1268                    cx,
1269                    &len.value,
1270                    UnusedDelimsCtx::ArrayLenExpr,
1271                    false,
1272                    None,
1273                    None,
1274                    false,
1275                );
1276            }
1277            ast::TyKind::Paren(r) => {
1278                match &r.kind {
1279                    ast::TyKind::TraitObject(..) => {}
1280                    ast::TyKind::BareFn(b)
1281                        if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
1282                    ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
1283                    _ => {
1284                        let spans = if !ty.span.from_expansion() {
1285                            r.span
1286                                .find_ancestor_inside(ty.span)
1287                                .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1288                        } else {
1289                            None
1290                        };
1291                        self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1292                    }
1293                }
1294                self.with_self_ty_parens = false;
1295            }
1296            _ => {}
1297        }
1298    }
1299
1300    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1301        <Self as UnusedDelimLint>::check_item(self, cx, item)
1302    }
1303
1304    fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1305        use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1306        if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1307            bounded_ty,
1308            bound_generic_params,
1309            ..
1310        }) = &pred.kind
1311            && let ast::TyKind::Paren(_) = &bounded_ty.kind
1312            && bound_generic_params.is_empty()
1313        {
1314            self.with_self_ty_parens = true;
1315        }
1316    }
1317
1318    fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1319        assert!(!self.with_self_ty_parens);
1320    }
1321}
1322
1323declare_lint! {
1324    /// The `unused_braces` lint detects unnecessary braces around an
1325    /// expression.
1326    ///
1327    /// ### Example
1328    ///
1329    /// ```rust
1330    /// if { true } {
1331    ///     // ...
1332    /// }
1333    /// ```
1334    ///
1335    /// {{produces}}
1336    ///
1337    /// ### Explanation
1338    ///
1339    /// The braces are not needed, and should be removed. This is the
1340    /// preferred style for writing these expressions.
1341    pub(super) UNUSED_BRACES,
1342    Warn,
1343    "unnecessary braces around an expression"
1344}
1345
1346declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1347
1348impl UnusedDelimLint for UnusedBraces {
1349    const DELIM_STR: &'static str = "braces";
1350
1351    const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1352
1353    fn lint(&self) -> &'static Lint {
1354        UNUSED_BRACES
1355    }
1356
1357    fn check_unused_delims_expr(
1358        &self,
1359        cx: &EarlyContext<'_>,
1360        value: &ast::Expr,
1361        ctx: UnusedDelimsCtx,
1362        followed_by_block: bool,
1363        left_pos: Option<BytePos>,
1364        right_pos: Option<BytePos>,
1365        is_kw: bool,
1366    ) {
1367        match value.kind {
1368            ast::ExprKind::Block(ref inner, None)
1369                if inner.rules == ast::BlockCheckMode::Default =>
1370            {
1371                // emit a warning under the following conditions:
1372                //
1373                // - the block does not have a label
1374                // - the block is not `unsafe`
1375                // - the block contains exactly one expression (do not lint `{ expr; }`)
1376                // - `followed_by_block` is true and the internal expr may contain a `{`
1377                // - the block is not multiline (do not lint multiline match arms)
1378                //      ```
1379                //      match expr {
1380                //          Pattern => {
1381                //              somewhat_long_expression
1382                //          }
1383                //          // ...
1384                //      }
1385                //      ```
1386                // - the block has no attribute and was not created inside a macro
1387                // - if the block is an `anon_const`, the inner expr must be a literal
1388                //   not created by a macro, i.e. do not lint on:
1389                //      ```
1390                //      struct A<const N: usize>;
1391                //      let _: A<{ 2 + 3 }>;
1392                //      let _: A<{produces_literal!()}>;
1393                //      ```
1394                // FIXME(const_generics): handle paths when #67075 is fixed.
1395                if let [stmt] = inner.stmts.as_slice() {
1396                    if let ast::StmtKind::Expr(ref expr) = stmt.kind {
1397                        if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1398                            && (ctx != UnusedDelimsCtx::AnonConst
1399                                || (matches!(expr.kind, ast::ExprKind::Lit(_))
1400                                    && !expr.span.from_expansion()))
1401                            && !cx.sess().source_map().is_multiline(value.span)
1402                            && value.attrs.is_empty()
1403                            && !value.span.from_expansion()
1404                            && !inner.span.from_expansion()
1405                        {
1406                            self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1407                        }
1408                    }
1409                }
1410            }
1411            ast::ExprKind::Let(_, ref expr, _, _) => {
1412                self.check_unused_delims_expr(
1413                    cx,
1414                    expr,
1415                    UnusedDelimsCtx::LetScrutineeExpr,
1416                    followed_by_block,
1417                    None,
1418                    None,
1419                    false,
1420                );
1421            }
1422            _ => {}
1423        }
1424    }
1425}
1426
1427impl EarlyLintPass for UnusedBraces {
1428    fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1429        <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1430    }
1431
1432    #[inline]
1433    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1434        <Self as UnusedDelimLint>::check_expr(self, cx, e);
1435
1436        if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1437            self.check_unused_delims_expr(
1438                cx,
1439                &anon_const.value,
1440                UnusedDelimsCtx::AnonConst,
1441                false,
1442                None,
1443                None,
1444                false,
1445            );
1446        }
1447    }
1448
1449    fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1450        if let ast::GenericArg::Const(ct) = arg {
1451            self.check_unused_delims_expr(
1452                cx,
1453                &ct.value,
1454                UnusedDelimsCtx::AnonConst,
1455                false,
1456                None,
1457                None,
1458                false,
1459            );
1460        }
1461    }
1462
1463    fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1464        if let Some(anon_const) = &v.disr_expr {
1465            self.check_unused_delims_expr(
1466                cx,
1467                &anon_const.value,
1468                UnusedDelimsCtx::AnonConst,
1469                false,
1470                None,
1471                None,
1472                false,
1473            );
1474        }
1475    }
1476
1477    fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1478        match ty.kind {
1479            ast::TyKind::Array(_, ref len) => {
1480                self.check_unused_delims_expr(
1481                    cx,
1482                    &len.value,
1483                    UnusedDelimsCtx::ArrayLenExpr,
1484                    false,
1485                    None,
1486                    None,
1487                    false,
1488                );
1489            }
1490
1491            ast::TyKind::Typeof(ref anon_const) => {
1492                self.check_unused_delims_expr(
1493                    cx,
1494                    &anon_const.value,
1495                    UnusedDelimsCtx::AnonConst,
1496                    false,
1497                    None,
1498                    None,
1499                    false,
1500                );
1501            }
1502
1503            _ => {}
1504        }
1505    }
1506
1507    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1508        <Self as UnusedDelimLint>::check_item(self, cx, item)
1509    }
1510}
1511
1512declare_lint! {
1513    /// The `unused_import_braces` lint catches unnecessary braces around an
1514    /// imported item.
1515    ///
1516    /// ### Example
1517    ///
1518    /// ```rust,compile_fail
1519    /// #![deny(unused_import_braces)]
1520    /// use test::{A};
1521    ///
1522    /// pub mod test {
1523    ///     pub struct A;
1524    /// }
1525    /// # fn main() {}
1526    /// ```
1527    ///
1528    /// {{produces}}
1529    ///
1530    /// ### Explanation
1531    ///
1532    /// If there is only a single item, then remove the braces (`use test::A;`
1533    /// for example).
1534    ///
1535    /// This lint is "allow" by default because it is only enforcing a
1536    /// stylistic choice.
1537    UNUSED_IMPORT_BRACES,
1538    Allow,
1539    "unnecessary braces around an imported item"
1540}
1541
1542declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1543
1544impl UnusedImportBraces {
1545    fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1546        if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1547            // Recursively check nested UseTrees
1548            for (tree, _) in items {
1549                self.check_use_tree(cx, tree, item);
1550            }
1551
1552            // Trigger the lint only if there is one nested item
1553            let [(tree, _)] = items.as_slice() else { return };
1554
1555            // Trigger the lint if the nested item is a non-self single item
1556            let node_name = match tree.kind {
1557                ast::UseTreeKind::Simple(rename) => {
1558                    let orig_ident = tree.prefix.segments.last().unwrap().ident;
1559                    if orig_ident.name == kw::SelfLower {
1560                        return;
1561                    }
1562                    rename.unwrap_or(orig_ident).name
1563                }
1564                ast::UseTreeKind::Glob => sym::asterisk,
1565                ast::UseTreeKind::Nested { .. } => return,
1566            };
1567
1568            cx.emit_span_lint(
1569                UNUSED_IMPORT_BRACES,
1570                item.span,
1571                UnusedImportBracesDiag { node: node_name },
1572            );
1573        }
1574    }
1575}
1576
1577impl EarlyLintPass for UnusedImportBraces {
1578    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1579        if let ast::ItemKind::Use(ref use_tree) = item.kind {
1580            self.check_use_tree(cx, use_tree, item);
1581        }
1582    }
1583}
1584
1585declare_lint! {
1586    /// The `unused_allocation` lint detects unnecessary allocations that can
1587    /// be eliminated.
1588    ///
1589    /// ### Example
1590    ///
1591    /// ```rust
1592    /// fn main() {
1593    ///     let a = Box::new([1, 2, 3]).len();
1594    /// }
1595    /// ```
1596    ///
1597    /// {{produces}}
1598    ///
1599    /// ### Explanation
1600    ///
1601    /// When a `box` expression is immediately coerced to a reference, then
1602    /// the allocation is unnecessary, and a reference (using `&` or `&mut`)
1603    /// should be used instead to avoid the allocation.
1604    pub(super) UNUSED_ALLOCATION,
1605    Warn,
1606    "detects unnecessary allocations that can be eliminated"
1607}
1608
1609declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1610
1611impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1612    fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1613        match e.kind {
1614            hir::ExprKind::Call(path_expr, [_])
1615                if let hir::ExprKind::Path(qpath) = &path_expr.kind
1616                    && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1617                    && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1618            _ => return,
1619        }
1620
1621        for adj in cx.typeck_results().expr_adjustments(e) {
1622            if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1623                match m {
1624                    adjustment::AutoBorrowMutability::Not => {
1625                        cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1626                    }
1627                    adjustment::AutoBorrowMutability::Mut { .. } => {
1628                        cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1629                    }
1630                };
1631            }
1632        }
1633    }
1634}