|
1 | 1 | use clippy_utils::diagnostics::span_lint_and_then;
|
2 | 2 | use clippy_utils::source::snippet_with_context;
|
3 |
| -use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; |
| 3 | +use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source, is_local_used}; |
4 | 4 | use core::ops::ControlFlow;
|
5 | 5 | use rustc_errors::Applicability;
|
6 | 6 | use rustc_hir::def::{DefKind, Res};
|
| 7 | +use rustc_hir::intravisit::{walk_body, Visitor}; |
7 | 8 | use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind};
|
8 | 9 | use rustc_lint::{LateContext, LintContext};
|
9 | 10 | use rustc_middle::lint::{in_external_macro, is_from_async_await};
|
10 | 11 | use rustc_middle::ty;
|
11 | 12 |
|
12 | 13 | use super::LET_UNIT_VALUE;
|
13 | 14 |
|
14 |
| -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { |
| 15 | +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { |
15 | 16 | // skip `let () = { ... }`
|
16 | 17 | if let PatKind::Tuple(fields, ..) = local.pat.kind
|
17 | 18 | && fields.is_empty()
|
@@ -75,12 +76,53 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
75 | 76 | let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0;
|
76 | 77 | diag.span_suggestion(local.span, "omit the `let` binding", format!("{snip};"), app);
|
77 | 78 | }
|
| 79 | + |
| 80 | + if let PatKind::Binding(_, binding_hir_id, ident, ..) = local.pat.kind |
| 81 | + && let Some(body_id) = cx.enclosing_body.as_ref() |
| 82 | + && let body = cx.tcx.hir().body(*body_id) |
| 83 | + && is_local_used(cx, body, binding_hir_id) |
| 84 | + { |
| 85 | + let identifier = ident.as_str(); |
| 86 | + let mut visitor = UnitVariableCollector::new(binding_hir_id); |
| 87 | + walk_body(&mut visitor, body); |
| 88 | + visitor.spans.into_iter().for_each(|span| { |
| 89 | + let msg = |
| 90 | + format!("variable `{identifier}` of type `()` can be replaced with explicit `()`"); |
| 91 | + diag.span_suggestion(span, msg, "()", Applicability::MachineApplicable); |
| 92 | + }); |
| 93 | + } |
78 | 94 | },
|
79 | 95 | );
|
80 | 96 | }
|
81 | 97 | }
|
82 | 98 | }
|
83 | 99 |
|
| 100 | +struct UnitVariableCollector { |
| 101 | + id: HirId, |
| 102 | + spans: Vec<rustc_span::Span>, |
| 103 | +} |
| 104 | + |
| 105 | +impl UnitVariableCollector { |
| 106 | + fn new(id: HirId) -> Self { |
| 107 | + Self { id, spans: vec![] } |
| 108 | + } |
| 109 | +} |
| 110 | + |
| 111 | +/** |
| 112 | + * Collect all instances where a variable is used based on its `HirId`. |
| 113 | + */ |
| 114 | +impl<'tcx> Visitor<'tcx> for UnitVariableCollector { |
| 115 | + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result { |
| 116 | + if let ExprKind::Path(QPath::Resolved(None, path)) = ex.kind |
| 117 | + && let Res::Local(id) = path.res |
| 118 | + && id == self.id |
| 119 | + { |
| 120 | + self.spans.push(path.span); |
| 121 | + } |
| 122 | + rustc_hir::intravisit::walk_expr(self, ex); |
| 123 | + } |
| 124 | +} |
| 125 | + |
84 | 126 | /// Checks sub-expressions which create the value returned by the given expression for whether
|
85 | 127 | /// return value inference is needed. This checks through locals to see if they also need inference
|
86 | 128 | /// at this point.
|
|
0 commit comments