diff options
Diffstat (limited to 'compiler/rustc_lint/src/unit_bindings.rs')
-rw-r--r-- | compiler/rustc_lint/src/unit_bindings.rs | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/compiler/rustc_lint/src/unit_bindings.rs b/compiler/rustc_lint/src/unit_bindings.rs new file mode 100644 index 000000000..c80889f3a --- /dev/null +++ b/compiler/rustc_lint/src/unit_bindings.rs @@ -0,0 +1,72 @@ +use crate::lints::UnitBindingsDiag; +use crate::{LateLintPass, LintContext}; +use rustc_hir as hir; +use rustc_middle::ty::Ty; + +declare_lint! { + /// The `unit_bindings` lint detects cases where bindings are useless because they have + /// the unit type `()` as their inferred type. The lint is suppressed if the user explicitly + /// annotates the let binding with the unit type `()`, or if the let binding uses an underscore + /// wildcard pattern, i.e. `let _ = expr`, or if the binding is produced from macro expansions. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unit_bindings)] + /// + /// fn foo() { + /// println!("do work"); + /// } + /// + /// pub fn main() { + /// let x = foo(); // useless binding + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Creating a local binding with the unit type `()` does not do much and can be a sign of a + /// user error, such as in this example: + /// + /// ```rust,no_run + /// fn main() { + /// let mut x = [1, 2, 3]; + /// x[0] = 5; + /// let y = x.sort(); // useless binding as `sort` returns `()` and not the sorted array. + /// println!("{:?}", y); // prints "()" + /// } + /// ``` + pub UNIT_BINDINGS, + Allow, + "binding is useless because it has the unit `()` type" +} + +declare_lint_pass!(UnitBindings => [UNIT_BINDINGS]); + +impl<'tcx> LateLintPass<'tcx> for UnitBindings { + fn check_local(&mut self, cx: &crate::LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { + // Suppress warning if user: + // - explicitly ascribes a type to the pattern + // - explicitly wrote `let pat = ();` + // - explicitly wrote `let () = init;`. + if !local.span.from_expansion() + && let Some(tyck_results) = cx.maybe_typeck_results() + && let Some(init) = local.init + && let init_ty = tyck_results.expr_ty(init) + && let local_ty = tyck_results.node_type(local.hir_id) + && init_ty == Ty::new_unit(cx.tcx) + && local_ty == Ty::new_unit(cx.tcx) + && local.ty.is_none() + && !matches!(init.kind, hir::ExprKind::Tup([])) + && !matches!(local.pat.kind, hir::PatKind::Tuple([], ..)) + { + cx.emit_spanned_lint( + UNIT_BINDINGS, + local.span, + UnitBindingsDiag { label: local.pat.span }, + ); + } + } +} |