summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint/src/unit_bindings.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint/src/unit_bindings.rs')
-rw-r--r--compiler/rustc_lint/src/unit_bindings.rs72
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 },
+ );
+ }
+ }
+}