fix: transform return into break when inlining functions#21908
fix: transform return into break when inlining functions#21908Albab-Hasan wants to merge 7 commits intorust-lang:masterfrom
return into break when inlining functions#21908Conversation
|
I have a few concerns with this PR:
|
|
@ChayimFriedman2 Thanks for the review. agree the syntactic approach is likely incomplete and not the best way to go. walking im thinking of adding a method on for macro-generated returns specifically: we can detect them (the |
|
Using the HIR is exactly what I meant. As for macros, we can upmap the |
|
@ChayimFriedman2 done |
crates/hir/src/lib.rs
Outdated
|
|
||
| /// Returns the return expressions in this function's body that belong to the | ||
| /// function itself (not inside closures or async blocks which create their own | ||
| /// return scope). Uses HIR analysis, which handles returns inside macro expansions. |
There was a problem hiding this comment.
| /// return scope). Uses HIR analysis, which handles returns inside macro expansions. | |
| /// return scope). |
No need to say it's using HIR analysis, it's obvious for any function in this file.
crates/hir/src/lib.rs
Outdated
| }; | ||
| let (body, source_map) = Body::with_source_map(db, func_id.into()); | ||
| let mut returns = vec![]; | ||
| let mut to_visit = vec![body.root_expr()]; |
There was a problem hiding this comment.
A recursion is simpler IMO than an out-of-line traversal.
| vec![] | ||
| } else if file_id.is_macro() { | ||
| // Function defined in a macro — the body was prettified so HIR source map ranges | ||
| // don't match. Walk the prettified body syntactically instead. |
There was a problem hiding this comment.
What? No. You can use return_points() on a function defined in a macro just fine.
| body.syntax() | ||
| .descendants() | ||
| .filter_map(ast::ReturnExpr::cast) | ||
| .find(|r| r.syntax().text_range() == range) |
There was a problem hiding this comment.
Inside a (function-like) macro we don't have ReturnExpr, we have a TokenTree. That's why I deliberately suggested mapping the return keyword, and collecting Vec<TextRange>.
| // Transform return expressions into break expressions with a labeled block. | ||
| // This is needed because `return` in the inlined body would otherwise exit | ||
| // the *caller* function, not just the inlined block. | ||
| if !returns_to_replace.is_empty() { |
There was a problem hiding this comment.
And then here you can just do a textual edit of the upmapped return with break 'label.
|
@ChayimFriedman2 done. sorry for the delay, let me know if anything needs to be changed |
crates/hir/src/lib.rs
Outdated
|
|
||
| /// Returns the text ranges of `return` keywords in this function's body, | ||
| /// excluding those inside closures or async blocks. | ||
| pub fn return_points(self, db: &dyn HirDatabase) -> Vec<TextRange> { |
There was a problem hiding this comment.
This function should still IMO return Vec<ast::ReturnExpr>, without upmapping them, as a general-purpose API (that maybe will be useful elsewhere as well). Do the upmapping in this assist.
| /// Replaces `return` / `return expr` with `break 'inline` / `break 'inline expr` | ||
| /// for all `ReturnExpr` nodes that belong directly to the function body | ||
| /// (i.e., not nested inside closures or async blocks). | ||
| fn replace_returns_with_breaks(body: &ast::BlockExpr) { |
There was a problem hiding this comment.
This doesn't use return_points().
There was a problem hiding this comment.
replace_returns_with_breaks operates on the cloned body (a different syntax tree) so the ReturnExpr nodes from return_points() dont correspond to it. how do you want to use return_points()?
There was a problem hiding this comment.
If you map the returns on the original body, the ranges will stay the same. Also as I said use text replacement.
crates/hir/src/lib.rs
Outdated
|
|
||
| /// Returns the `return` expressions in this function's body, | ||
| /// excluding those inside closures or async blocks. | ||
| pub fn return_points(self, db: &dyn HirDatabase) -> Vec<ast::ReturnExpr> { |
There was a problem hiding this comment.
Probably better to return Vec<InFile<ast::ReturnExpr>>, even if this is not needed here.
|
@ChayimFriedman2 done. can u pls check it out |
|
The current revision is not what I had in mind still. But I realized that what I had in mind is very difficult because the various edits we do with the cloned body make it difficult to keep track of the ranges. This is actually a perfect place to use I also stole |
|
@ChayimFriedman2 thanks, ive moved for the also really cool that you picked up |
|
@ChayimFriedman2 can i get a approval on weather i should work with this approach or not? |
|
Indeed, inline_call is one of the more complicated assists to migrate. And it should definitely be done in a separate PR. If you want to work on migrating it, that's of course fine. If you do not, that is also fine, but I think I'll close this PR as unactionable for now. We can return to it after the assist is migrated. |
|
@ChayimFriedman2 that sounds great. ill start working on the migration. also i would appreciate it if u could check out this pr of mine. its been out for a while and might face merged conflicts soon. |
when inlining a function containing
returnexpressions thereturnwould incorrectly exit the caller rather than the inlined block. wrap the inlined body in a labeled block ('inline: { ... }) and rewritereturn <expr>tobreak 'inline <expr>.returns inside closures, async, const, and gen blocks are not changed because they have their own return scope.
Closes #19377