1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
+ use clippy_utils:: get_parent_node;
2
3
use clippy_utils:: source:: snippet_with_macro_callsite;
3
- use clippy_utils:: visitors:: for_each_value_source;
4
+ use clippy_utils:: visitors:: { for_each_local_assignment , for_each_value_source} ;
4
5
use core:: ops:: ControlFlow ;
5
6
use rustc_errors:: Applicability ;
6
7
use rustc_hir:: def:: { DefKind , Res } ;
7
- use rustc_hir:: { Expr , ExprKind , Local , PatKind } ;
8
+ use rustc_hir:: { Expr , ExprKind , HirId , HirIdSet , Local , Node , PatKind , QPath , TyKind } ;
8
9
use rustc_lint:: { LateContext , LintContext } ;
9
10
use rustc_middle:: lint:: in_external_macro;
10
- use rustc_middle:: ty:: { self , Ty , TypeFoldable , TypeSuperFoldable , TypeVisitor } ;
11
+ use rustc_middle:: ty;
11
12
12
13
use super :: LET_UNIT_VALUE ;
13
14
14
- pub ( super ) fn check ( cx : & LateContext < ' _ > , local : & Local < ' _ > ) {
15
+ pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , local : & ' tcx Local < ' _ > ) {
15
16
if let Some ( init) = local. init
16
17
&& !local. pat . span . from_expansion ( )
17
18
&& !in_external_macro ( cx. sess ( ) , local. span )
18
19
&& cx. typeck_results ( ) . pat_ty ( local. pat ) . is_unit ( )
19
20
{
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 ) {
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)
24
+ {
25
+ if !matches ! ( local. pat. kind, PatKind :: Wild | PatKind :: Tuple ( [ ] , None ) ) {
28
26
span_lint_and_then (
29
27
cx,
30
28
LET_UNIT_VALUE ,
31
29
local. span ,
32
30
"this let-binding has unit value" ,
33
31
|diag| {
34
- diag. span_suggestion (
35
- local. pat . span ,
36
- "use a wild (`_`) binding" ,
37
- "_" ,
38
- Applicability :: MaybeIncorrect , // snippet
39
- ) ;
32
+ diag. span_suggestion (
33
+ local. pat . span ,
34
+ "use a wild (`_`) binding" ,
35
+ "_" ,
36
+ Applicability :: MaybeIncorrect , // snippet
37
+ ) ;
40
38
} ,
41
39
) ;
42
40
}
@@ -62,48 +60,106 @@ pub(super) fn check(cx: &LateContext<'_>, local: &Local<'_>) {
62
60
}
63
61
}
64
62
65
- fn needs_inferred_result_ty ( cx : & LateContext < ' _ > , e : & Expr < ' _ > ) -> bool {
66
- let id = match e. kind {
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
65
+ /// at this point.
66
+ ///
67
+ /// e.g.
68
+ /// ```rust,ignore
69
+ /// let bar = foo();
70
+ /// let x: u32 = if true { baz() } else { bar };
71
+ /// ```
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) {
81
+ return false ;
82
+ }
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 ) ) {
86
+ return false ;
87
+ }
88
+ if let Some ( e) = l. init {
89
+ if !each_value_source_needs_inference ( cx, e, & mut locals_to_check, & mut seen_locals) {
90
+ return false ;
91
+ }
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 ( ( ) )
95
+ } else {
96
+ ControlFlow :: Break ( ( ) )
97
+ }
98
+ } )
99
+ . is_break ( )
100
+ {
101
+ return false ;
102
+ }
103
+ }
104
+ }
105
+
106
+ true
107
+ }
108
+
109
+ fn each_value_source_needs_inference (
110
+ cx : & LateContext < ' _ > ,
111
+ e : & Expr < ' _ > ,
112
+ locals_to_check : & mut Vec < HirId > ,
113
+ seen_locals : & mut HirIdSet ,
114
+ ) -> bool {
115
+ for_each_value_source ( e, & mut |e| {
116
+ if needs_inferred_result_ty ( cx, e, locals_to_check, seen_locals) {
117
+ ControlFlow :: Continue ( ( ) )
118
+ } else {
119
+ ControlFlow :: Break ( ( ) )
120
+ }
121
+ } )
122
+ . is_continue ( )
123
+ }
124
+
125
+ fn needs_inferred_result_ty (
126
+ cx : & LateContext < ' _ > ,
127
+ e : & Expr < ' _ > ,
128
+ locals_to_check : & mut Vec < HirId > ,
129
+ seen_locals : & mut HirIdSet ,
130
+ ) -> bool {
131
+ let ( id, args) = match e. kind {
67
132
ExprKind :: Call (
68
133
Expr {
69
134
kind : ExprKind :: Path ( ref path) ,
70
135
hir_id,
71
136
..
72
137
} ,
73
- _ ,
138
+ args ,
74
139
) => match cx. qpath_res ( path, * hir_id) {
75
- Res :: Def ( DefKind :: AssocFn | DefKind :: Fn , id) => id ,
140
+ Res :: Def ( DefKind :: AssocFn | DefKind :: Fn , id) => ( id , args ) ,
76
141
_ => return false ,
77
142
} ,
78
- ExprKind :: MethodCall ( .. ) => match cx. typeck_results ( ) . type_dependent_def_id ( e. hir_id ) {
79
- Some ( id) => id ,
143
+ ExprKind :: MethodCall ( _ , args , _ ) => match cx. typeck_results ( ) . type_dependent_def_id ( e. hir_id ) {
144
+ Some ( id) => ( id , args ) ,
80
145
None => return false ,
81
146
} ,
147
+ ExprKind :: Path ( QPath :: Resolved ( None , path) ) => {
148
+ if let Res :: Local ( id) = path. res
149
+ && seen_locals. insert ( id)
150
+ {
151
+ locals_to_check. push ( id) ;
152
+ }
153
+ return true ;
154
+ } ,
82
155
_ => return false ,
83
156
} ;
84
157
let sig = cx. tcx . fn_sig ( id) . skip_binder ( ) ;
85
158
if let ty:: Param ( output_ty) = * sig. output ( ) . kind ( ) {
86
- sig. inputs ( ) . iter ( ) . all ( |& ty| !ty_contains_param ( ty, output_ty. index ) )
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)
161
+ } )
87
162
} else {
88
163
false
89
164
}
90
165
}
91
-
92
- fn ty_contains_param ( ty : Ty < ' _ > , index : u32 ) -> bool {
93
- struct Visitor ( u32 ) ;
94
- impl < ' tcx > TypeVisitor < ' tcx > for Visitor {
95
- type BreakTy = ( ) ;
96
- fn visit_ty ( & mut self , ty : Ty < ' tcx > ) -> ControlFlow < Self :: BreakTy > {
97
- if let ty:: Param ( ty) = * ty. kind ( ) {
98
- if ty. index == self . 0 {
99
- ControlFlow :: BREAK
100
- } else {
101
- ControlFlow :: CONTINUE
102
- }
103
- } else {
104
- ty. super_visit_with ( self )
105
- }
106
- }
107
- }
108
- ty. visit_with ( & mut Visitor ( index) ) . is_break ( )
109
- }
0 commit comments