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