11use clippy_utils:: diagnostics:: span_lint_and_then;
22use clippy_utils:: source:: snippet_with_macro_callsite;
3+ use clippy_utils:: visitors:: for_each_value_source;
4+ use core:: ops:: ControlFlow ;
35use rustc_errors:: Applicability ;
4- use rustc_hir:: { Stmt , StmtKind } ;
6+ use rustc_hir:: { Expr , ExprKind , PatKind , Stmt , StmtKind } ;
57use rustc_lint:: { LateContext , LintContext } ;
68use rustc_middle:: lint:: in_external_macro;
9+ use rustc_middle:: ty:: { self , Ty , TypeFoldable , TypeVisitor } ;
710
811use super :: LET_UNIT_VALUE ;
912
1013pub ( 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+ ) ;
1542 }
43+ } else {
1644 span_lint_and_then (
1745 cx,
1846 LET_UNIT_VALUE ,
@@ -33,3 +61,45 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
3361 }
3462 }
3563}
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