1- use  crate :: { Diagnostic ,  DiagnosticCode ,  DiagnosticsContext } ; 
1+ use  either:: Either ; 
2+ use  hir:: Semantics ; 
3+ use  ide_db:: text_edit:: TextEdit ; 
4+ use  ide_db:: ty_filter:: TryEnum ; 
5+ use  ide_db:: { RootDatabase ,  source_change:: SourceChange } ; 
6+ use  syntax:: { AstNode ,  ast} ; 
7+ 
8+ use  crate :: { Assist ,  Diagnostic ,  DiagnosticCode ,  DiagnosticsContext ,  fix} ; 
29
310// Diagnostic: non-exhaustive-let 
411// 
@@ -15,11 +22,68 @@ pub(crate) fn non_exhaustive_let(
1522        d. pat . map ( Into :: into) , 
1623    ) 
1724    . stable ( ) 
25+     . with_fixes ( fixes ( & ctx. sema ,  d) ) 
26+ } 
27+ fn  fixes ( sema :  & Semantics < ' _ ,  RootDatabase > ,  d :  & hir:: NonExhaustiveLet )  -> Option < Vec < Assist > >  { 
28+     let  root = sema. parse_or_expand ( d. pat . file_id ) ; 
29+     let  pat = d. pat . value . to_node ( & root) ; 
30+     let  let_stmt = ast:: LetStmt :: cast ( pat. syntax ( ) . parent ( ) ?) ?; 
31+     let  early_node = let_stmt. syntax ( ) . ancestors ( ) . find_map ( AstNode :: cast) ?; 
32+     let  early_text = early_text ( sema,  & early_node) ; 
33+ 
34+     if  let_stmt. let_else ( ) . is_some ( )  { 
35+         return  None ; 
36+     } 
37+ 
38+     let  file_id = d. pat . file_id . file_id ( ) ?. file_id ( sema. db ) ; 
39+     let  semicolon = if  let_stmt. semicolon_token ( ) . is_none ( )  {  ";"  }  else  {  ""  } ; 
40+     let  else_block = format ! ( " else {{ {early_text} }}{semicolon}" ) ; 
41+     let  insert_offset = let_stmt
42+         . semicolon_token ( ) 
43+         . map ( |it| it. text_range ( ) . start ( ) ) 
44+         . unwrap_or_else ( || let_stmt. syntax ( ) . text_range ( ) . end ( ) ) ; 
45+ 
46+     let  source_change =
47+         SourceChange :: from_text_edit ( file_id,  TextEdit :: insert ( insert_offset,  else_block) ) ; 
48+     let  target = sema. original_range ( let_stmt. syntax ( ) ) . range ; 
49+     Some ( vec ! [ fix( "add_let_else_block" ,  "Add let-else block" ,  source_change,  target) ] ) 
50+ } 
51+ 
52+ fn  early_text ( 
53+     sema :  & Semantics < ' _ ,  RootDatabase > , 
54+     early_node :  & Either < ast:: AnyHasLoopBody ,  Either < ast:: Fn ,  ast:: ClosureExpr > > , 
55+ )  -> & ' static  str  { 
56+     match  early_node { 
57+         Either :: Left ( _any_loop)  => "continue" , 
58+         Either :: Right ( Either :: Left ( fn_) )  => sema
59+             . to_def ( fn_) 
60+             . map ( |fn_def| fn_def. ret_type ( sema. db ) ) 
61+             . map ( |ty| return_text ( & ty,  sema) ) 
62+             . unwrap_or ( "return" ) , 
63+         Either :: Right ( Either :: Right ( closure) )  => closure
64+             . body ( ) 
65+             . and_then ( |expr| sema. type_of_expr ( & expr) ) 
66+             . map ( |ty| return_text ( & ty. adjusted ( ) ,  sema) ) 
67+             . unwrap_or ( "return" ) , 
68+     } 
69+ } 
70+ 
71+ fn  return_text ( ty :  & hir:: Type < ' _ > ,  sema :  & Semantics < ' _ ,  RootDatabase > )  -> & ' static  str  { 
72+     if  ty. is_unit ( )  { 
73+         "return" 
74+     }  else  if  let  Some ( try_enum)  = TryEnum :: from_ty ( sema,  ty)  { 
75+         match  try_enum { 
76+             TryEnum :: Option  => "return None" , 
77+             TryEnum :: Result  => "return Err($0)" , 
78+         } 
79+     }  else  { 
80+         "return $0" 
81+     } 
1882} 
1983
2084#[ cfg( test) ]  
2185mod  tests { 
22-     use  crate :: tests:: check_diagnostics; 
86+     use  crate :: tests:: { check_diagnostics,  check_fix } ; 
2387
2488    #[ test]  
2589    fn  option_nonexhaustive ( )  { 
@@ -28,7 +92,7 @@ mod tests {
2892//- minicore: option 
2993fn main() { 
3094    let None = Some(5); 
31-       //^^^^ error: non-exhaustive pattern: `Some(_)` not covered 
95+       //^^^^ 💡  error: non-exhaustive pattern: `Some(_)` not covered 
3296} 
3397"# , 
3498        ) ; 
@@ -54,7 +118,7 @@ fn main() {
54118fn main() { 
55119    '_a: { 
56120        let None = Some(5); 
57-           //^^^^ error: non-exhaustive pattern: `Some(_)` not covered 
121+           //^^^^ 💡  error: non-exhaustive pattern: `Some(_)` not covered 
58122    } 
59123} 
60124"# , 
@@ -66,7 +130,7 @@ fn main() {
66130fn main() { 
67131    let _ = async { 
68132        let None = Some(5); 
69-           //^^^^ error: non-exhaustive pattern: `Some(_)` not covered 
133+           //^^^^ 💡  error: non-exhaustive pattern: `Some(_)` not covered 
70134    }; 
71135} 
72136"# , 
@@ -78,7 +142,7 @@ fn main() {
78142fn main() { 
79143    unsafe { 
80144        let None = Some(5); 
81-           //^^^^ error: non-exhaustive pattern: `Some(_)` not covered 
145+           //^^^^ 💡  error: non-exhaustive pattern: `Some(_)` not covered 
82146    } 
83147} 
84148"# , 
@@ -101,7 +165,7 @@ fn test(x: Result<i32, !>) {
101165//- minicore: result 
102166fn test(x: Result<i32, &'static !>) { 
103167    let Ok(_y) = x; 
104-       //^^^^^^ error: non-exhaustive pattern: `Err(_)` not covered 
168+       //^^^^^^ 💡  error: non-exhaustive pattern: `Err(_)` not covered 
105169} 
106170"# , 
107171        ) ; 
@@ -132,6 +196,113 @@ fn foo(v: Enum<()>) {
132196        ) ; 
133197    } 
134198
199+     #[ test]  
200+     fn  fix_return_in_loop ( )  { 
201+         check_fix ( 
202+             r#" 
203+ //- minicore: option 
204+ fn foo() { 
205+     while cond { 
206+         let None$0 = Some(5); 
207+     } 
208+ } 
209+ "# , 
210+             r#" 
211+ fn foo() { 
212+     while cond { 
213+         let None = Some(5) else { continue }; 
214+     } 
215+ } 
216+ "# , 
217+         ) ; 
218+     } 
219+ 
220+     #[ test]  
221+     fn  fix_return_in_fn ( )  { 
222+         check_fix ( 
223+             r#" 
224+ //- minicore: option 
225+ fn foo() { 
226+     let None$0 = Some(5); 
227+ } 
228+ "# , 
229+             r#" 
230+ fn foo() { 
231+     let None = Some(5) else { return }; 
232+ } 
233+ "# , 
234+         ) ; 
235+     } 
236+ 
237+     #[ test]  
238+     fn  fix_return_in_incomplete_let ( )  { 
239+         check_fix ( 
240+             r#" 
241+ //- minicore: option 
242+ fn foo() { 
243+     let None$0 = Some(5) 
244+ } 
245+ "# , 
246+             r#" 
247+ fn foo() { 
248+     let None = Some(5) else { return }; 
249+ } 
250+ "# , 
251+         ) ; 
252+     } 
253+ 
254+     #[ test]  
255+     fn  fix_return_in_closure ( )  { 
256+         check_fix ( 
257+             r#" 
258+ //- minicore: option 
259+ fn foo() -> Option<()> { 
260+     let _f = || { 
261+         let None$0 = Some(5); 
262+     }; 
263+ } 
264+ "# , 
265+             r#" 
266+ fn foo() -> Option<()> { 
267+     let _f = || { 
268+         let None = Some(5) else { return }; 
269+     }; 
270+ } 
271+ "# , 
272+         ) ; 
273+     } 
274+ 
275+     #[ test]  
276+     fn  fix_return_try_in_fn ( )  { 
277+         check_fix ( 
278+             r#" 
279+ //- minicore: option 
280+ fn foo() -> Option<()> { 
281+     let None$0 = Some(5); 
282+ } 
283+ "# , 
284+             r#" 
285+ fn foo() -> Option<()> { 
286+     let None = Some(5) else { return None }; 
287+ } 
288+ "# , 
289+         ) ; 
290+ 
291+         check_fix ( 
292+             r#" 
293+ //- minicore: option, result 
294+ fn foo() -> Result<(), i32> { 
295+     let None$0 = Some(5); 
296+ } 
297+ "# , 
298+             r#" 
299+ fn foo() -> Result<(), i32> { 
300+     let None = Some(5) else { return Err($0) }; 
301+ } 
302+ "# , 
303+         ) ; 
304+     } 
305+ 
135306    #[ test]  
136307    fn  regression_20259 ( )  { 
137308        check_diagnostics ( 
0 commit comments