]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
New upstream version 1.63.0+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;
923072b8 6use rustc_hir::def::{DefKind, Res};
04454e1e 7use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
cdc7bbd5
XL
8use rustc_lint::{LateContext, LintContext};
9use rustc_middle::lint::in_external_macro;
923072b8 10use rustc_middle::ty::{self, Ty, TypeFoldable, TypeSuperFoldable, TypeVisitor};
cdc7bbd5
XL
11
12use super::LET_UNIT_VALUE;
13
14pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
04454e1e
FG
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 );
cdc7bbd5 43 }
04454e1e 44 } else {
cdc7bbd5
XL
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}
04454e1e
FG
65
66fn 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 _,
923072b8
FG
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 },
04454e1e
FG
83 _ => return false,
84 };
923072b8
FG
85 let sig = cx.tcx.fn_sig(id).skip_binder();
86 if let ty::Param(output_ty) = *sig.output().kind() {
04454e1e
FG
87 sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
88 } else {
89 false
90 }
91}
92
93fn 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}