aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladyslav Katasonov <[email protected]>2021-02-10 16:26:42 +0000
committerVladyslav Katasonov <[email protected]>2021-02-13 19:04:52 +0000
commit9eb19d92dd8d3200f3530faefa7a4048f58d280d (patch)
tree2414b2478255d2856ccffb168e6092145bde8fbd
parentf345d1772ab3827fbc3e31428b0d9479cab0ea39 (diff)
allow try expr? when extacting function
-rw-r--r--crates/assists/src/handlers/extract_function.rs377
-rw-r--r--crates/syntax/src/ast/make.rs3
2 files changed, 350 insertions, 30 deletions
diff --git a/crates/assists/src/handlers/extract_function.rs b/crates/assists/src/handlers/extract_function.rs
index 4372479b9..225a50d2d 100644
--- a/crates/assists/src/handlers/extract_function.rs
+++ b/crates/assists/src/handlers/extract_function.rs
@@ -84,7 +84,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
84 // We should not have variables that outlive body if we have expression block 84 // We should not have variables that outlive body if we have expression block
85 return None; 85 return None;
86 } 86 }
87 let control_flow = external_control_flow(&body)?; 87 let control_flow = external_control_flow(ctx, &body)?;
88 88
89 let target_range = body.text_range(); 89 let target_range = body.text_range();
90 90
@@ -117,7 +117,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
117 ) 117 )
118} 118}
119 119
120fn external_control_flow(body: &FunctionBody) -> Option<ControlFlow> { 120fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<ControlFlow> {
121 let mut ret_expr = None; 121 let mut ret_expr = None;
122 let mut try_expr = None; 122 let mut try_expr = None;
123 let mut break_expr = None; 123 let mut break_expr = None;
@@ -180,35 +180,71 @@ fn external_control_flow(body: &FunctionBody) -> Option<ControlFlow> {
180 } 180 }
181 } 181 }
182 182
183 if try_expr.is_some() { 183 let kind = match (try_expr, ret_expr, break_expr, continue_expr) {
184 // FIXME: support try 184 (Some(e), None, None, None) => {
185 return None; 185 let func = e.syntax().ancestors().find_map(ast::Fn::cast)?;
186 } 186 let def = ctx.sema.to_def(&func)?;
187 let ret_ty = def.ret_type(ctx.db());
188 let kind = try_kind_of_ty(ret_ty, ctx)?;
187 189
188 let kind = match (ret_expr, break_expr, continue_expr) { 190 Some(FlowKind::Try { kind })
189 (Some(r), None, None) => match r.expr() { 191 }
192 (Some(_), Some(r), None, None) => match r.expr() {
193 Some(expr) => {
194 if let Some(kind) = expr_err_kind(&expr, ctx) {
195 Some(FlowKind::TryReturn { expr, kind })
196 } else {
197 mark::hit!(external_control_flow_try_and_return_non_err);
198 return None;
199 }
200 }
201 None => return None,
202 },
203 (Some(_), _, _, _) => {
204 mark::hit!(external_control_flow_try_and_bc);
205 return None;
206 }
207 (None, Some(r), None, None) => match r.expr() {
190 Some(expr) => Some(FlowKind::ReturnValue(expr)), 208 Some(expr) => Some(FlowKind::ReturnValue(expr)),
191 None => Some(FlowKind::Return), 209 None => Some(FlowKind::Return),
192 }, 210 },
193 (Some(_), _, _) => { 211 (None, Some(_), _, _) => {
194 mark::hit!(external_control_flow_return_and_bc); 212 mark::hit!(external_control_flow_return_and_bc);
195 return None; 213 return None;
196 } 214 }
197 (None, Some(_), Some(_)) => { 215 (None, None, Some(_), Some(_)) => {
198 mark::hit!(external_control_flow_break_and_continue); 216 mark::hit!(external_control_flow_break_and_continue);
199 return None; 217 return None;
200 } 218 }
201 (None, Some(b), None) => match b.expr() { 219 (None, None, Some(b), None) => match b.expr() {
202 Some(expr) => Some(FlowKind::BreakValue(expr)), 220 Some(expr) => Some(FlowKind::BreakValue(expr)),
203 None => Some(FlowKind::Break), 221 None => Some(FlowKind::Break),
204 }, 222 },
205 (None, None, Some(_)) => Some(FlowKind::Continue), 223 (None, None, None, Some(_)) => Some(FlowKind::Continue),
206 (None, None, None) => None, 224 (None, None, None, None) => None,
207 }; 225 };
208 226
209 Some(ControlFlow { kind }) 227 Some(ControlFlow { kind })
210} 228}
211 229
230/// Checks is expr is `Err(_)` or `None`
231fn expr_err_kind(expr: &ast::Expr, ctx: &AssistContext) -> Option<TryKind> {
232 let call_expr = match expr {
233 ast::Expr::CallExpr(call_expr) => call_expr,
234 _ => return None,
235 };
236 let func = call_expr.expr()?;
237 let text = func.syntax().text();
238
239 if text == "Err" {
240 Some(TryKind::Result { ty: ctx.sema.type_of_expr(expr)? })
241 } else if text == "None" {
242 Some(TryKind::Option)
243 } else {
244 None
245 }
246}
247
212#[derive(Debug)] 248#[derive(Debug)]
213struct Function { 249struct Function {
214 name: String, 250 name: String,
@@ -330,6 +366,13 @@ enum FlowKind {
330 Return, 366 Return,
331 /// Return with value (`return $expr;`) 367 /// Return with value (`return $expr;`)
332 ReturnValue(ast::Expr), 368 ReturnValue(ast::Expr),
369 Try {
370 kind: TryKind,
371 },
372 TryReturn {
373 expr: ast::Expr,
374 kind: TryKind,
375 },
333 /// Break without value (`return;`) 376 /// Break without value (`return;`)
334 Break, 377 Break,
335 /// Break with value (`break $expr;`) 378 /// Break with value (`break $expr;`)
@@ -338,11 +381,21 @@ enum FlowKind {
338 Continue, 381 Continue,
339} 382}
340 383
384#[derive(Debug, Clone)]
385enum TryKind {
386 Option,
387 Result { ty: hir::Type },
388}
389
341impl FlowKind { 390impl FlowKind {
342 fn make_expr(&self, expr: Option<ast::Expr>) -> ast::Expr { 391 fn make_result_handler(&self, expr: Option<ast::Expr>) -> ast::Expr {
343 match self { 392 match self {
344 FlowKind::Return | FlowKind::ReturnValue(_) => make::expr_return(expr), 393 FlowKind::Return | FlowKind::ReturnValue(_) => make::expr_return(expr),
345 FlowKind::Break | FlowKind::BreakValue(_) => make::expr_break(expr), 394 FlowKind::Break | FlowKind::BreakValue(_) => make::expr_break(expr),
395 FlowKind::Try { .. } | FlowKind::TryReturn { .. } => {
396 stdx::never!("cannot have result handler with try");
397 expr.unwrap_or_else(|| make::expr_return(None))
398 }
346 FlowKind::Continue => { 399 FlowKind::Continue => {
347 stdx::always!(expr.is_none(), "continue with value is not possible"); 400 stdx::always!(expr.is_none(), "continue with value is not possible");
348 make::expr_continue() 401 make::expr_continue()
@@ -352,12 +405,34 @@ impl FlowKind {
352 405
353 fn expr_ty(&self, ctx: &AssistContext) -> Option<hir::Type> { 406 fn expr_ty(&self, ctx: &AssistContext) -> Option<hir::Type> {
354 match self { 407 match self {
355 FlowKind::ReturnValue(expr) | FlowKind::BreakValue(expr) => ctx.sema.type_of_expr(expr), 408 FlowKind::ReturnValue(expr)
409 | FlowKind::BreakValue(expr)
410 | FlowKind::TryReturn { expr, .. } => ctx.sema.type_of_expr(expr),
411 FlowKind::Try { .. } => {
412 stdx::never!("try does not have defined expr_ty");
413 None
414 }
356 FlowKind::Return | FlowKind::Break | FlowKind::Continue => None, 415 FlowKind::Return | FlowKind::Break | FlowKind::Continue => None,
357 } 416 }
358 } 417 }
359} 418}
360 419
420fn try_kind_of_ty(ty: hir::Type, ctx: &AssistContext) -> Option<TryKind> {
421 if ty.is_unknown() {
422 // We favour Result for `expr?`
423 return Some(TryKind::Result { ty });
424 }
425 let adt = ty.as_adt()?;
426 let name = adt.name(ctx.db());
427 // FIXME: use lang items to determine if it is std type or user defined
428 // E.g. if user happens to define type named `Option`, we would have false positive
429 match name.to_string().as_str() {
430 "Option" => Some(TryKind::Option),
431 "Result" => Some(TryKind::Result { ty }),
432 _ => None,
433 }
434}
435
361#[derive(Debug)] 436#[derive(Debug)]
362enum RetType { 437enum RetType {
363 Expr(hir::Type), 438 Expr(hir::Type),
@@ -851,7 +926,7 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
851 926
852 let handler = FlowHandler::from_ret_ty(fun, &ret_ty); 927 let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
853 928
854 let expr = handler.make_expr(call_expr).indent(indent); 929 let expr = handler.make_call_expr(call_expr).indent(indent);
855 930
856 let mut buf = String::new(); 931 let mut buf = String::new();
857 match fun.vars_defined_in_body_and_outlive.as_slice() { 932 match fun.vars_defined_in_body_and_outlive.as_slice() {
@@ -877,6 +952,7 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
877 952
878enum FlowHandler { 953enum FlowHandler {
879 None, 954 None,
955 Try { kind: TryKind },
880 If { action: FlowKind }, 956 If { action: FlowKind },
881 IfOption { action: FlowKind }, 957 IfOption { action: FlowKind },
882 MatchOption { none: FlowKind }, 958 MatchOption { none: FlowKind },
@@ -897,6 +973,9 @@ impl FlowHandler {
897 FlowKind::ReturnValue(_) | FlowKind::BreakValue(_) => { 973 FlowKind::ReturnValue(_) | FlowKind::BreakValue(_) => {
898 FlowHandler::IfOption { action } 974 FlowHandler::IfOption { action }
899 } 975 }
976 FlowKind::Try { kind } | FlowKind::TryReturn { kind, .. } => {
977 FlowHandler::Try { kind: kind.clone() }
978 }
900 } 979 }
901 } else { 980 } else {
902 match flow_kind { 981 match flow_kind {
@@ -906,17 +985,21 @@ impl FlowHandler {
906 FlowKind::ReturnValue(_) | FlowKind::BreakValue(_) => { 985 FlowKind::ReturnValue(_) | FlowKind::BreakValue(_) => {
907 FlowHandler::MatchResult { err: action } 986 FlowHandler::MatchResult { err: action }
908 } 987 }
988 FlowKind::Try { kind } | FlowKind::TryReturn { kind, .. } => {
989 FlowHandler::Try { kind: kind.clone() }
990 }
909 } 991 }
910 } 992 }
911 } 993 }
912 } 994 }
913 } 995 }
914 996
915 fn make_expr(&self, call_expr: ast::Expr) -> ast::Expr { 997 fn make_call_expr(&self, call_expr: ast::Expr) -> ast::Expr {
916 match self { 998 match self {
917 FlowHandler::None => call_expr, 999 FlowHandler::None => call_expr,
1000 FlowHandler::Try { kind: _ } => make::expr_try(call_expr),
918 FlowHandler::If { action } => { 1001 FlowHandler::If { action } => {
919 let action = action.make_expr(None); 1002 let action = action.make_result_handler(None);
920 let stmt = make::expr_stmt(action); 1003 let stmt = make::expr_stmt(action);
921 let block = make::block_expr(iter::once(stmt.into()), None); 1004 let block = make::block_expr(iter::once(stmt.into()), None);
922 let condition = make::condition(call_expr, None); 1005 let condition = make::condition(call_expr, None);
@@ -928,7 +1011,7 @@ impl FlowHandler {
928 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1011 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
929 let cond = make::condition(call_expr, Some(pattern.into())); 1012 let cond = make::condition(call_expr, Some(pattern.into()));
930 let value = make::expr_path(make_path_from_text("value")); 1013 let value = make::expr_path(make_path_from_text("value"));
931 let action_expr = action.make_expr(Some(value)); 1014 let action_expr = action.make_result_handler(Some(value));
932 let action_stmt = make::expr_stmt(action_expr); 1015 let action_stmt = make::expr_stmt(action_expr);
933 let then = make::block_expr(iter::once(action_stmt.into()), None); 1016 let then = make::block_expr(iter::once(action_stmt.into()), None);
934 make::expr_if(cond, then, None) 1017 make::expr_if(cond, then, None)
@@ -946,7 +1029,7 @@ impl FlowHandler {
946 let none_arm = { 1029 let none_arm = {
947 let path = make_path_from_text("None"); 1030 let path = make_path_from_text("None");
948 let pat = make::path_pat(path); 1031 let pat = make::path_pat(path);
949 make::match_arm(iter::once(pat), none.make_expr(None)) 1032 make::match_arm(iter::once(pat), none.make_result_handler(None))
950 }; 1033 };
951 let arms = make::match_arm_list(vec![some_arm, none_arm]); 1034 let arms = make::match_arm_list(vec![some_arm, none_arm]);
952 make::expr_match(call_expr, arms) 1035 make::expr_match(call_expr, arms)
@@ -967,7 +1050,7 @@ impl FlowHandler {
967 let value_pat = make::ident_pat(make::name(err_name)); 1050 let value_pat = make::ident_pat(make::name(err_name));
968 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1051 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
969 let value = make::expr_path(make_path_from_text(err_name)); 1052 let value = make::expr_path(make_path_from_text(err_name));
970 make::match_arm(iter::once(pat.into()), err.make_expr(Some(value))) 1053 make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value)))
971 }; 1054 };
972 let arms = make::match_arm_list(vec![ok_arm, err_arm]); 1055 let arms = make::match_arm_list(vec![ok_arm, err_arm]);
973 make::expr_match(call_expr, arms) 1056 make::expr_match(call_expr, arms)
@@ -1035,14 +1118,25 @@ impl FunType {
1035} 1118}
1036 1119
1037fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Option<ast::RetType> { 1120fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Option<ast::RetType> {
1038 let ty = fun.return_type(ctx); 1121 let fun_ty = fun.return_type(ctx);
1039 let handler = FlowHandler::from_ret_ty(fun, &ty); 1122 let handler = FlowHandler::from_ret_ty(fun, &fun_ty);
1040 let ret_ty = match &handler { 1123 let ret_ty = match &handler {
1041 FlowHandler::None => { 1124 FlowHandler::None => {
1042 if matches!(ty, FunType::Unit) { 1125 if matches!(fun_ty, FunType::Unit) {
1043 return None; 1126 return None;
1044 } 1127 }
1045 ty.make_ty(ctx, module) 1128 fun_ty.make_ty(ctx, module)
1129 }
1130 FlowHandler::Try { kind: TryKind::Option } => {
1131 make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module)))
1132 }
1133 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => {
1134 let handler_ty =
1135 result_err_ty(parent_ret_ty, ctx, module).unwrap_or_else(make::ty_unit);
1136 make::ty_generic(
1137 make::name_ref("Result"),
1138 vec![fun_ty.make_ty(ctx, module), handler_ty],
1139 )
1046 } 1140 }
1047 FlowHandler::If { .. } => make::ty("bool"), 1141 FlowHandler::If { .. } => make::ty("bool"),
1048 FlowHandler::IfOption { action } => { 1142 FlowHandler::IfOption { action } => {
@@ -1053,17 +1147,42 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
1053 make::ty_generic(make::name_ref("Option"), iter::once(handler_ty)) 1147 make::ty_generic(make::name_ref("Option"), iter::once(handler_ty))
1054 } 1148 }
1055 FlowHandler::MatchOption { .. } => { 1149 FlowHandler::MatchOption { .. } => {
1056 make::ty_generic(make::name_ref("Option"), iter::once(ty.make_ty(ctx, module))) 1150 make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module)))
1057 } 1151 }
1058 FlowHandler::MatchResult { err } => { 1152 FlowHandler::MatchResult { err } => {
1059 let handler_ty = 1153 let handler_ty =
1060 err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit); 1154 err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit);
1061 make::ty_generic(make::name_ref("Result"), vec![ty.make_ty(ctx, module), handler_ty]) 1155 make::ty_generic(
1156 make::name_ref("Result"),
1157 vec![fun_ty.make_ty(ctx, module), handler_ty],
1158 )
1062 } 1159 }
1063 }; 1160 };
1064 Some(make::ret_type(ret_ty)) 1161 Some(make::ret_type(ret_ty))
1065} 1162}
1066 1163
1164/// Extract `E` type from `Result<T, E>`
1165fn result_err_ty(
1166 parent_ret_ty: &hir::Type,
1167 ctx: &AssistContext,
1168 module: hir::Module,
1169) -> Option<ast::Type> {
1170 // FIXME: use hir to extract argument information
1171 // currently we use `format -> take part -> parse`
1172 let path_ty = match make_ty(&parent_ret_ty, ctx, module) {
1173 ast::Type::PathType(path_ty) => path_ty,
1174 _ => return None,
1175 };
1176 let arg_list = path_ty.path()?.segment()?.generic_arg_list()?;
1177 let err_arg = arg_list.generic_args().nth(1)?;
1178 let type_arg = match err_arg {
1179 ast::GenericArg::TypeArg(type_arg) => type_arg,
1180 _ => return None,
1181 };
1182
1183 type_arg.ty()
1184}
1185
1067fn make_body( 1186fn make_body(
1068 ctx: &AssistContext, 1187 ctx: &AssistContext,
1069 old_indent: IndentLevel, 1188 old_indent: IndentLevel,
@@ -1128,6 +1247,18 @@ fn make_body(
1128 1247
1129 let block = match &handler { 1248 let block = match &handler {
1130 FlowHandler::None => block, 1249 FlowHandler::None => block,
1250 FlowHandler::Try { kind } => {
1251 let block = with_default_tail_expr(block, make::expr_unit());
1252 map_tail_expr(block, |tail_expr| {
1253 let constructor = match kind {
1254 TryKind::Option => "Some",
1255 TryKind::Result { .. } => "Ok",
1256 };
1257 let func = make::expr_path(make_path_from_text(constructor));
1258 let args = make::arg_list(iter::once(tail_expr));
1259 make::expr_call(func, args)
1260 })
1261 }
1131 FlowHandler::If { .. } => { 1262 FlowHandler::If { .. } => {
1132 let lit_false = ast::Literal::cast(make::tokens::literal("false").parent()).unwrap(); 1263 let lit_false = ast::Literal::cast(make::tokens::literal("false").parent()).unwrap();
1133 with_tail_expr(block, lit_false.into()) 1264 with_tail_expr(block, lit_false.into())
@@ -1142,9 +1273,9 @@ fn make_body(
1142 make::expr_call(some, args) 1273 make::expr_call(some, args)
1143 }), 1274 }),
1144 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { 1275 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| {
1145 let some = make::expr_path(make_path_from_text("Ok")); 1276 let ok = make::expr_path(make_path_from_text("Ok"));
1146 let args = make::arg_list(iter::once(tail_expr)); 1277 let args = make::arg_list(iter::once(tail_expr));
1147 make::expr_call(some, args) 1278 make::expr_call(ok, args)
1148 }), 1279 }),
1149 }; 1280 };
1150 1281
@@ -1159,6 +1290,13 @@ fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr)
1159 make::block_expr(block.statements(), Some(f(tail_expr))) 1290 make::block_expr(block.statements(), Some(f(tail_expr)))
1160} 1291}
1161 1292
1293fn with_default_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr {
1294 match block.tail_expr() {
1295 Some(_) => block,
1296 None => make::block_expr(block.statements(), Some(tail_expr)),
1297 }
1298}
1299
1162fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr { 1300fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr {
1163 let stmt_tail = block.tail_expr().map(|expr| make::expr_stmt(expr).into()); 1301 let stmt_tail = block.tail_expr().map(|expr| make::expr_stmt(expr).into());
1164 let stmts = block.statements().chain(stmt_tail); 1302 let stmts = block.statements().chain(stmt_tail);
@@ -1295,7 +1433,7 @@ fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> S
1295 1433
1296fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { 1434fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> {
1297 let value = match handler { 1435 let value = match handler {
1298 FlowHandler::None => return None, 1436 FlowHandler::None | FlowHandler::Try { .. } => return None,
1299 FlowHandler::If { .. } => { 1437 FlowHandler::If { .. } => {
1300 ast::Literal::cast(make::tokens::literal("true").parent()).unwrap().into() 1438 ast::Literal::cast(make::tokens::literal("true").parent()).unwrap().into()
1301 } 1439 }
@@ -3039,4 +3177,183 @@ fn $0fun_name() -> Result<i32, i64> {
3039}"##, 3177}"##,
3040 ); 3178 );
3041 } 3179 }
3180
3181 #[test]
3182 fn try_option() {
3183 check_assist(
3184 extract_function,
3185 r##"
3186enum Option<T> { None, Some(T), }
3187use Option::*;
3188fn bar() -> Option<i32> { None }
3189fn foo() -> Option<()> {
3190 let n = bar()?;
3191 $0let k = foo()?;
3192 let m = k + 1;$0
3193 let h = 1 + m;
3194 Some(())
3195}"##,
3196 r##"
3197enum Option<T> { None, Some(T), }
3198use Option::*;
3199fn bar() -> Option<i32> { None }
3200fn foo() -> Option<()> {
3201 let n = bar()?;
3202 let m = fun_name()?;
3203 let h = 1 + m;
3204 Some(())
3205}
3206
3207fn $0fun_name() -> Option<i32> {
3208 let k = foo()?;
3209 let m = k + 1;
3210 Some(m)
3211}"##,
3212 );
3213 }
3214
3215 #[test]
3216 fn try_option_unit() {
3217 check_assist(
3218 extract_function,
3219 r##"
3220enum Option<T> { None, Some(T), }
3221use Option::*;
3222fn foo() -> Option<()> {
3223 let n = 1;
3224 $0let k = foo()?;
3225 let m = k + 1;$0
3226 let h = 1 + n;
3227 Some(())
3228}"##,
3229 r##"
3230enum Option<T> { None, Some(T), }
3231use Option::*;
3232fn foo() -> Option<()> {
3233 let n = 1;
3234 fun_name()?;
3235 let h = 1 + n;
3236 Some(())
3237}
3238
3239fn $0fun_name() -> Option<()> {
3240 let k = foo()?;
3241 let m = k + 1;
3242 Some(())
3243}"##,
3244 );
3245 }
3246
3247 #[test]
3248 fn try_result() {
3249 check_assist(
3250 extract_function,
3251 r##"
3252enum Result<T, E> { Ok(T), Err(E), }
3253use Result::*;
3254fn foo() -> Result<(), i64> {
3255 let n = 1;
3256 $0let k = foo()?;
3257 let m = k + 1;$0
3258 let h = 1 + m;
3259 Ok(())
3260}"##,
3261 r##"
3262enum Result<T, E> { Ok(T), Err(E), }
3263use Result::*;
3264fn foo() -> Result<(), i64> {
3265 let n = 1;
3266 let m = fun_name()?;
3267 let h = 1 + m;
3268 Ok(())
3269}
3270
3271fn $0fun_name() -> Result<i32, i64> {
3272 let k = foo()?;
3273 let m = k + 1;
3274 Ok(m)
3275}"##,
3276 );
3277 }
3278
3279 #[test]
3280 fn try_result_with_return() {
3281 check_assist(
3282 extract_function,
3283 r##"
3284enum Result<T, E> { Ok(T), Err(E), }
3285use Result::*;
3286fn foo() -> Result<(), i64> {
3287 let n = 1;
3288 $0let k = foo()?;
3289 if k == 42 {
3290 return Err(1);
3291 }
3292 let m = k + 1;$0
3293 let h = 1 + m;
3294 Ok(())
3295}"##,
3296 r##"
3297enum Result<T, E> { Ok(T), Err(E), }
3298use Result::*;
3299fn foo() -> Result<(), i64> {
3300 let n = 1;
3301 let m = fun_name()?;
3302 let h = 1 + m;
3303 Ok(())
3304}
3305
3306fn $0fun_name() -> Result<i32, i64> {
3307 let k = foo()?;
3308 if k == 42 {
3309 return Err(1);
3310 }
3311 let m = k + 1;
3312 Ok(m)
3313}"##,
3314 );
3315 }
3316
3317 #[test]
3318 fn try_and_break() {
3319 mark::check!(external_control_flow_try_and_bc);
3320 check_assist_not_applicable(
3321 extract_function,
3322 r##"
3323enum Option<T> { None, Some(T) }
3324use Option::*;
3325fn foo() -> Option<()> {
3326 loop {
3327 let n = Some(1);
3328 $0let m = n? + 1;
3329 break;
3330 let k = 2;
3331 let k = k + 1;$0
3332 let r = n + k;
3333 }
3334 Some(())
3335}"##,
3336 );
3337 }
3338
3339 #[test]
3340 fn try_and_return_ok() {
3341 mark::check!(external_control_flow_try_and_return_non_err);
3342 check_assist_not_applicable(
3343 extract_function,
3344 r##"
3345enum Result<T, E> { Ok(T), Err(E), }
3346use Result::*;
3347fn foo() -> Result<(), i64> {
3348 let n = 1;
3349 $0let k = foo()?;
3350 if k == 42 {
3351 return Ok(1);
3352 }
3353 let m = k + 1;$0
3354 let h = 1 + m;
3355 Ok(())
3356}"##,
3357 );
3358 }
3042} 3359}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 5f6b96c23..5eee33545 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -200,6 +200,9 @@ pub fn expr_return(expr: Option<ast::Expr>) -> ast::Expr {
200 None => expr_from_text("return"), 200 None => expr_from_text("return"),
201 } 201 }
202} 202}
203pub fn expr_try(expr: ast::Expr) -> ast::Expr {
204 expr_from_text(&format!("{}?", expr))
205}
203pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr { 206pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
204 expr_from_text(&format!("match {} {}", expr, match_arm_list)) 207 expr_from_text(&format!("match {} {}", expr, match_arm_list))
205} 208}