1 use clippy_utils
::diagnostics
::span_lint_and_then
;
2 use clippy_utils
::get_parent_node
;
3 use clippy_utils
::source
::snippet_with_macro_callsite
;
4 use clippy_utils
::visitors
::{for_each_local_assignment, for_each_value_source}
;
5 use core
::ops
::ControlFlow
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::def
::{DefKind, Res}
;
8 use rustc_hir
::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind}
;
9 use rustc_lint
::{LateContext, LintContext}
;
10 use rustc_middle
::lint
::in_external_macro
;
13 use super::LET_UNIT_VALUE
;
15 pub(super) fn check
<'tcx
>(cx
: &LateContext
<'tcx
>, local
: &'tcx Local
<'_
>) {
16 if let Some(init
) = local
.init
17 && !local
.pat
.span
.from_expansion()
18 && !in_external_macro(cx
.sess(), local
.span
)
19 && cx
.typeck_results().pat_ty(local
.pat
).is_unit()
21 if (local
.ty
.map_or(false, |ty
| !matches
!(ty
.kind
, TyKind
::Infer
))
22 || matches
!(local
.pat
.kind
, PatKind
::Tuple([], None
)))
23 && expr_needs_inferred_result(cx
, init
)
25 if !matches
!(local
.pat
.kind
, PatKind
::Wild
| PatKind
::Tuple([], None
)) {
30 "this let-binding has unit value",
34 "use a wild (`_`) binding",
36 Applicability
::MaybeIncorrect
, // snippet
46 "this let-binding has unit value",
48 if let Some(expr
) = &local
.init
{
49 let snip
= snippet_with_macro_callsite(cx
, expr
.span
, "()");
52 "omit the `let` binding",
54 Applicability
::MachineApplicable
, // snippet
63 /// Checks sub-expressions which create the value returned by the given expression for whether
64 /// return value inference is needed. This checks through locals to see if they also need inference
70 /// let x: u32 = if true { baz() } else { bar };
72 /// Here the sources of the value assigned to `x` would be `baz()`, and `foo()` via the
73 /// initialization of `bar`. If both `foo` and `baz` have a return type which require type
74 /// inference then this function would return `true`.
75 fn expr_needs_inferred_result
<'tcx
>(cx
: &LateContext
<'tcx
>, e
: &'tcx Expr
<'_
>) -> bool
{
76 // The locals used for initialization which have yet to be checked.
77 let mut locals_to_check
= Vec
::new();
78 // All the locals which have been added to `locals_to_check`. Needed to prevent cycles.
79 let mut seen_locals
= HirIdSet
::default();
80 if !each_value_source_needs_inference(cx
, e
, &mut locals_to_check
, &mut seen_locals
) {
83 while let Some(id
) = locals_to_check
.pop() {
84 if let Some(Node
::Local(l
)) = get_parent_node(cx
.tcx
, id
) {
85 if !l
.ty
.map_or(true, |ty
| matches
!(ty
.kind
, TyKind
::Infer
)) {
88 if let Some(e
) = l
.init
{
89 if !each_value_source_needs_inference(cx
, e
, &mut locals_to_check
, &mut seen_locals
) {
92 } else if for_each_local_assignment(cx
, id
, |e
| {
93 if each_value_source_needs_inference(cx
, e
, &mut locals_to_check
, &mut seen_locals
) {
94 ControlFlow
::Continue(())
96 ControlFlow
::Break(())
109 fn each_value_source_needs_inference(
110 cx
: &LateContext
<'_
>,
112 locals_to_check
: &mut Vec
<HirId
>,
113 seen_locals
: &mut HirIdSet
,
115 for_each_value_source(e
, &mut |e
| {
116 if needs_inferred_result_ty(cx
, e
, locals_to_check
, seen_locals
) {
117 ControlFlow
::Continue(())
119 ControlFlow
::Break(())
125 fn needs_inferred_result_ty(
126 cx
: &LateContext
<'_
>,
128 locals_to_check
: &mut Vec
<HirId
>,
129 seen_locals
: &mut HirIdSet
,
131 let (id
, args
) = match e
.kind
{
134 kind
: ExprKind
::Path(ref path
),
139 ) => match cx
.qpath_res(path
, *hir_id
) {
140 Res
::Def(DefKind
::AssocFn
| DefKind
::Fn
, id
) => (id
, args
),
143 ExprKind
::MethodCall(_
, args
, _
) => match cx
.typeck_results().type_dependent_def_id(e
.hir_id
) {
144 Some(id
) => (id
, args
),
145 None
=> return false,
147 ExprKind
::Path(QPath
::Resolved(None
, path
)) => {
148 if let Res
::Local(id
) = path
.res
149 && seen_locals
.insert(id
)
151 locals_to_check
.push(id
);
157 let sig
= cx
.tcx
.fn_sig(id
).skip_binder();
158 if let ty
::Param(output_ty
) = *sig
.output().kind() {
159 sig
.inputs().iter().zip(args
).all(|(&ty
, arg
)| {
160 !ty
.is_param(output_ty
.index
) || each_value_source_needs_inference(cx
, arg
, locals_to_check
, seen_locals
)