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}