rustc_lint/
unused.rs

1use std::iter;
2
3use rustc_ast as ast;
4use rustc_ast::util::{classify, parser};
5use rustc_ast::{ExprKind, StmtKind};
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(attr) = cx.tcx.get_attr(def_id, sym::must_use) {
373                // check for #[must_use = "..."]
374                let reason = attr.value_str();
375                Some(MustUsePath::Def(span, def_id, reason))
376            } else {
377                None
378            }
379        }
380
381        // Returns whether further errors should be suppressed because either a lint has been
382        // emitted or the type should be ignored.
383        fn check_must_use_def(
384            cx: &LateContext<'_>,
385            def_id: DefId,
386            span: Span,
387            descr_pre_path: &str,
388            descr_post_path: &str,
389            expr_is_from_block: bool,
390        ) -> bool {
391            is_def_must_use(cx, def_id, span)
392                .map(|must_use_path| {
393                    emit_must_use_untranslated(
394                        cx,
395                        &must_use_path,
396                        descr_pre_path,
397                        descr_post_path,
398                        1,
399                        false,
400                        expr_is_from_block,
401                    )
402                })
403                .is_some()
404        }
405
406        #[instrument(skip(cx), level = "debug")]
407        fn emit_must_use_untranslated(
408            cx: &LateContext<'_>,
409            path: &MustUsePath,
410            descr_pre: &str,
411            descr_post: &str,
412            plural_len: usize,
413            is_inner: bool,
414            expr_is_from_block: bool,
415        ) {
416            let plural_suffix = pluralize!(plural_len);
417
418            match path {
419                MustUsePath::Suppressed => {}
420                MustUsePath::Boxed(path) => {
421                    let descr_pre = &format!("{descr_pre}boxed ");
422                    emit_must_use_untranslated(
423                        cx,
424                        path,
425                        descr_pre,
426                        descr_post,
427                        plural_len,
428                        true,
429                        expr_is_from_block,
430                    );
431                }
432                MustUsePath::Pinned(path) => {
433                    let descr_pre = &format!("{descr_pre}pinned ");
434                    emit_must_use_untranslated(
435                        cx,
436                        path,
437                        descr_pre,
438                        descr_post,
439                        plural_len,
440                        true,
441                        expr_is_from_block,
442                    );
443                }
444                MustUsePath::Opaque(path) => {
445                    let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
446                    emit_must_use_untranslated(
447                        cx,
448                        path,
449                        descr_pre,
450                        descr_post,
451                        plural_len,
452                        true,
453                        expr_is_from_block,
454                    );
455                }
456                MustUsePath::TraitObject(path) => {
457                    let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
458                    emit_must_use_untranslated(
459                        cx,
460                        path,
461                        descr_pre,
462                        descr_post,
463                        plural_len,
464                        true,
465                        expr_is_from_block,
466                    );
467                }
468                MustUsePath::TupleElement(elems) => {
469                    for (index, path) in elems {
470                        let descr_post = &format!(" in tuple element {index}");
471                        emit_must_use_untranslated(
472                            cx,
473                            path,
474                            descr_pre,
475                            descr_post,
476                            plural_len,
477                            true,
478                            expr_is_from_block,
479                        );
480                    }
481                }
482                MustUsePath::Array(path, len) => {
483                    let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
484                    emit_must_use_untranslated(
485                        cx,
486                        path,
487                        descr_pre,
488                        descr_post,
489                        plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
490                        true,
491                        expr_is_from_block,
492                    );
493                }
494                MustUsePath::Closure(span) => {
495                    cx.emit_span_lint(
496                        UNUSED_MUST_USE,
497                        *span,
498                        UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
499                    );
500                }
501                MustUsePath::Coroutine(span) => {
502                    cx.emit_span_lint(
503                        UNUSED_MUST_USE,
504                        *span,
505                        UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
506                    );
507                }
508                MustUsePath::Def(span, def_id, reason) => {
509                    cx.emit_span_lint(
510                        UNUSED_MUST_USE,
511                        *span,
512                        UnusedDef {
513                            pre: descr_pre,
514                            post: descr_post,
515                            cx,
516                            def_id: *def_id,
517                            note: *reason,
518                            suggestion: (!is_inner).then_some(if expr_is_from_block {
519                                UnusedDefSuggestion::BlockTailExpr {
520                                    before_span: span.shrink_to_lo(),
521                                    after_span: span.shrink_to_hi(),
522                                }
523                            } else {
524                                UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
525                            }),
526                        },
527                    );
528                }
529            }
530        }
531    }
532}
533
534declare_lint! {
535    /// The `path_statements` lint detects path statements with no effect.
536    ///
537    /// ### Example
538    ///
539    /// ```rust
540    /// let x = 42;
541    ///
542    /// x;
543    /// ```
544    ///
545    /// {{produces}}
546    ///
547    /// ### Explanation
548    ///
549    /// It is usually a mistake to have a statement that has no effect.
550    pub PATH_STATEMENTS,
551    Warn,
552    "path statements with no effect"
553}
554
555declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
556
557impl<'tcx> LateLintPass<'tcx> for PathStatements {
558    fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
559        if let hir::StmtKind::Semi(expr) = s.kind {
560            if let hir::ExprKind::Path(_) = expr.kind {
561                let ty = cx.typeck_results().expr_ty(expr);
562                if ty.needs_drop(cx.tcx, cx.typing_env()) {
563                    let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
564                    {
565                        PathStatementDropSub::Suggestion { span: s.span, snippet }
566                    } else {
567                        PathStatementDropSub::Help { span: s.span }
568                    };
569                    cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
570                } else {
571                    cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
572                }
573            }
574        }
575    }
576}
577
578#[derive(Copy, Clone, Debug, PartialEq, Eq)]
579enum UnusedDelimsCtx {
580    FunctionArg,
581    MethodArg,
582    AssignedValue,
583    AssignedValueLetElse,
584    IfCond,
585    WhileCond,
586    ForIterExpr,
587    MatchScrutineeExpr,
588    ReturnValue,
589    BlockRetValue,
590    BreakValue,
591    LetScrutineeExpr,
592    ArrayLenExpr,
593    AnonConst,
594    MatchArmExpr,
595    IndexExpr,
596}
597
598impl From<UnusedDelimsCtx> for &'static str {
599    fn from(ctx: UnusedDelimsCtx) -> &'static str {
600        match ctx {
601            UnusedDelimsCtx::FunctionArg => "function argument",
602            UnusedDelimsCtx::MethodArg => "method argument",
603            UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
604                "assigned value"
605            }
606            UnusedDelimsCtx::IfCond => "`if` condition",
607            UnusedDelimsCtx::WhileCond => "`while` condition",
608            UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
609            UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression",
610            UnusedDelimsCtx::ReturnValue => "`return` value",
611            UnusedDelimsCtx::BlockRetValue => "block return value",
612            UnusedDelimsCtx::BreakValue => "`break` value",
613            UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
614            UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
615            UnusedDelimsCtx::MatchArmExpr => "match arm expression",
616            UnusedDelimsCtx::IndexExpr => "index expression",
617        }
618    }
619}
620
621/// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication.
622trait UnusedDelimLint {
623    const DELIM_STR: &'static str;
624
625    /// Due to `ref` pattern, there can be a difference between using
626    /// `{ expr }` and `expr` in pattern-matching contexts. This means
627    /// that we should only lint `unused_parens` and not `unused_braces`
628    /// in this case.
629    ///
630    /// ```rust
631    /// let mut a = 7;
632    /// let ref b = { a }; // We actually borrow a copy of `a` here.
633    /// a += 1; // By mutating `a` we invalidate any borrows of `a`.
634    /// assert_eq!(b + 1, a); // `b` does not borrow `a`, so we can still use it here.
635    /// ```
636    const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
637
638    // this cannot be a constant is it refers to a static.
639    fn lint(&self) -> &'static Lint;
640
641    fn check_unused_delims_expr(
642        &self,
643        cx: &EarlyContext<'_>,
644        value: &ast::Expr,
645        ctx: UnusedDelimsCtx,
646        followed_by_block: bool,
647        left_pos: Option<BytePos>,
648        right_pos: Option<BytePos>,
649        is_kw: bool,
650    );
651
652    fn is_expr_delims_necessary(
653        inner: &ast::Expr,
654        ctx: UnusedDelimsCtx,
655        followed_by_block: bool,
656    ) -> bool {
657        let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
658
659        if followed_by_else {
660            match inner.kind {
661                ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
662                _ if classify::expr_trailing_brace(inner).is_some() => return true,
663                _ => {}
664            }
665        }
666
667        // Check it's range in LetScrutineeExpr
668        if let ast::ExprKind::Range(..) = inner.kind
669            && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
670        {
671            return true;
672        }
673
674        // Do not lint against parentheses around `&raw [const|mut] expr`.
675        // These parentheses will have to be added e.g. when calling a method on the result of this
676        // expression, and we want to avoid churn wrt adding and removing parentheses.
677        if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) {
678            return true;
679        }
680
681        // Check if LHS needs parens to prevent false-positives in cases like
682        // `fn x() -> u8 { ({ 0 } + 1) }`.
683        //
684        // FIXME: https://github.com/rust-lang/rust/issues/119426
685        // The syntax tree in this code is from after macro expansion, so the
686        // current implementation has both false negatives and false positives
687        // related to expressions containing macros.
688        //
689        //     macro_rules! m1 {
690        //         () => {
691        //             1
692        //         };
693        //     }
694        //
695        //     fn f1() -> u8 {
696        //         // Lint says parens are not needed, but they are.
697        //         (m1! {} + 1)
698        //     }
699        //
700        //     macro_rules! m2 {
701        //         () => {
702        //             loop { break 1; }
703        //         };
704        //     }
705        //
706        //     fn f2() -> u8 {
707        //         // Lint says parens are needed, but they are not.
708        //         (m2!() + 1)
709        //     }
710        {
711            let mut innermost = inner;
712            loop {
713                innermost = match &innermost.kind {
714                    ExprKind::Binary(_op, lhs, _rhs) => lhs,
715                    ExprKind::Call(fn_, _params) => fn_,
716                    ExprKind::Cast(expr, _ty) => expr,
717                    ExprKind::Type(expr, _ty) => expr,
718                    ExprKind::Index(base, _subscript, _) => base,
719                    _ => break,
720                };
721                if !classify::expr_requires_semi_to_be_stmt(innermost) {
722                    return true;
723                }
724            }
725        }
726
727        // Check if RHS needs parens to prevent false-positives in cases like `if (() == return)
728        // {}`.
729        if !followed_by_block {
730            return false;
731        }
732
733        // Check if we need parens for `match &( Struct { field:  }) {}`.
734        {
735            let mut innermost = inner;
736            loop {
737                innermost = match &innermost.kind {
738                    ExprKind::AddrOf(_, _, expr) => expr,
739                    _ => {
740                        if parser::contains_exterior_struct_lit(innermost) {
741                            return true;
742                        } else {
743                            break;
744                        }
745                    }
746                }
747            }
748        }
749
750        let mut innermost = inner;
751        loop {
752            innermost = match &innermost.kind {
753                ExprKind::Unary(_op, expr) => expr,
754                ExprKind::Binary(_op, _lhs, rhs) => rhs,
755                ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
756                ExprKind::Assign(_lhs, rhs, _span) => rhs,
757
758                ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
759
760                ExprKind::Break(_label, None) => return false,
761                ExprKind::Break(_label, Some(break_expr)) => {
762                    return matches!(break_expr.kind, ExprKind::Block(..));
763                }
764
765                ExprKind::Range(_lhs, Some(rhs), _limits) => {
766                    return matches!(rhs.kind, ExprKind::Block(..));
767                }
768
769                _ => return parser::contains_exterior_struct_lit(inner),
770            }
771        }
772    }
773
774    fn emit_unused_delims_expr(
775        &self,
776        cx: &EarlyContext<'_>,
777        value: &ast::Expr,
778        ctx: UnusedDelimsCtx,
779        left_pos: Option<BytePos>,
780        right_pos: Option<BytePos>,
781        is_kw: bool,
782    ) {
783        let spans = match value.kind {
784            ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => stmt
785                .span
786                .find_ancestor_inside(value.span)
787                .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))),
788            ast::ExprKind::Paren(ref expr) => {
789                // For the expr with attributes, like `let _ = (#[inline] || println!("Hello!"));`,
790                // the span should contains the attributes, or the suggestion will remove them.
791                let expr_span_with_attrs =
792                    if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() {
793                        expr.span.with_lo(attr_lo)
794                    } else {
795                        expr.span
796                    };
797                expr_span_with_attrs.find_ancestor_inside(value.span).map(|expr_span| {
798                    (value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi()))
799                })
800            }
801            _ => return,
802        };
803        let keep_space = (
804            left_pos.is_some_and(|s| s >= value.span.lo()),
805            right_pos.is_some_and(|s| s <= value.span.hi()),
806        );
807        self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
808    }
809
810    fn emit_unused_delims(
811        &self,
812        cx: &EarlyContext<'_>,
813        value_span: Span,
814        spans: Option<(Span, Span)>,
815        msg: &str,
816        keep_space: (bool, bool),
817        is_kw: bool,
818    ) {
819        let primary_span = if let Some((lo, hi)) = spans {
820            if hi.is_empty() {
821                // do not point at delims that do not exist
822                return;
823            }
824            MultiSpan::from(vec![lo, hi])
825        } else {
826            MultiSpan::from(value_span)
827        };
828        let suggestion = spans.map(|(lo, hi)| {
829            let sm = cx.sess().source_map();
830            let lo_replace = if (keep_space.0 || is_kw)
831                && let Ok(snip) = sm.span_to_prev_source(lo)
832                && !snip.ends_with(' ')
833            {
834                " "
835            } else {
836                ""
837            };
838
839            let hi_replace = if keep_space.1
840                && let Ok(snip) = sm.span_to_next_source(hi)
841                && !snip.starts_with(' ')
842            {
843                " "
844            } else {
845                ""
846            };
847            UnusedDelimSuggestion {
848                start_span: lo,
849                start_replace: lo_replace,
850                end_span: hi,
851                end_replace: hi_replace,
852            }
853        });
854        cx.emit_span_lint(
855            self.lint(),
856            primary_span,
857            UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
858        );
859    }
860
861    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
862        use rustc_ast::ExprKind::*;
863        let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
864            // Do not lint `unused_braces` in `if let` expressions.
865            If(ref cond, ref block, _)
866                if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
867            {
868                let left = e.span.lo() + rustc_span::BytePos(2);
869                let right = block.span.lo();
870                (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
871            }
872
873            // Do not lint `unused_braces` in `while let` expressions.
874            While(ref cond, ref block, ..)
875                if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
876            {
877                let left = e.span.lo() + rustc_span::BytePos(5);
878                let right = block.span.lo();
879                (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
880            }
881
882            ForLoop { ref iter, ref body, .. } => {
883                (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
884            }
885
886            Match(ref head, _, ast::MatchKind::Prefix)
887                if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
888            {
889                let left = e.span.lo() + rustc_span::BytePos(5);
890                (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
891            }
892
893            Ret(Some(ref value)) => {
894                let left = e.span.lo() + rustc_span::BytePos(3);
895                (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
896            }
897
898            Break(_, Some(ref value)) => {
899                (value, UnusedDelimsCtx::BreakValue, false, None, None, true)
900            }
901
902            Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
903
904            Assign(_, ref value, _) | AssignOp(.., ref value) => {
905                (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
906            }
907            // either function/method call, or something this lint doesn't care about
908            ref call_or_other => {
909                let (args_to_check, ctx) = match *call_or_other {
910                    Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
911                    MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
912                    // actual catch-all arm
913                    _ => {
914                        return;
915                    }
916                };
917                // Don't lint if this is a nested macro expansion: otherwise, the lint could
918                // trigger in situations that macro authors shouldn't have to care about, e.g.,
919                // when a parenthesized token tree matched in one macro expansion is matched as
920                // an expression in another and used as a fn/method argument (Issue #47775)
921                if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
922                    return;
923                }
924                for arg in args_to_check {
925                    self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
926                }
927                return;
928            }
929        };
930        self.check_unused_delims_expr(
931            cx,
932            value,
933            ctx,
934            followed_by_block,
935            left_pos,
936            right_pos,
937            is_kw,
938        );
939    }
940
941    fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
942        match s.kind {
943            StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
944                if let Some((init, els)) = local.kind.init_else_opt() {
945                    if els.is_some()
946                        && let ExprKind::Paren(paren) = &init.kind
947                        && !init.span.eq_ctxt(paren.span)
948                    {
949                        // This branch prevents cases where parentheses wrap an expression
950                        // resulting from macro expansion, such as:
951                        // ```
952                        // macro_rules! x {
953                        // () => { None::<i32> };
954                        // }
955                        // let Some(_) = (x!{}) else { return };
956                        // // -> let Some(_) = (None::<i32>) else { return };
957                        // //                  ~           ~ No Lint
958                        // ```
959                        return;
960                    }
961                    let ctx = match els {
962                        None => UnusedDelimsCtx::AssignedValue,
963                        Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
964                    };
965                    self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
966                }
967            }
968            StmtKind::Expr(ref expr) => {
969                self.check_unused_delims_expr(
970                    cx,
971                    expr,
972                    UnusedDelimsCtx::BlockRetValue,
973                    false,
974                    None,
975                    None,
976                    false,
977                );
978            }
979            _ => {}
980        }
981    }
982
983    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
984        use ast::ItemKind::*;
985
986        if let Const(box ast::ConstItem { expr: Some(expr), .. })
987        | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
988        {
989            self.check_unused_delims_expr(
990                cx,
991                expr,
992                UnusedDelimsCtx::AssignedValue,
993                false,
994                None,
995                None,
996                false,
997            );
998        }
999    }
1000}
1001
1002declare_lint! {
1003    /// The `unused_parens` lint detects `if`, `match`, `while` and `return`
1004    /// with parentheses; they do not need them.
1005    ///
1006    /// ### Examples
1007    ///
1008    /// ```rust
1009    /// if(true) {}
1010    /// ```
1011    ///
1012    /// {{produces}}
1013    ///
1014    /// ### Explanation
1015    ///
1016    /// The parentheses are not needed, and should be removed. This is the
1017    /// preferred style for writing these expressions.
1018    pub(super) UNUSED_PARENS,
1019    Warn,
1020    "`if`, `match`, `while` and `return` do not need parentheses"
1021}
1022
1023#[derive(Default)]
1024pub(crate) struct UnusedParens {
1025    with_self_ty_parens: bool,
1026    /// `1 as (i32) < 2` parses to ExprKind::Lt
1027    /// `1 as i32 < 2` parses to i32::<2[missing angle bracket]
1028    parens_in_cast_in_lt: Vec<ast::NodeId>,
1029}
1030
1031impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
1032
1033impl UnusedDelimLint for UnusedParens {
1034    const DELIM_STR: &'static str = "parentheses";
1035
1036    const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
1037
1038    fn lint(&self) -> &'static Lint {
1039        UNUSED_PARENS
1040    }
1041
1042    fn check_unused_delims_expr(
1043        &self,
1044        cx: &EarlyContext<'_>,
1045        value: &ast::Expr,
1046        ctx: UnusedDelimsCtx,
1047        followed_by_block: bool,
1048        left_pos: Option<BytePos>,
1049        right_pos: Option<BytePos>,
1050        is_kw: bool,
1051    ) {
1052        match value.kind {
1053            ast::ExprKind::Paren(ref inner) => {
1054                if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
1055                    && value.attrs.is_empty()
1056                    && !value.span.from_expansion()
1057                    && (ctx != UnusedDelimsCtx::LetScrutineeExpr
1058                        || !matches!(inner.kind, ast::ExprKind::Binary(
1059                                rustc_span::source_map::Spanned { node, .. },
1060                                _,
1061                                _,
1062                            ) if node.is_lazy()))
1063                    && !((ctx == UnusedDelimsCtx::ReturnValue
1064                        || ctx == UnusedDelimsCtx::BreakValue)
1065                        && matches!(inner.kind, ast::ExprKind::Assign(_, _, _)))
1066                {
1067                    self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1068                }
1069            }
1070            ast::ExprKind::Let(_, ref expr, _, _) => {
1071                self.check_unused_delims_expr(
1072                    cx,
1073                    expr,
1074                    UnusedDelimsCtx::LetScrutineeExpr,
1075                    followed_by_block,
1076                    None,
1077                    None,
1078                    false,
1079                );
1080            }
1081            _ => {}
1082        }
1083    }
1084}
1085
1086impl UnusedParens {
1087    fn check_unused_parens_pat(
1088        &self,
1089        cx: &EarlyContext<'_>,
1090        value: &ast::Pat,
1091        avoid_or: bool,
1092        avoid_mut: bool,
1093        keep_space: (bool, bool),
1094    ) {
1095        use ast::{BindingMode, PatKind};
1096
1097        if let PatKind::Paren(inner) = &value.kind {
1098            match inner.kind {
1099                // The lint visitor will visit each subpattern of `p`. We do not want to lint
1100                // any range pattern no matter where it occurs in the pattern. For something like
1101                // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume
1102                // that if there are unnecessary parens they serve a purpose of readability.
1103                PatKind::Range(..) => return,
1104                // Avoid `p0 | .. | pn` if we should.
1105                PatKind::Or(..) if avoid_or => return,
1106                // Avoid `mut x` and `mut x @ p` if we should:
1107                PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1108                    return;
1109                }
1110                // Otherwise proceed with linting.
1111                _ => {}
1112            }
1113            let spans = if !value.span.from_expansion() {
1114                inner
1115                    .span
1116                    .find_ancestor_inside(value.span)
1117                    .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1118            } else {
1119                None
1120            };
1121            self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1122        }
1123    }
1124
1125    fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1126        if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1127            && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1128        {
1129            let mut cur = lhs;
1130            while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1131                cur = rhs;
1132            }
1133
1134            if let ExprKind::Cast(_, ty) = &cur.kind
1135                && let ast::TyKind::Paren(_) = &ty.kind
1136            {
1137                return Some(ty.id);
1138            }
1139        }
1140        None
1141    }
1142}
1143
1144impl EarlyLintPass for UnusedParens {
1145    #[inline]
1146    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1147        if let Some(ty_id) = self.cast_followed_by_lt(e) {
1148            self.parens_in_cast_in_lt.push(ty_id);
1149        }
1150
1151        match e.kind {
1152            ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1153                self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1154            }
1155            // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
1156            // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still
1157            // want to complain about things like `if let 42 = (42)`.
1158            ExprKind::If(ref cond, ref block, ref else_)
1159                if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1160            {
1161                self.check_unused_delims_expr(
1162                    cx,
1163                    cond.peel_parens(),
1164                    UnusedDelimsCtx::LetScrutineeExpr,
1165                    true,
1166                    None,
1167                    None,
1168                    true,
1169                );
1170                for stmt in &block.stmts {
1171                    <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1172                }
1173                if let Some(e) = else_ {
1174                    <Self as UnusedDelimLint>::check_expr(self, cx, e);
1175                }
1176                return;
1177            }
1178            ExprKind::Match(ref _expr, ref arm, _) => {
1179                for a in arm {
1180                    if let Some(body) = &a.body {
1181                        self.check_unused_delims_expr(
1182                            cx,
1183                            body,
1184                            UnusedDelimsCtx::MatchArmExpr,
1185                            false,
1186                            None,
1187                            None,
1188                            true,
1189                        );
1190                    }
1191                }
1192            }
1193            _ => {}
1194        }
1195
1196        <Self as UnusedDelimLint>::check_expr(self, cx, e)
1197    }
1198
1199    fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1200        if let Some(ty_id) = self.cast_followed_by_lt(e) {
1201            let id = self
1202                .parens_in_cast_in_lt
1203                .pop()
1204                .expect("check_expr and check_expr_post must balance");
1205            assert_eq!(
1206                id, ty_id,
1207                "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1208            );
1209        }
1210    }
1211
1212    fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1213        use ast::Mutability;
1214        use ast::PatKind::*;
1215        let keep_space = (false, false);
1216        match &p.kind {
1217            // Do not lint on `(..)` as that will result in the other arms being useless.
1218            Paren(_)
1219            // The other cases do not contain sub-patterns.
1220            | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1221            | Path(..) | Err(_) => {},
1222            // These are list-like patterns; parens can always be removed.
1223            TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1224                self.check_unused_parens_pat(cx, p, false, false, keep_space);
1225            },
1226            Struct(_, _, fps, _) => for f in fps {
1227                self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1228            },
1229            // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
1230            Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1231            // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
1232            // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
1233            Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1234        }
1235    }
1236
1237    fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1238        if let StmtKind::Let(ref local) = s.kind {
1239            self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1240        }
1241
1242        <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1243    }
1244
1245    fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1246        self.check_unused_parens_pat(cx, &param.pat, true, false, (false, false));
1247    }
1248
1249    fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1250        self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1251    }
1252
1253    fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1254        if let ast::TyKind::Paren(_) = ty.kind
1255            && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1256        {
1257            return;
1258        }
1259        match &ty.kind {
1260            ast::TyKind::Array(_, len) => {
1261                self.check_unused_delims_expr(
1262                    cx,
1263                    &len.value,
1264                    UnusedDelimsCtx::ArrayLenExpr,
1265                    false,
1266                    None,
1267                    None,
1268                    false,
1269                );
1270            }
1271            ast::TyKind::Paren(r) => {
1272                match &r.kind {
1273                    ast::TyKind::TraitObject(..) => {}
1274                    ast::TyKind::BareFn(b)
1275                        if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
1276                    ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
1277                    _ => {
1278                        let spans = if !ty.span.from_expansion() {
1279                            r.span
1280                                .find_ancestor_inside(ty.span)
1281                                .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1282                        } else {
1283                            None
1284                        };
1285                        self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1286                    }
1287                }
1288                self.with_self_ty_parens = false;
1289            }
1290            _ => {}
1291        }
1292    }
1293
1294    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1295        <Self as UnusedDelimLint>::check_item(self, cx, item)
1296    }
1297
1298    fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1299        use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1300        if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1301            bounded_ty,
1302            bound_generic_params,
1303            ..
1304        }) = &pred.kind
1305            && let ast::TyKind::Paren(_) = &bounded_ty.kind
1306            && bound_generic_params.is_empty()
1307        {
1308            self.with_self_ty_parens = true;
1309        }
1310    }
1311
1312    fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1313        assert!(!self.with_self_ty_parens);
1314    }
1315}
1316
1317declare_lint! {
1318    /// The `unused_braces` lint detects unnecessary braces around an
1319    /// expression.
1320    ///
1321    /// ### Example
1322    ///
1323    /// ```rust
1324    /// if { true } {
1325    ///     // ...
1326    /// }
1327    /// ```
1328    ///
1329    /// {{produces}}
1330    ///
1331    /// ### Explanation
1332    ///
1333    /// The braces are not needed, and should be removed. This is the
1334    /// preferred style for writing these expressions.
1335    pub(super) UNUSED_BRACES,
1336    Warn,
1337    "unnecessary braces around an expression"
1338}
1339
1340declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1341
1342impl UnusedDelimLint for UnusedBraces {
1343    const DELIM_STR: &'static str = "braces";
1344
1345    const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1346
1347    fn lint(&self) -> &'static Lint {
1348        UNUSED_BRACES
1349    }
1350
1351    fn check_unused_delims_expr(
1352        &self,
1353        cx: &EarlyContext<'_>,
1354        value: &ast::Expr,
1355        ctx: UnusedDelimsCtx,
1356        followed_by_block: bool,
1357        left_pos: Option<BytePos>,
1358        right_pos: Option<BytePos>,
1359        is_kw: bool,
1360    ) {
1361        match value.kind {
1362            ast::ExprKind::Block(ref inner, None)
1363                if inner.rules == ast::BlockCheckMode::Default =>
1364            {
1365                // emit a warning under the following conditions:
1366                //
1367                // - the block does not have a label
1368                // - the block is not `unsafe`
1369                // - the block contains exactly one expression (do not lint `{ expr; }`)
1370                // - `followed_by_block` is true and the internal expr may contain a `{`
1371                // - the block is not multiline (do not lint multiline match arms)
1372                //      ```
1373                //      match expr {
1374                //          Pattern => {
1375                //              somewhat_long_expression
1376                //          }
1377                //          // ...
1378                //      }
1379                //      ```
1380                // - the block has no attribute and was not created inside a macro
1381                // - if the block is an `anon_const`, the inner expr must be a literal
1382                //   not created by a macro, i.e. do not lint on:
1383                //      ```
1384                //      struct A<const N: usize>;
1385                //      let _: A<{ 2 + 3 }>;
1386                //      let _: A<{produces_literal!()}>;
1387                //      ```
1388                // FIXME(const_generics): handle paths when #67075 is fixed.
1389                if let [stmt] = inner.stmts.as_slice() {
1390                    if let ast::StmtKind::Expr(ref expr) = stmt.kind {
1391                        if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1392                            && (ctx != UnusedDelimsCtx::AnonConst
1393                                || (matches!(expr.kind, ast::ExprKind::Lit(_))
1394                                    && !expr.span.from_expansion()))
1395                            && !cx.sess().source_map().is_multiline(value.span)
1396                            && value.attrs.is_empty()
1397                            && !value.span.from_expansion()
1398                            && !inner.span.from_expansion()
1399                        {
1400                            self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1401                        }
1402                    }
1403                }
1404            }
1405            ast::ExprKind::Let(_, ref expr, _, _) => {
1406                self.check_unused_delims_expr(
1407                    cx,
1408                    expr,
1409                    UnusedDelimsCtx::LetScrutineeExpr,
1410                    followed_by_block,
1411                    None,
1412                    None,
1413                    false,
1414                );
1415            }
1416            _ => {}
1417        }
1418    }
1419}
1420
1421impl EarlyLintPass for UnusedBraces {
1422    fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1423        <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1424    }
1425
1426    #[inline]
1427    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1428        <Self as UnusedDelimLint>::check_expr(self, cx, e);
1429
1430        if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1431            self.check_unused_delims_expr(
1432                cx,
1433                &anon_const.value,
1434                UnusedDelimsCtx::AnonConst,
1435                false,
1436                None,
1437                None,
1438                false,
1439            );
1440        }
1441    }
1442
1443    fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1444        if let ast::GenericArg::Const(ct) = arg {
1445            self.check_unused_delims_expr(
1446                cx,
1447                &ct.value,
1448                UnusedDelimsCtx::AnonConst,
1449                false,
1450                None,
1451                None,
1452                false,
1453            );
1454        }
1455    }
1456
1457    fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1458        if let Some(anon_const) = &v.disr_expr {
1459            self.check_unused_delims_expr(
1460                cx,
1461                &anon_const.value,
1462                UnusedDelimsCtx::AnonConst,
1463                false,
1464                None,
1465                None,
1466                false,
1467            );
1468        }
1469    }
1470
1471    fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1472        match ty.kind {
1473            ast::TyKind::Array(_, ref len) => {
1474                self.check_unused_delims_expr(
1475                    cx,
1476                    &len.value,
1477                    UnusedDelimsCtx::ArrayLenExpr,
1478                    false,
1479                    None,
1480                    None,
1481                    false,
1482                );
1483            }
1484
1485            ast::TyKind::Typeof(ref anon_const) => {
1486                self.check_unused_delims_expr(
1487                    cx,
1488                    &anon_const.value,
1489                    UnusedDelimsCtx::AnonConst,
1490                    false,
1491                    None,
1492                    None,
1493                    false,
1494                );
1495            }
1496
1497            _ => {}
1498        }
1499    }
1500
1501    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1502        <Self as UnusedDelimLint>::check_item(self, cx, item)
1503    }
1504}
1505
1506declare_lint! {
1507    /// The `unused_import_braces` lint catches unnecessary braces around an
1508    /// imported item.
1509    ///
1510    /// ### Example
1511    ///
1512    /// ```rust,compile_fail
1513    /// #![deny(unused_import_braces)]
1514    /// use test::{A};
1515    ///
1516    /// pub mod test {
1517    ///     pub struct A;
1518    /// }
1519    /// # fn main() {}
1520    /// ```
1521    ///
1522    /// {{produces}}
1523    ///
1524    /// ### Explanation
1525    ///
1526    /// If there is only a single item, then remove the braces (`use test::A;`
1527    /// for example).
1528    ///
1529    /// This lint is "allow" by default because it is only enforcing a
1530    /// stylistic choice.
1531    UNUSED_IMPORT_BRACES,
1532    Allow,
1533    "unnecessary braces around an imported item"
1534}
1535
1536declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1537
1538impl UnusedImportBraces {
1539    fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1540        if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1541            // Recursively check nested UseTrees
1542            for (tree, _) in items {
1543                self.check_use_tree(cx, tree, item);
1544            }
1545
1546            // Trigger the lint only if there is one nested item
1547            let [(tree, _)] = items.as_slice() else { return };
1548
1549            // Trigger the lint if the nested item is a non-self single item
1550            let node_name = match tree.kind {
1551                ast::UseTreeKind::Simple(rename) => {
1552                    let orig_ident = tree.prefix.segments.last().unwrap().ident;
1553                    if orig_ident.name == kw::SelfLower {
1554                        return;
1555                    }
1556                    rename.unwrap_or(orig_ident).name
1557                }
1558                ast::UseTreeKind::Glob => sym::asterisk,
1559                ast::UseTreeKind::Nested { .. } => return,
1560            };
1561
1562            cx.emit_span_lint(
1563                UNUSED_IMPORT_BRACES,
1564                item.span,
1565                UnusedImportBracesDiag { node: node_name },
1566            );
1567        }
1568    }
1569}
1570
1571impl EarlyLintPass for UnusedImportBraces {
1572    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1573        if let ast::ItemKind::Use(ref use_tree) = item.kind {
1574            self.check_use_tree(cx, use_tree, item);
1575        }
1576    }
1577}
1578
1579declare_lint! {
1580    /// The `unused_allocation` lint detects unnecessary allocations that can
1581    /// be eliminated.
1582    ///
1583    /// ### Example
1584    ///
1585    /// ```rust
1586    /// fn main() {
1587    ///     let a = Box::new([1, 2, 3]).len();
1588    /// }
1589    /// ```
1590    ///
1591    /// {{produces}}
1592    ///
1593    /// ### Explanation
1594    ///
1595    /// When a `box` expression is immediately coerced to a reference, then
1596    /// the allocation is unnecessary, and a reference (using `&` or `&mut`)
1597    /// should be used instead to avoid the allocation.
1598    pub(super) UNUSED_ALLOCATION,
1599    Warn,
1600    "detects unnecessary allocations that can be eliminated"
1601}
1602
1603declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1604
1605impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1606    fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1607        match e.kind {
1608            hir::ExprKind::Call(path_expr, [_])
1609                if let hir::ExprKind::Path(qpath) = &path_expr.kind
1610                    && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1611                    && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1612            _ => return,
1613        }
1614
1615        for adj in cx.typeck_results().expr_adjustments(e) {
1616            if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1617                match m {
1618                    adjustment::AutoBorrowMutability::Not => {
1619                        cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1620                    }
1621                    adjustment::AutoBorrowMutability::Mut { .. } => {
1622                        cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1623                    }
1624                };
1625            }
1626        }
1627    }
1628}