1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
2
use clippy_utils:: source:: snippet_with_macro_callsite;
3
+ use clippy_utils:: visitors:: for_each_value_source;
4
+ use core:: ops:: ControlFlow ;
3
5
use rustc_errors:: Applicability ;
4
- use rustc_hir:: { Stmt , StmtKind } ;
6
+ use rustc_hir:: { Expr , ExprKind , PatKind , Stmt , StmtKind } ;
5
7
use rustc_lint:: { LateContext , LintContext } ;
6
8
use rustc_middle:: lint:: in_external_macro;
9
+ use rustc_middle:: ty:: { self , Ty , TypeFoldable , TypeVisitor } ;
7
10
8
11
use super :: LET_UNIT_VALUE ;
9
12
10
13
pub ( super ) fn check ( cx : & LateContext < ' _ > , stmt : & Stmt < ' _ > ) {
11
- if let StmtKind :: Local ( local) = stmt. kind {
12
- if cx. typeck_results ( ) . pat_ty ( local. pat ) . is_unit ( ) {
13
- if in_external_macro ( cx. sess ( ) , stmt. span ) || local. pat . span . from_expansion ( ) {
14
- return ;
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
+ "_" . into ( ) ,
38
+ Applicability :: MaybeIncorrect , // snippet
39
+ ) ;
40
+ } ,
41
+ ) ;
15
42
}
43
+ } else {
16
44
span_lint_and_then (
17
45
cx,
18
46
LET_UNIT_VALUE ,
@@ -33,3 +61,45 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
33
61
}
34
62
}
35
63
}
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
+ }
0 commit comments