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