diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/extract_function.rs | 377 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 3 |
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 | ||
120 | fn external_control_flow(body: &FunctionBody) -> Option<ControlFlow> { | 120 | fn 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` | ||
231 | fn 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)] |
213 | struct Function { | 249 | struct 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)] | ||
385 | enum TryKind { | ||
386 | Option, | ||
387 | Result { ty: hir::Type }, | ||
388 | } | ||
389 | |||
341 | impl FlowKind { | 390 | impl 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 | ||
420 | fn 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)] |
362 | enum RetType { | 437 | enum 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 | ||
878 | enum FlowHandler { | 953 | enum 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 | ||
1037 | fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Option<ast::RetType> { | 1120 | fn 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>` | ||
1165 | fn 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 | |||
1067 | fn make_body( | 1186 | fn 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 | ||
1293 | fn 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 | |||
1162 | fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr { | 1300 | fn 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 | ||
1296 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { | 1434 | fn 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##" | ||
3186 | enum Option<T> { None, Some(T), } | ||
3187 | use Option::*; | ||
3188 | fn bar() -> Option<i32> { None } | ||
3189 | fn 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##" | ||
3197 | enum Option<T> { None, Some(T), } | ||
3198 | use Option::*; | ||
3199 | fn bar() -> Option<i32> { None } | ||
3200 | fn foo() -> Option<()> { | ||
3201 | let n = bar()?; | ||
3202 | let m = fun_name()?; | ||
3203 | let h = 1 + m; | ||
3204 | Some(()) | ||
3205 | } | ||
3206 | |||
3207 | fn $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##" | ||
3220 | enum Option<T> { None, Some(T), } | ||
3221 | use Option::*; | ||
3222 | fn 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##" | ||
3230 | enum Option<T> { None, Some(T), } | ||
3231 | use Option::*; | ||
3232 | fn foo() -> Option<()> { | ||
3233 | let n = 1; | ||
3234 | fun_name()?; | ||
3235 | let h = 1 + n; | ||
3236 | Some(()) | ||
3237 | } | ||
3238 | |||
3239 | fn $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##" | ||
3252 | enum Result<T, E> { Ok(T), Err(E), } | ||
3253 | use Result::*; | ||
3254 | fn 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##" | ||
3262 | enum Result<T, E> { Ok(T), Err(E), } | ||
3263 | use Result::*; | ||
3264 | fn foo() -> Result<(), i64> { | ||
3265 | let n = 1; | ||
3266 | let m = fun_name()?; | ||
3267 | let h = 1 + m; | ||
3268 | Ok(()) | ||
3269 | } | ||
3270 | |||
3271 | fn $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##" | ||
3284 | enum Result<T, E> { Ok(T), Err(E), } | ||
3285 | use Result::*; | ||
3286 | fn 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##" | ||
3297 | enum Result<T, E> { Ok(T), Err(E), } | ||
3298 | use Result::*; | ||
3299 | fn foo() -> Result<(), i64> { | ||
3300 | let n = 1; | ||
3301 | let m = fun_name()?; | ||
3302 | let h = 1 + m; | ||
3303 | Ok(()) | ||
3304 | } | ||
3305 | |||
3306 | fn $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##" | ||
3323 | enum Option<T> { None, Some(T) } | ||
3324 | use Option::*; | ||
3325 | fn 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##" | ||
3345 | enum Result<T, E> { Ok(T), Err(E), } | ||
3346 | use Result::*; | ||
3347 | fn 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 | } |
203 | pub fn expr_try(expr: ast::Expr) -> ast::Expr { | ||
204 | expr_from_text(&format!("{}?", expr)) | ||
205 | } | ||
203 | pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr { | 206 | pub 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 | } |