diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-02-16 14:01:09 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-02-16 14:01:09 +0000 |
commit | 88e8b0a5fa17075475c75941932056f7289c5bcf (patch) | |
tree | 84889038ca64a9ee4775ecf5605ed0213b90a0b7 /crates/syntax/src/ast | |
parent | 00d5a9563af9c84cdea9d029a9583be0513cc459 (diff) | |
parent | 37a8cec6386d57e76b3067af48c78867aa9922ed (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/src/ast')
-rw-r--r-- | crates/syntax/src/ast/make.rs | 59 |
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`. |
26 | pub fn ty(text: &str) -> ast::Type { | 26 | pub 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 | } |
29 | pub fn ty_unit() -> ast::Type { | 29 | pub fn ty_unit() -> ast::Type { |
30 | ty("()") | 30 | ty("()") |
31 | } | 31 | } |
32 | // FIXME: handle types of length == 1 | ||
33 | pub 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 | ||
38 | pub 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 | } | ||
42 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { | ||
43 | ty(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) | ||
44 | } | ||
32 | 45 | ||
33 | pub fn assoc_item_list() -> ast::AssocItemList { | 46 | pub 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 { | |||
175 | pub fn expr_continue() -> ast::Expr { | 188 | pub fn expr_continue() -> ast::Expr { |
176 | expr_from_text("continue") | 189 | expr_from_text("continue") |
177 | } | 190 | } |
178 | pub fn expr_break() -> ast::Expr { | 191 | pub 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 | } |
181 | pub fn expr_return() -> ast::Expr { | 197 | pub 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 | } | ||
203 | pub fn expr_try(expr: ast::Expr) -> ast::Expr { | ||
204 | expr_from_text(&format!("{}?", expr)) | ||
183 | } | 205 | } |
184 | 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 { |
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 { | |||
212 | pub fn expr_paren(expr: ast::Expr) -> ast::Expr { | 234 | pub fn expr_paren(expr: ast::Expr) -> ast::Expr { |
213 | expr_from_text(&format!("({})", expr)) | 235 | expr_from_text(&format!("({})", expr)) |
214 | } | 236 | } |
237 | pub 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 | } | ||
215 | fn expr_from_text(text: &str) -> ast::Expr { | 241 | fn 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 | } |
265 | pub 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 | ||
240 | pub fn wildcard_pat() -> ast::WildcardPat { | 273 | pub 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 | ||
359 | pub fn param(name: String, ty: String) -> ast::Param { | 392 | pub 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 | ||
363 | pub fn ret_type(ty: ast::Type) -> ast::RetType { | 396 | pub 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 | ||
367 | pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList { | 400 | pub 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 | ||
372 | pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { | 413 | pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { |