@@ -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) ]
459489mod 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