@@ -74,7 +74,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
7474 . next ( )
7575 . and_then ( ast:: Expr :: cast)
7676 {
77- expr. syntax ( ) . ancestors ( ) . find_map ( valid_target_expr) ?. syntax ( ) . clone ( )
77+ expr. syntax ( ) . ancestors ( ) . find_map ( valid_target_expr ( ctx ) ) ?. syntax ( ) . clone ( )
7878 } else {
7979 return None ;
8080 }
@@ -95,7 +95,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
9595 let to_extract = node
9696 . descendants ( )
9797 . take_while ( |it| range. contains_range ( it. text_range ( ) ) )
98- . find_map ( valid_target_expr) ?;
98+ . find_map ( valid_target_expr ( ctx ) ) ?;
9999
100100 let ty = ctx. sema . type_of_expr ( & to_extract) . map ( TypeInfo :: adjusted) ;
101101 if matches ! ( & ty, Some ( ty_info) if ty_info. is_unit( ) ) {
@@ -282,14 +282,19 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
282282
283283/// Check whether the node is a valid expression which can be extracted to a variable.
284284/// In general that's true for any expression, but in some cases that would produce invalid code.
285- fn valid_target_expr ( node : SyntaxNode ) -> Option < ast:: Expr > {
286- match node. kind ( ) {
287- SyntaxKind :: PATH_EXPR | SyntaxKind :: LOOP_EXPR | SyntaxKind :: LET_EXPR => None ,
285+ fn valid_target_expr ( ctx : & AssistContext < ' _ > ) -> impl Fn ( SyntaxNode ) -> Option < ast:: Expr > {
286+ |node| match node. kind ( ) {
287+ SyntaxKind :: LOOP_EXPR | SyntaxKind :: LET_EXPR => None ,
288288 SyntaxKind :: BREAK_EXPR => ast:: BreakExpr :: cast ( node) . and_then ( |e| e. expr ( ) ) ,
289289 SyntaxKind :: RETURN_EXPR => ast:: ReturnExpr :: cast ( node) . and_then ( |e| e. expr ( ) ) ,
290290 SyntaxKind :: BLOCK_EXPR => {
291291 ast:: BlockExpr :: cast ( node) . filter ( |it| it. is_standalone ( ) ) . map ( ast:: Expr :: from)
292292 }
293+ SyntaxKind :: PATH_EXPR => {
294+ let path_expr = ast:: PathExpr :: cast ( node) ?;
295+ let path_resolution = ctx. sema . resolve_path ( & path_expr. path ( ) ?) ?;
296+ like_const_value ( ctx, path_resolution) . then_some ( path_expr. into ( ) )
297+ }
293298 _ => ast:: Expr :: cast ( node) ,
294299 }
295300}
@@ -454,6 +459,31 @@ impl Anchor {
454459 }
455460}
456461
462+ fn like_const_value ( ctx : & AssistContext < ' _ > , path_resolution : hir:: PathResolution ) -> bool {
463+ let db = ctx. db ( ) ;
464+ let adt_like_const_value = |adt : Option < hir:: Adt > | matches ! ( adt, Some ( hir:: Adt :: Struct ( s) ) if s. kind( db) == hir:: StructKind :: Unit ) ;
465+ match path_resolution {
466+ hir:: PathResolution :: Def ( def) => match def {
467+ hir:: ModuleDef :: Adt ( adt) => adt_like_const_value ( Some ( adt) ) ,
468+ hir:: ModuleDef :: Variant ( variant) => variant. kind ( db) == hir:: StructKind :: Unit ,
469+ hir:: ModuleDef :: TypeAlias ( ty) => adt_like_const_value ( ty. ty ( db) . as_adt ( ) ) ,
470+ hir:: ModuleDef :: Const ( _) | hir:: ModuleDef :: Static ( _) => true ,
471+ hir:: ModuleDef :: Trait ( _)
472+ | hir:: ModuleDef :: BuiltinType ( _)
473+ | hir:: ModuleDef :: Macro ( _)
474+ | hir:: ModuleDef :: Module ( _) => false ,
475+ hir:: ModuleDef :: Function ( _) => false , // no extract named function
476+ } ,
477+ hir:: PathResolution :: SelfType ( ty) => adt_like_const_value ( ty. self_ty ( db) . as_adt ( ) ) ,
478+ hir:: PathResolution :: ConstParam ( _) => true ,
479+ hir:: PathResolution :: Local ( _)
480+ | hir:: PathResolution :: TypeParam ( _)
481+ | hir:: PathResolution :: BuiltinAttr ( _)
482+ | hir:: PathResolution :: ToolModule ( _)
483+ | hir:: PathResolution :: DeriveHelper ( _) => false ,
484+ }
485+ }
486+
457487#[ cfg( test) ]
458488mod tests {
459489 // NOTE: We use check_assist_by_label, but not check_assist_not_applicable_by_label
@@ -1746,6 +1776,27 @@ fn main() {
17461776 ) ;
17471777 }
17481778
1779+ #[ test]
1780+ fn extract_non_local_path_expr ( ) {
1781+ check_assist_by_label (
1782+ extract_variable,
1783+ r#"
1784+ struct Foo;
1785+ fn foo() -> Foo {
1786+ $0Foo$0
1787+ }
1788+ "# ,
1789+ r#"
1790+ struct Foo;
1791+ fn foo() -> Foo {
1792+ let $0foo = Foo;
1793+ foo
1794+ }
1795+ "# ,
1796+ "Extract into variable" ,
1797+ ) ;
1798+ }
1799+
17491800 #[ test]
17501801 fn extract_var_for_return_not_applicable ( ) {
17511802 check_assist_not_applicable ( extract_variable, "fn foo() { $0return$0; } " ) ;
0 commit comments