aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-02-16 14:01:09 +0000
committerGitHub <[email protected]>2021-02-16 14:01:09 +0000
commit88e8b0a5fa17075475c75941932056f7289c5bcf (patch)
tree84889038ca64a9ee4775ecf5605ed0213b90a0b7 /crates/syntax
parent00d5a9563af9c84cdea9d029a9583be0513cc459 (diff)
parent37a8cec6386d57e76b3067af48c78867aa9922ed (diff)
Merge #7620
7620: Support control flow in `extract_function` assist r=matklad a=cpud36 Support `return`ing from outer function, `break`ing and `continue`ing outer loops when extracting function. # Example Transforms ```rust fn foo() -> i32 { let items = [1,2,3]; let mut sum = 0; for &item in items { <|>if item == 42 { break; }<|> sum += item; } sum } ``` Into ```rust fn foo() -> i32 { let items = [1,2,3]; let mut sum = 0; for &item in items { if fun_name(item) { break; } sum += item; } sum } fn fun_name(item: i32) -> bool { if item == 42 { return true; } false } ``` ![add_explicit_type_infer_type](https://user-images.githubusercontent.com/4218373/107544222-0fadf280-6bdb-11eb-9625-ed6194ba92c0.gif) # Features Supported variants - break and function does not return => uses `bool` and plain if - break and function does return => uses `Option<T>` and matches on it - break with value and function does not return => uses `Option<T>` and if let - break with value and function does return => uses `Result<T, U>` and matches on t - same for `return` and `continue`(but we can't continue with value) Assist does handle nested loops and nested items(like functions, modules, impls) Try `expr?` operator is allowed together with `return Err(_)` and `return None`. `return expr` is not allowed. # Not supported ## Mixing `return` with `break` or `continue` If we have e.g. a `return` and a `break` in the selected code, it is unclear what the produced code should look like. We can try `Result<T, Option<U>>` or something like that, but it isn't idiomatic, nor it is established. Otherwise, implementation is relatively simple. ## `break` with label Not sure how to handle different labels for multiple `break`s. [edit] implemented try `expr?` Co-authored-by: Vladyslav Katasonov <[email protected]>
Diffstat (limited to 'crates/syntax')
-rw-r--r--crates/syntax/src/ast/make.rs59
1 files changed, 50 insertions, 9 deletions
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 1da5a125e..5eee33545 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -24,11 +24,24 @@ pub fn name_ref(text: &str) -> ast::NameRef {
24// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la 24// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
25// `expr_xxx`. 25// `expr_xxx`.
26pub fn ty(text: &str) -> ast::Type { 26pub fn ty(text: &str) -> ast::Type {
27 ast_from_text(&format!("impl {} for D {{}};", text)) 27 ast_from_text(&format!("fn f() -> {} {{}}", text))
28} 28}
29pub fn ty_unit() -> ast::Type { 29pub fn ty_unit() -> ast::Type {
30 ty("()") 30 ty("()")
31} 31}
32// FIXME: handle types of length == 1
33pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
34 let contents = types.into_iter().join(", ");
35 ty(&format!("({})", contents))
36}
37// FIXME: handle path to type
38pub fn ty_generic(name: ast::NameRef, types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
39 let contents = types.into_iter().join(", ");
40 ty(&format!("{}<{}>", name, contents))
41}
42pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type {
43 ty(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) })
44}
32 45
33pub fn assoc_item_list() -> ast::AssocItemList { 46pub fn assoc_item_list() -> ast::AssocItemList {
34 ast_from_text("impl C for D {};") 47 ast_from_text("impl C for D {};")
@@ -175,11 +188,20 @@ pub fn expr_path(path: ast::Path) -> ast::Expr {
175pub fn expr_continue() -> ast::Expr { 188pub fn expr_continue() -> ast::Expr {
176 expr_from_text("continue") 189 expr_from_text("continue")
177} 190}
178pub fn expr_break() -> ast::Expr { 191pub fn expr_break(expr: Option<ast::Expr>) -> ast::Expr {
179 expr_from_text("break") 192 match expr {
193 Some(expr) => expr_from_text(&format!("break {}", expr)),
194 None => expr_from_text("break"),
195 }
180} 196}
181pub fn expr_return() -> ast::Expr { 197pub fn expr_return(expr: Option<ast::Expr>) -> ast::Expr {
182 expr_from_text("return") 198 match expr {
199 Some(expr) => expr_from_text(&format!("return {}", expr)),
200 None => expr_from_text("return"),
201 }
202}
203pub fn expr_try(expr: ast::Expr) -> ast::Expr {
204 expr_from_text(&format!("{}?", expr))
183} 205}
184pub 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 {
185 expr_from_text(&format!("match {} {}", expr, match_arm_list)) 207 expr_from_text(&format!("match {} {}", expr, match_arm_list))
@@ -212,6 +234,10 @@ pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
212pub fn expr_paren(expr: ast::Expr) -> ast::Expr { 234pub fn expr_paren(expr: ast::Expr) -> ast::Expr {
213 expr_from_text(&format!("({})", expr)) 235 expr_from_text(&format!("({})", expr))
214} 236}
237pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr {
238 let expr = elements.into_iter().format(", ");
239 expr_from_text(&format!("({})", expr))
240}
215fn expr_from_text(text: &str) -> ast::Expr { 241fn expr_from_text(text: &str) -> ast::Expr {
216 ast_from_text(&format!("const C: () = {};", text)) 242 ast_from_text(&format!("const C: () = {};", text))
217} 243}
@@ -236,6 +262,13 @@ pub fn ident_pat(name: ast::Name) -> ast::IdentPat {
236 ast_from_text(&format!("fn f({}: ())", text)) 262 ast_from_text(&format!("fn f({}: ())", text))
237 } 263 }
238} 264}
265pub fn ident_mut_pat(name: ast::Name) -> ast::IdentPat {
266 return from_text(name.text());
267
268 fn from_text(text: &str) -> ast::IdentPat {
269 ast_from_text(&format!("fn f(mut {}: ())", text))
270 }
271}
239 272
240pub fn wildcard_pat() -> ast::WildcardPat { 273pub fn wildcard_pat() -> ast::WildcardPat {
241 return from_text("_"); 274 return from_text("_");
@@ -356,17 +389,25 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken {
356 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) 389 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
357} 390}
358 391
359pub fn param(name: String, ty: String) -> ast::Param { 392pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
360 ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty)) 393 ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty))
361} 394}
362 395
363pub fn ret_type(ty: ast::Type) -> ast::RetType { 396pub fn ret_type(ty: ast::Type) -> ast::RetType {
364 ast_from_text(&format!("fn f() -> {} {{ }}", ty)) 397 ast_from_text(&format!("fn f() -> {} {{ }}", ty))
365} 398}
366 399
367pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList { 400pub fn param_list(
401 self_param: Option<ast::SelfParam>,
402 pats: impl IntoIterator<Item = ast::Param>,
403) -> ast::ParamList {
368 let args = pats.into_iter().join(", "); 404 let args = pats.into_iter().join(", ");
369 ast_from_text(&format!("fn f({}) {{ }}", args)) 405 let list = match self_param {
406 Some(self_param) if args.is_empty() => format!("fn f({}) {{ }}", self_param),
407 Some(self_param) => format!("fn f({}, {}) {{ }}", self_param, args),
408 None => format!("fn f({}) {{ }}", args),
409 };
410 ast_from_text(&list)
370} 411}
371 412
372pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { 413pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam {