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