rustc_lint/
unit_bindings.rs

1use rustc_hir as hir;
2use rustc_session::{declare_lint, declare_lint_pass};
3
4use crate::lints::UnitBindingsDiag;
5use crate::{LateLintPass, LintContext};
6
7declare_lint! {
8    /// The `unit_bindings` lint detects cases where bindings are useless because they have
9    /// the unit type `()` as their inferred type. The lint is suppressed if the user explicitly
10    /// annotates the let binding with the unit type `()`, or if the let binding uses an underscore
11    /// wildcard pattern, i.e. `let _ = expr`, or if the binding is produced from macro expansions.
12    ///
13    /// ### Example
14    ///
15    /// ```rust,compile_fail
16    /// #![deny(unit_bindings)]
17    ///
18    /// fn foo() {
19    ///     println!("do work");
20    /// }
21    ///
22    /// pub fn main() {
23    ///     let x = foo(); // useless binding
24    /// }
25    /// ```
26    ///
27    /// {{produces}}
28    ///
29    /// ### Explanation
30    ///
31    /// Creating a local binding with the unit type `()` does not do much and can be a sign of a
32    /// user error, such as in this example:
33    ///
34    /// ```rust,no_run
35    /// fn main() {
36    ///     let mut x = [1, 2, 3];
37    ///     x[0] = 5;
38    ///     let y = x.sort(); // useless binding as `sort` returns `()` and not the sorted array.
39    ///     println!("{:?}", y); // prints "()"
40    /// }
41    /// ```
42    pub UNIT_BINDINGS,
43    Allow,
44    "binding is useless because it has the unit `()` type"
45}
46
47declare_lint_pass!(UnitBindings => [UNIT_BINDINGS]);
48
49impl<'tcx> LateLintPass<'tcx> for UnitBindings {
50    fn check_local(&mut self, cx: &crate::LateContext<'tcx>, local: &'tcx hir::LetStmt<'tcx>) {
51        // Suppress warning if user:
52        // - explicitly ascribes a type to the pattern
53        // - explicitly wrote `let pat = ();`
54        // - explicitly wrote `let () = init;`.
55        if !local.span.from_expansion()
56            && let Some(tyck_results) = cx.maybe_typeck_results()
57            && let Some(init) = local.init
58            && let init_ty = tyck_results.expr_ty(init)
59            && let local_ty = tyck_results.node_type(local.hir_id)
60            && init_ty == cx.tcx.types.unit
61            && local_ty == cx.tcx.types.unit
62            && local.ty.is_none()
63            && !matches!(init.kind, hir::ExprKind::Tup([]))
64            && !matches!(local.pat.kind, hir::PatKind::Tuple([], ..))
65        {
66            cx.emit_span_lint(
67                UNIT_BINDINGS,
68                local.span,
69                UnitBindingsDiag { label: local.pat.span },
70            );
71        }
72    }
73}