]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 | 1 | use clippy_utils::diagnostics::span_lint_and_then; |
cdc7bbd5 | 2 | use clippy_utils::source::snippet_with_macro_callsite; |
04454e1e FG |
3 | use clippy_utils::visitors::for_each_value_source; |
4 | use core::ops::ControlFlow; | |
cdc7bbd5 | 5 | use rustc_errors::Applicability; |
04454e1e | 6 | use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind}; |
cdc7bbd5 XL |
7 | use rustc_lint::{LateContext, LintContext}; |
8 | use rustc_middle::lint::in_external_macro; | |
04454e1e | 9 | use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor}; |
cdc7bbd5 XL |
10 | |
11 | use super::LET_UNIT_VALUE; | |
12 | ||
13 | pub(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 | |
65 | fn 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 | ||
88 | fn 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 | } |