@@ -77,53 +77,54 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
7777 local. els . is_none ( ) &&
7878 local. ty . is_none ( ) &&
7979 init. span . ctxt ( ) == stmt. span . ctxt ( ) &&
80- let Some ( if_let_or_match) = IfLetOrMatch :: parse ( cx, init) {
81- match if_let_or_match {
82- IfLetOrMatch :: IfLet ( if_let_expr, let_pat, if_then, if_else) => if_chain ! {
83- if expr_is_simple_identity( let_pat, if_then) ;
84- if let Some ( if_else) = if_else;
85- if expr_diverges( cx, if_else) ;
86- then {
87- emit_manual_let_else( cx, stmt. span, if_let_expr, local. pat, let_pat, if_else) ;
88- }
89- } ,
90- IfLetOrMatch :: Match ( match_expr, arms, source) => {
91- if self . matches_behaviour == MatchLintBehaviour :: Never {
92- return ;
93- }
94- if source != MatchSource :: Normal {
95- return ;
96- }
97- // Any other number than two arms doesn't (necessarily)
98- // have a trivial mapping to let else.
99- if arms. len ( ) != 2 {
100- return ;
101- }
102- // Guards don't give us an easy mapping either
103- if arms. iter ( ) . any ( |arm| arm. guard . is_some ( ) ) {
104- return ;
105- }
106- let check_types = self . matches_behaviour == MatchLintBehaviour :: WellKnownTypes ;
107- let diverging_arm_opt = arms
108- . iter ( )
109- . enumerate ( )
110- . find ( |( _, arm) | expr_diverges ( cx, arm. body ) && pat_allowed_for_else ( cx, arm. pat , check_types) ) ;
111- let Some ( ( idx, diverging_arm) ) = diverging_arm_opt else { return ; } ;
112- // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
113- // However, if it arrives in second position, its pattern may cover some cases already covered
114- // by the diverging one.
115- // TODO: accept the non-diverging arm as a second position if patterns are disjointed.
116- if idx == 0 {
117- return ;
118- }
119- let pat_arm = & arms[ 1 - idx] ;
120- if !expr_is_simple_identity ( pat_arm. pat , pat_arm. body ) {
121- return ;
122- }
80+ let Some ( if_let_or_match) = IfLetOrMatch :: parse ( cx, init)
81+ {
82+ match if_let_or_match {
83+ IfLetOrMatch :: IfLet ( if_let_expr, let_pat, if_then, if_else) => if_chain ! {
84+ if expr_is_simple_identity( let_pat, if_then) ;
85+ if let Some ( if_else) = if_else;
86+ if expr_diverges( cx, if_else) ;
87+ then {
88+ emit_manual_let_else( cx, stmt. span, if_let_expr, local. pat, let_pat, if_else) ;
89+ }
90+ } ,
91+ IfLetOrMatch :: Match ( match_expr, arms, source) => {
92+ if self . matches_behaviour == MatchLintBehaviour :: Never {
93+ return ;
94+ }
95+ if source != MatchSource :: Normal {
96+ return ;
97+ }
98+ // Any other number than two arms doesn't (necessarily)
99+ // have a trivial mapping to let else.
100+ if arms. len ( ) != 2 {
101+ return ;
102+ }
103+ // Guards don't give us an easy mapping either
104+ if arms. iter ( ) . any ( |arm| arm. guard . is_some ( ) ) {
105+ return ;
106+ }
107+ let check_types = self . matches_behaviour == MatchLintBehaviour :: WellKnownTypes ;
108+ let diverging_arm_opt = arms
109+ . iter ( )
110+ . enumerate ( )
111+ . find ( |( _, arm) | expr_diverges ( cx, arm. body ) && pat_allowed_for_else ( cx, arm. pat , check_types) ) ;
112+ let Some ( ( idx, diverging_arm) ) = diverging_arm_opt else { return ; } ;
113+ // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
114+ // However, if it arrives in second position, its pattern may cover some cases already covered
115+ // by the diverging one.
116+ // TODO: accept the non-diverging arm as a second position if patterns are disjointed.
117+ if idx == 0 {
118+ return ;
119+ }
120+ let pat_arm = & arms[ 1 - idx] ;
121+ if !expr_is_simple_identity ( pat_arm. pat , pat_arm. body ) {
122+ return ;
123+ }
123124
124- emit_manual_let_else ( cx, stmt. span , match_expr, local. pat , pat_arm. pat , diverging_arm. body ) ;
125- } ,
126- }
125+ emit_manual_let_else ( cx, stmt. span , match_expr, local. pat , pat_arm. pat , diverging_arm. body ) ;
126+ } ,
127+ }
127128 } ;
128129 }
129130
@@ -145,10 +146,9 @@ fn emit_manual_let_else(
145146 "this could be rewritten as `let...else`" ,
146147 |diag| {
147148 // This is far from perfect, for example there needs to be:
148- // * mut additions for the bindings
149- // * renamings of the bindings for `PatKind::Or`
149+ // * tracking for multi-binding cases: let (foo, bar) = if let (Some(foo), Ok(bar)) = ...
150+ // * renamings of the bindings for many `PatKind`s like structs, slices, etc.
150151 // * unused binding collision detection with existing ones
151- // * putting patterns with at the top level | inside ()
152152 // for this to be machine applicable.
153153 let mut app = Applicability :: HasPlaceholders ;
154154 let ( sn_expr, _) = snippet_with_context ( cx, expr. span , span. ctxt ( ) , "" , & mut app) ;
@@ -159,28 +159,62 @@ fn emit_manual_let_else(
159159 } else {
160160 format ! ( "{{ {sn_else} }}" )
161161 } ;
162- let sn_bl = match pat. kind {
163- PatKind :: Or ( ..) => {
164- let ( sn_pat, _) = snippet_with_context ( cx, pat. span , span. ctxt ( ) , "" , & mut app) ;
165- format ! ( "({sn_pat})" )
166- } ,
167- // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
168- PatKind :: TupleStruct ( ref w, args, ..) if args. len ( ) == 1 => {
169- let sn_wrapper = cx. sess ( ) . source_map ( ) . span_to_snippet ( w. span ( ) ) . unwrap_or_default ( ) ;
170- let ( sn_inner, _) = snippet_with_context ( cx, local. span , span. ctxt ( ) , "" , & mut app) ;
171- format ! ( "{sn_wrapper}({sn_inner})" )
172- } ,
173- _ => {
174- let ( sn_pat, _) = snippet_with_context ( cx, pat. span , span. ctxt ( ) , "" , & mut app) ;
175- sn_pat. into_owned ( )
176- } ,
177- } ;
162+ let sn_bl = replace_in_pattern ( cx, span, local, pat, & mut app) ;
178163 let sugg = format ! ( "let {sn_bl} = {sn_expr} else {else_bl};" ) ;
179164 diag. span_suggestion ( span, "consider writing" , sugg, app) ;
180165 } ,
181166 ) ;
182167}
183168
169+ // replaces the locals in the pattern
170+ fn replace_in_pattern (
171+ cx : & LateContext < ' _ > ,
172+ span : Span ,
173+ local : & Pat < ' _ > ,
174+ pat : & Pat < ' _ > ,
175+ app : & mut Applicability ,
176+ ) -> String {
177+ let mut bindings_count = 0 ;
178+ pat. each_binding_or_first ( & mut |_, _, _, _| bindings_count += 1 ) ;
179+ // If the pattern creates multiple bindings, exit early,
180+ // as otherwise we might paste the pattern to the positions of multiple bindings.
181+ if bindings_count > 1 {
182+ let ( sn_pat, _) = snippet_with_context ( cx, pat. span , span. ctxt ( ) , "" , app) ;
183+ return sn_pat. into_owned ( ) ;
184+ }
185+
186+ match pat. kind {
187+ PatKind :: Binding ( ..) => {
188+ let ( sn_bdg, _) = snippet_with_context ( cx, local. span , span. ctxt ( ) , "" , app) ;
189+ return sn_bdg. to_string ( ) ;
190+ } ,
191+ PatKind :: Or ( pats) => {
192+ let patterns = pats
193+ . iter ( )
194+ . map ( |pat| replace_in_pattern ( cx, span, local, pat, app) )
195+ . collect :: < Vec < _ > > ( ) ;
196+ let or_pat = patterns. join ( " | " ) ;
197+ return format ! ( "({or_pat})" ) ;
198+ } ,
199+ // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
200+ PatKind :: TupleStruct ( ref w, args, dot_dot_pos) => {
201+ let mut args = args
202+ . iter ( )
203+ . map ( |pat| replace_in_pattern ( cx, span, local, pat, app) )
204+ . collect :: < Vec < _ > > ( ) ;
205+ if let Some ( pos) = dot_dot_pos. as_opt_usize ( ) {
206+ args. insert ( pos, ".." . to_owned ( ) ) ;
207+ }
208+ let args = args. join ( ", " ) ;
209+ let sn_wrapper = cx. sess ( ) . source_map ( ) . span_to_snippet ( w. span ( ) ) . unwrap_or_default ( ) ;
210+ return format ! ( "{sn_wrapper}({args})" ) ;
211+ } ,
212+ _ => { } ,
213+ }
214+ let ( sn_pat, _) = snippet_with_context ( cx, pat. span , span. ctxt ( ) , "" , app) ;
215+ sn_pat. into_owned ( )
216+ }
217+
184218/// Check whether an expression is divergent. May give false negatives.
185219fn expr_diverges ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
186220 struct V < ' cx , ' tcx > {
0 commit comments