Skip to content

Commit b7c4100

Browse files
committed
fix: offer on const like path-expr for 'extract_variable'
Example --- ```rust struct Foo; fn foo() -> Foo { $0Foo$0 } ``` **Before this PR** Assist not applicable **After this PR** ```rust struct Foo; fn foo() -> Foo { let foo = Foo; foo } ```
1 parent 6254616 commit b7c4100

1 file changed

Lines changed: 56 additions & 5 deletions

File tree

crates/ide-assists/src/handlers/extract_variable.rs

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
7575
.next()
7676
.and_then(ast::Expr::cast)
7777
{
78-
expr.syntax().ancestors().find_map(valid_target_expr)?.syntax().clone()
78+
expr.syntax().ancestors().find_map(valid_target_expr(ctx))?.syntax().clone()
7979
} else {
8080
return None;
8181
}
@@ -96,7 +96,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
9696
let to_extract = node
9797
.descendants()
9898
.take_while(|it| range.contains_range(it.text_range()))
99-
.find_map(valid_target_expr)?;
99+
.find_map(valid_target_expr(ctx))?;
100100

101101
let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted);
102102
if matches!(&ty, Some(ty_info) if ty_info.is_unit()) {
@@ -283,14 +283,19 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
283283

284284
/// Check whether the node is a valid expression which can be extracted to a variable.
285285
/// In general that's true for any expression, but in some cases that would produce invalid code.
286-
fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
287-
match node.kind() {
288-
SyntaxKind::PATH_EXPR | SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None,
286+
fn valid_target_expr(ctx: &AssistContext<'_>) -> impl Fn(SyntaxNode) -> Option<ast::Expr> {
287+
|node| match node.kind() {
288+
SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None,
289289
SyntaxKind::BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
290290
SyntaxKind::RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()),
291291
SyntaxKind::BLOCK_EXPR => {
292292
ast::BlockExpr::cast(node).filter(|it| it.is_standalone()).map(ast::Expr::from)
293293
}
294+
SyntaxKind::PATH_EXPR => {
295+
let path_expr = ast::PathExpr::cast(node)?;
296+
let path_resolution = ctx.sema.resolve_path(&path_expr.path()?)?;
297+
like_const_value(ctx, path_resolution).then_some(path_expr.into())
298+
}
294299
_ => ast::Expr::cast(node),
295300
}
296301
}
@@ -455,6 +460,31 @@ impl Anchor {
455460
}
456461
}
457462

463+
fn like_const_value(ctx: &AssistContext<'_>, path_resolution: hir::PathResolution) -> bool {
464+
let db = ctx.db();
465+
let adt_like_const_value = |adt: Option<hir::Adt>| matches!(adt, Some(hir::Adt::Struct(s)) if s.kind(db) == hir::StructKind::Unit);
466+
match path_resolution {
467+
hir::PathResolution::Def(def) => match def {
468+
hir::ModuleDef::Adt(adt) => adt_like_const_value(Some(adt)),
469+
hir::ModuleDef::Variant(variant) => variant.kind(db) == hir::StructKind::Unit,
470+
hir::ModuleDef::TypeAlias(ty) => adt_like_const_value(ty.ty(db).as_adt()),
471+
hir::ModuleDef::Const(_) | hir::ModuleDef::Static(_) => true,
472+
hir::ModuleDef::Trait(_)
473+
| hir::ModuleDef::BuiltinType(_)
474+
| hir::ModuleDef::Macro(_)
475+
| hir::ModuleDef::Module(_) => false,
476+
hir::ModuleDef::Function(_) => false, // no extract named function
477+
},
478+
hir::PathResolution::SelfType(ty) => adt_like_const_value(ty.self_ty(db).as_adt()),
479+
hir::PathResolution::ConstParam(_) => true,
480+
hir::PathResolution::Local(_)
481+
| hir::PathResolution::TypeParam(_)
482+
| hir::PathResolution::BuiltinAttr(_)
483+
| hir::PathResolution::ToolModule(_)
484+
| hir::PathResolution::DeriveHelper(_) => false,
485+
}
486+
}
487+
458488
#[cfg(test)]
459489
mod tests {
460490
// NOTE: We use check_assist_by_label, but not check_assist_not_applicable_by_label
@@ -1747,6 +1777,27 @@ fn main() {
17471777
);
17481778
}
17491779

1780+
#[test]
1781+
fn extract_non_local_path_expr() {
1782+
check_assist_by_label(
1783+
extract_variable,
1784+
r#"
1785+
struct Foo;
1786+
fn foo() -> Foo {
1787+
$0Foo$0
1788+
}
1789+
"#,
1790+
r#"
1791+
struct Foo;
1792+
fn foo() -> Foo {
1793+
let $0foo = Foo;
1794+
foo
1795+
}
1796+
"#,
1797+
"Extract into variable",
1798+
);
1799+
}
1800+
17501801
#[test]
17511802
fn extract_var_for_return_not_applicable() {
17521803
check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } ");

0 commit comments

Comments
 (0)