]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
Rollup merge of #99394 - JohnTitor:issue-95230, r=compiler-errors
[rust.git] / src / tools / clippy / clippy_lints / src / unit_types / let_unit_value.rs
1 use clippy_utils::diagnostics::span_lint_and_then;
2 use clippy_utils::source::snippet_with_macro_callsite;
3 use clippy_utils::visitors::for_each_value_source;
4 use core::ops::ControlFlow;
5 use rustc_errors::Applicability;
6 use rustc_hir::def::{DefKind, Res};
7 use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
8 use rustc_lint::{LateContext, LintContext};
9 use rustc_middle::lint::in_external_macro;
10 use rustc_middle::ty::{self, Ty, TypeVisitable, TypeSuperVisitable, TypeVisitor};
11
12 use super::LET_UNIT_VALUE;
13
14 pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
15     if let StmtKind::Local(local) = stmt.kind
16         && let Some(init) = local.init
17         && !local.pat.span.from_expansion()
18         && !in_external_macro(cx.sess(), stmt.span)
19         && cx.typeck_results().pat_ty(local.pat).is_unit()
20     {
21         let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
22             ControlFlow::Continue(())
23         } else {
24             ControlFlow::Break(())
25         }).is_continue();
26
27         if needs_inferred {
28             if !matches!(local.pat.kind, PatKind::Wild) {
29                 span_lint_and_then(
30                     cx,
31                     LET_UNIT_VALUE,
32                     stmt.span,
33                     "this let-binding has unit value",
34                     |diag| {
35                             diag.span_suggestion(
36                                 local.pat.span,
37                                 "use a wild (`_`) binding",
38                                 "_",
39                                 Applicability::MaybeIncorrect, // snippet
40                             );
41                     },
42                 );
43             }
44         } else {
45             span_lint_and_then(
46                 cx,
47                 LET_UNIT_VALUE,
48                 stmt.span,
49                 "this let-binding has unit value",
50                 |diag| {
51                     if let Some(expr) = &local.init {
52                         let snip = snippet_with_macro_callsite(cx, expr.span, "()");
53                         diag.span_suggestion(
54                             stmt.span,
55                             "omit the `let` binding",
56                             format!("{};", snip),
57                             Applicability::MachineApplicable, // snippet
58                         );
59                     }
60                 },
61             );
62         }
63     }
64 }
65
66 fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
67     let id = match e.kind {
68         ExprKind::Call(
69             Expr {
70                 kind: ExprKind::Path(ref path),
71                 hir_id,
72                 ..
73             },
74             _,
75         ) => match cx.qpath_res(path, *hir_id) {
76             Res::Def(DefKind::AssocFn | DefKind::Fn, id) => id,
77             _ => return false,
78         },
79         ExprKind::MethodCall(..) => match cx.typeck_results().type_dependent_def_id(e.hir_id) {
80             Some(id) => id,
81             None => return false,
82         },
83         _ => return false,
84     };
85     let sig = cx.tcx.fn_sig(id).skip_binder();
86     if let ty::Param(output_ty) = *sig.output().kind() {
87         sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
88     } else {
89         false
90     }
91 }
92
93 fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool {
94     struct Visitor(u32);
95     impl<'tcx> TypeVisitor<'tcx> for Visitor {
96         type BreakTy = ();
97         fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
98             if let ty::Param(ty) = *ty.kind() {
99                 if ty.index == self.0 {
100                     ControlFlow::BREAK
101                 } else {
102                     ControlFlow::CONTINUE
103                 }
104             } else {
105                 ty.super_visit_with(self)
106             }
107         }
108     }
109     ty.visit_with(&mut Visitor(index)).is_break()
110 }