]>
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; |
923072b8 | 6 | use rustc_hir::def::{DefKind, Res}; |
04454e1e | 7 | use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind}; |
cdc7bbd5 XL |
8 | use rustc_lint::{LateContext, LintContext}; |
9 | use rustc_middle::lint::in_external_macro; | |
923072b8 | 10 | use rustc_middle::ty::{self, Ty, TypeFoldable, TypeSuperFoldable, TypeVisitor}; |
cdc7bbd5 XL |
11 | |
12 | use super::LET_UNIT_VALUE; | |
13 | ||
14 | pub(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 | |
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 | _, | |
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 | ||
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 | } |