11use clippy_utils:: diagnostics:: span_lint_and_then;
2+ use clippy_utils:: get_parent_node;
23use 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} ;
45use core:: ops:: ControlFlow ;
56use rustc_errors:: Applicability ;
67use 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 } ;
89use rustc_lint:: { LateContext , LintContext } ;
910use rustc_middle:: lint:: in_external_macro;
10- use rustc_middle:: ty:: { self , Ty , TypeFoldable , TypeSuperFoldable , TypeVisitor } ;
11+ use rustc_middle:: ty;
1112
1213use super :: LET_UNIT_VALUE ;
1314
14- pub ( super ) fn check ( cx : & LateContext < ' _ > , local : & Local < ' _ > ) {
15+ pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , local : & ' tcx Local < ' _ > ) {
1516 if let Some ( init) = local. init
1617 && !local. pat . span . from_expansion ( )
1718 && !in_external_macro ( cx. sess ( ) , local. span )
1819 && cx. typeck_results ( ) . pat_ty ( local. pat ) . is_unit ( )
1920 {
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 ) ) {
2826 span_lint_and_then (
2927 cx,
3028 LET_UNIT_VALUE ,
3129 local. span ,
3230 "this let-binding has unit value" ,
3331 |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+ ) ;
4038 } ,
4139 ) ;
4240 }
@@ -62,48 +60,106 @@ pub(super) fn check(cx: &LateContext<'_>, local: &Local<'_>) {
6260 }
6361}
6462
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 {
67132 ExprKind :: Call (
68133 Expr {
69134 kind : ExprKind :: Path ( ref path) ,
70135 hir_id,
71136 ..
72137 } ,
73- _ ,
138+ args ,
74139 ) => 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 ) ,
76141 _ => return false ,
77142 } ,
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 ) ,
80145 None => return false ,
81146 } ,
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+ } ,
82155 _ => return false ,
83156 } ;
84157 let sig = cx. tcx . fn_sig ( id) . skip_binder ( ) ;
85158 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+ } )
87162 } else {
88163 false
89164 }
90165}
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