-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #112380 - jieyouxu:useless-bindings-lint, r=WaffleLapkin
Add allow-by-default lint for unit bindings ### Example ```rust #![warn(unit_bindings)] macro_rules! owo { () => { let whats_this = (); } } fn main() { // No warning if user explicitly wrote `()` on either side. let expr = (); let () = expr; let _ = (); let _ = expr; //~ WARN binding has unit type let pat = expr; //~ WARN binding has unit type let _pat = expr; //~ WARN binding has unit type // No warning for let bindings with unit type in macro expansions. owo!(); // No warning if user explicitly annotates the unit type on the binding. let pat: () = expr; } ``` outputs ``` warning: binding has unit type `()` --> $DIR/unit-bindings.rs:17:5 | LL | let _ = expr; | ^^^^-^^^^^^^^ | | | this pattern is inferred to be the unit type `()` | note: the lint level is defined here --> $DIR/unit-bindings.rs:3:9 | LL | #![warn(unit_bindings)] | ^^^^^^^^^^^^^ warning: binding has unit type `()` --> $DIR/unit-bindings.rs:18:5 | LL | let pat = expr; | ^^^^---^^^^^^^^ | | | this pattern is inferred to be the unit type `()` warning: binding has unit type `()` --> $DIR/unit-bindings.rs:19:5 | LL | let _pat = expr; | ^^^^----^^^^^^^^ | | | this pattern is inferred to be the unit type `()` warning: 3 warnings emitted ``` This lint is not triggered if any of the following conditions are met: - The user explicitly annotates the binding with the `()` type. - The binding is from a macro expansion. - The user explicitly wrote `let () = init;` - The user explicitly wrote `let pat = ();`. This is allowed for local lifetimes. ### Known Issue It is known that this lint can trigger on some proc-macro generated code whose span returns false for `Span::from_expansion` because e.g. the proc-macro simply forwards user code spans, and otherwise don't have distinguishing syntax context compared to non-macro-generated code. For those kind of proc-macros, I believe the correct way to fix them is to instead emit identifers with span like `Span::mixed_site().located_at(user_span)`. Closes #71432.
- Loading branch information
Showing
6 changed files
with
88 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 }, | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters