Skip to main content

ide_assists/handlers/
extract_variable.rs

1use hir::{HirDisplay, TypeInfo};
2use ide_db::{
3    assists::GroupLabel,
4    syntax_helpers::{LexedStr, suggest_name},
5};
6use syntax::{
7    Direction, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, T, TextRange,
8    algo::{ancestors_at_offset, skip_trivia_token},
9    ast::{
10        self, AstNode,
11        edit::{AstNodeEdit, IndentLevel},
12    },
13    syntax_editor::{Element, Position},
14};
15
16use crate::{AssistContext, AssistId, Assists, utils::is_body_const};
17
18// Assist: extract_variable
19//
20// Extracts subexpression into a variable.
21//
22// ```
23// fn main() {
24//     $0(1 + 2)$0 * 4;
25// }
26// ```
27// ->
28// ```
29// fn main() {
30//     let $0var_name = 1 + 2;
31//     var_name * 4;
32// }
33// ```
34
35// Assist: extract_constant
36//
37// Extracts subexpression into a constant.
38//
39// ```
40// fn main() {
41//     $0(1 + 2)$0 * 4;
42// }
43// ```
44// ->
45// ```
46// fn main() {
47//     const $0VAR_NAME: i32 = 1 + 2;
48//     VAR_NAME * 4;
49// }
50// ```
51
52// Assist: extract_static
53//
54// Extracts subexpression into a static.
55//
56// ```
57// fn main() {
58//     $0(1 + 2)$0 * 4;
59// }
60// ```
61// ->
62// ```
63// fn main() {
64//     static $0VAR_NAME: i32 = 1 + 2;
65//     VAR_NAME * 4;
66// }
67// ```
68pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
69    let node = if ctx.has_empty_selection() {
70        if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) {
71            t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone()
72        } else if let Some(expr) = ancestors_at_offset(ctx.source_file().syntax(), ctx.offset())
73            .next()
74            .and_then(ast::Expr::cast)
75        {
76            expr.syntax().ancestors().find_map(valid_target_expr(ctx))?.syntax().clone()
77        } else {
78            return None;
79        }
80    } else {
81        match ctx.covering_element() {
82            NodeOrToken::Node(it) => it,
83            NodeOrToken::Token(it) if it.kind() == SyntaxKind::COMMENT => {
84                cov_mark::hit!(extract_var_in_comment_is_not_applicable);
85                return None;
86            }
87            NodeOrToken::Token(it) => it.parent()?,
88        }
89    };
90
91    let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?;
92    let range = node.text_range();
93
94    let (to_replace, analysis) = if node.kind() == SyntaxKind::TOKEN_TREE {
95        let (first, last) = extract_token_range_of(&node, ctx.selection_trimmed())?;
96
97        let first_descend = ctx.sema.descend_into_macros_single_exact(first.clone());
98        let last_descend = ctx.sema.descend_into_macros_single_exact(last.clone());
99        let range = first_descend.text_range().cover(last_descend.text_range());
100
101        if first_descend.parent_ancestors().last() != last_descend.parent_ancestors().last() {
102            return None;
103        }
104
105        let expr = first_descend
106            .parent_ancestors()
107            .skip_while(|it| !it.text_range().contains_range(range))
108            .find_map(valid_target_expr(ctx))?;
109        let original_range = ctx.sema.original_range(expr.syntax());
110        let (first, last) = extract_token_range_of(&node, original_range.range)?;
111        let to_extract = first.syntax_element()..=last.syntax_element();
112        (to_extract, expr)
113    } else {
114        let expr = node
115            .descendants()
116            .take_while(|it| range.contains_range(it.text_range()))
117            .find_map(valid_target_expr(ctx))?;
118        let to_extract = expr.syntax().syntax_element();
119        (to_extract.clone()..=to_extract, expr)
120    };
121    let place = match to_replace.start() {
122        NodeOrToken::Node(node) => node.clone(),
123        NodeOrToken::Token(t) => t.parent()?,
124    };
125
126    let ty = ctx.sema.type_of_expr(&analysis).map(TypeInfo::adjusted);
127    if matches!(&ty, Some(ty_info) if ty_info.is_unit()) {
128        return None;
129    }
130
131    let parent = analysis.syntax().parent().and_then(ast::Expr::cast);
132    // Any expression that autoderefs may need adjustment.
133    let mut needs_adjust = parent.as_ref().is_some_and(|it| match it {
134        ast::Expr::FieldExpr(_)
135        | ast::Expr::MethodCallExpr(_)
136        | ast::Expr::CallExpr(_)
137        | ast::Expr::AwaitExpr(_) => true,
138        ast::Expr::IndexExpr(index) if index.base().as_ref() == Some(&analysis) => true,
139        _ => false,
140    });
141    let mut to_extract_no_ref = peel_parens(analysis.clone());
142    let needs_ref = needs_adjust
143        && match &to_extract_no_ref {
144            ast::Expr::FieldExpr(_)
145            | ast::Expr::IndexExpr(_)
146            | ast::Expr::MacroExpr(_)
147            | ast::Expr::ParenExpr(_)
148            | ast::Expr::PathExpr(_) => true,
149            ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::UnaryOp::Deref) => {
150                to_extract_no_ref = prefix.expr()?;
151                needs_adjust = false;
152                false
153            }
154            _ => false,
155        };
156    let module = ctx.sema.scope(analysis.syntax())?.module();
157    let target = to_replace.start().text_range().cover(to_replace.end().text_range());
158    let needs_mut = match &parent {
159        Some(ast::Expr::RefExpr(expr)) => expr.mut_token().is_some(),
160        _ => needs_adjust && !needs_ref && ty.as_ref().is_some_and(|ty| ty.is_mutable_reference()),
161    };
162    for kind in ExtractionKind::ALL {
163        let Some(anchor) = Anchor::from(&place, kind) else {
164            continue;
165        };
166
167        let ty_string = match kind {
168            ExtractionKind::Constant | ExtractionKind::Static => {
169                let Some(ty) = ty.clone() else {
170                    continue;
171                };
172
173                // We can't mutably reference a const, nor can we define
174                // one using a non-const expression or one of unknown type
175                if needs_mut
176                    || !is_body_const(&ctx.sema, &to_extract_no_ref)
177                    || ty.is_unknown()
178                    || ty.is_mutable_reference()
179                {
180                    continue;
181                }
182
183                let Ok(type_string) = ty.display_source_code(ctx.db(), module.into(), false) else {
184                    continue;
185                };
186
187                type_string
188            }
189            _ => "".to_owned(),
190        };
191
192        acc.add_group(
193            &GroupLabel("Extract into...".to_owned()),
194            kind.assist_id(),
195            kind.label(),
196            target,
197            |edit| {
198                let (var_name, expr_replace) = kind.get_name_and_expr(ctx, &analysis);
199
200                let to_replace =
201                    if expr_replace.ancestors().last() == to_replace.start().ancestors().last() {
202                        let element = expr_replace.clone().syntax_element();
203                        element.clone()..=element
204                    } else {
205                        to_replace.clone()
206                    };
207
208                let editor = edit.make_editor(&place);
209                let make = editor.make();
210
211                let pat_name = make.name(&var_name);
212                let name_expr = make.expr_path(make.ident_path(&var_name));
213
214                if let Some(cap) = ctx.config.snippet_cap {
215                    let tabstop = edit.make_tabstop_before(cap);
216                    editor.add_annotation(pat_name.syntax().clone(), tabstop);
217                }
218
219                let initializer = match ty.as_ref().filter(|_| needs_ref) {
220                    Some(receiver_type) if receiver_type.is_mutable_reference() => {
221                        make.expr_ref(to_extract_no_ref.clone(), true)
222                    }
223                    Some(receiver_type) if receiver_type.is_reference() => {
224                        make.expr_ref(to_extract_no_ref.clone(), false)
225                    }
226                    _ => to_extract_no_ref.clone(),
227                };
228
229                let new_stmt: ast::Stmt = match kind {
230                    ExtractionKind::Variable => {
231                        let ident_pat = make.ident_pat(false, needs_mut, pat_name);
232                        make.let_stmt(ident_pat.into(), None, Some(initializer)).into()
233                    }
234                    ExtractionKind::Constant => {
235                        let ast_ty = make.ty(&ty_string);
236                        ast::Item::Const(make.item_const(None, None, pat_name, ast_ty, initializer))
237                            .into()
238                    }
239                    ExtractionKind::Static => {
240                        let ast_ty = make.ty(&ty_string);
241                        ast::Item::Static(make.item_static(
242                            None,
243                            false,
244                            false,
245                            pat_name,
246                            ast_ty,
247                            Some(initializer),
248                        ))
249                        .into()
250                    }
251                };
252
253                match &anchor {
254                    Anchor::Before(place) => {
255                        let prev_ws = place.prev_sibling_or_token().and_then(|it| it.into_token());
256                        let indent_to = IndentLevel::from_node(place);
257
258                        // Adjust ws to insert depending on if this is all inline or on separate lines
259                        let trailing_ws = if prev_ws.is_some_and(|it| it.text().starts_with('\n')) {
260                            format!("\n{indent_to}")
261                        } else {
262                            " ".to_owned()
263                        };
264
265                        editor.insert_all(
266                            Position::before(place),
267                            vec![
268                                new_stmt.syntax().clone().into(),
269                                make.whitespace(&trailing_ws).into(),
270                            ],
271                        );
272
273                        editor.replace_all(to_replace, vec![name_expr.syntax().syntax_element()]);
274                    }
275                    Anchor::Replace(stmt) => {
276                        cov_mark::hit!(test_extract_var_expr_stmt);
277
278                        editor.replace(stmt.syntax(), new_stmt.syntax());
279                    }
280                    Anchor::WrapInBlock(to_wrap) => {
281                        let indent_to = to_wrap.indent_level();
282
283                        let block = if to_wrap.syntax() == &expr_replace {
284                            // Since `expr_replace` is the same that needs to be wrapped in a block,
285                            // we can just directly replace it with a block
286                            make.block_expr([new_stmt], Some(name_expr))
287                        } else {
288                            // `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`.
289                            editor
290                                .replace_all(to_replace, vec![name_expr.syntax().syntax_element()]);
291                            make.block_expr([new_stmt], Some(to_wrap.clone()))
292                        }
293                        // fixup indentation of block
294                        .indent_with_mapping(indent_to, make);
295
296                        editor.replace(to_wrap.syntax(), block.syntax());
297                    }
298                }
299                edit.add_file_edits(ctx.vfs_file_id(), editor);
300                edit.rename();
301            },
302        );
303    }
304
305    Some(())
306}
307
308fn extract_token_range_of(
309    node: &SyntaxNode,
310    range: TextRange,
311) -> Option<(SyntaxToken, SyntaxToken)> {
312    let first = node.token_at_offset(range.start()).right_biased()?;
313    let last = node.token_at_offset(range.end()).left_biased()?;
314
315    let first = skip_trivia_token(first, Direction::Next)?;
316    let last = skip_trivia_token(last, Direction::Next)?;
317
318    if first.text_range().ordering(last.text_range()).is_gt() {
319        return None;
320    }
321
322    Some((first, last))
323}
324
325fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
326    while let ast::Expr::ParenExpr(parens) = &expr {
327        let Some(expr_inside) = parens.expr() else { break };
328        expr = expr_inside;
329    }
330    expr
331}
332
333/// Check whether the node is a valid expression which can be extracted to a variable.
334/// In general that's true for any expression, but in some cases that would produce invalid code.
335fn valid_target_expr(ctx: &AssistContext<'_, '_>) -> impl Fn(SyntaxNode) -> Option<ast::Expr> {
336    let selection = ctx.selection_trimmed();
337    move |node| match node.kind() {
338        SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None,
339        SyntaxKind::BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
340        SyntaxKind::RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()),
341        SyntaxKind::BLOCK_EXPR => {
342            ast::BlockExpr::cast(node).filter(|it| it.is_standalone()).map(ast::Expr::from)
343        }
344        SyntaxKind::ARG_LIST => ast::ArgList::cast(node)?
345            .args()
346            .find(|expr| crate::utils::is_selected(expr, selection, false)),
347        SyntaxKind::PATH_EXPR => {
348            let path_expr = ast::PathExpr::cast(node)?;
349            let path_resolution = ctx.sema.resolve_path(&path_expr.path()?)?;
350            like_const_value(ctx, path_resolution).then_some(path_expr.into())
351        }
352        _ => ast::Expr::cast(node),
353    }
354}
355
356enum ExtractionKind {
357    Variable,
358    Constant,
359    Static,
360}
361
362impl ExtractionKind {
363    const ALL: &'static [ExtractionKind] =
364        &[ExtractionKind::Variable, ExtractionKind::Constant, ExtractionKind::Static];
365
366    fn assist_id(&self) -> AssistId {
367        let s = match self {
368            ExtractionKind::Variable => "extract_variable",
369            ExtractionKind::Constant => "extract_constant",
370            ExtractionKind::Static => "extract_static",
371        };
372
373        AssistId::refactor_extract(s)
374    }
375
376    fn label(&self) -> &'static str {
377        match self {
378            ExtractionKind::Variable => "Extract into variable",
379            ExtractionKind::Constant => "Extract into constant",
380            ExtractionKind::Static => "Extract into static",
381        }
382    }
383
384    fn get_name_and_expr(
385        &self,
386        ctx: &AssistContext<'_, '_>,
387        to_extract: &ast::Expr,
388    ) -> (String, SyntaxNode) {
389        // We only do this sort of extraction for fields because they should have lowercase names
390        if let ExtractionKind::Variable = self {
391            let field_shorthand = to_extract
392                .syntax()
393                .parent()
394                .and_then(ast::RecordExprField::cast)
395                .filter(|field| field.name_ref().is_some());
396
397            if let Some(field) = field_shorthand {
398                return (field.to_string(), field.syntax().clone());
399            }
400        }
401
402        let mut name_generator =
403            suggest_name::NameGenerator::new_from_scope_locals(ctx.sema.scope(to_extract.syntax()));
404        let var_name = if let Some(literal_name) = get_literal_name(ctx, to_extract) {
405            name_generator.suggest_name(&literal_name)
406        } else {
407            name_generator.for_variable(to_extract, &ctx.sema)
408        };
409
410        let var_name = match self {
411            ExtractionKind::Variable => var_name.to_lowercase(),
412            ExtractionKind::Constant | ExtractionKind::Static => var_name.to_uppercase(),
413        };
414
415        (var_name, to_extract.syntax().clone())
416    }
417}
418
419fn get_literal_name(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option<String> {
420    let ast::Expr::Literal(literal) = expr else {
421        return None;
422    };
423
424    let inner = match literal.kind() {
425        ast::LiteralKind::String(string) => string.value().ok()?.into_owned(),
426        ast::LiteralKind::ByteString(byte_string) => {
427            String::from_utf8(byte_string.value().ok()?.into_owned()).ok()?
428        }
429        ast::LiteralKind::CString(cstring) => {
430            String::from_utf8(cstring.value().ok()?.into_owned()).ok()?
431        }
432        _ => return None,
433    };
434
435    // Entirely arbitrary
436    if inner.len() > 32 {
437        return None;
438    }
439
440    match LexedStr::single_token(ctx.edition(), &inner) {
441        Some((SyntaxKind::IDENT, None)) => Some(inner),
442        _ => None,
443    }
444}
445
446#[derive(Debug, Clone)]
447enum Anchor {
448    Before(SyntaxNode),
449    Replace(ast::ExprStmt),
450    WrapInBlock(ast::Expr),
451}
452
453impl Anchor {
454    fn from(place: &SyntaxNode, kind: &ExtractionKind) -> Option<Anchor> {
455        let result = place
456            .ancestors()
457            .take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind()))
458            .find_map(|node| {
459                if ast::MacroCall::can_cast(node.kind()) {
460                    return None;
461                }
462                if let Some(expr) =
463                    node.parent().and_then(ast::StmtList::cast).and_then(|it| it.tail_expr())
464                    && expr.syntax() == &node
465                {
466                    cov_mark::hit!(test_extract_var_last_expr);
467                    return Some(Anchor::Before(node));
468                }
469
470                if let Some(parent) = node.parent() {
471                    if let Some(parent) = ast::ClosureExpr::cast(parent.clone()) {
472                        cov_mark::hit!(test_extract_var_in_closure_no_block);
473                        return parent.body().map(Anchor::WrapInBlock);
474                    }
475                    if let Some(parent) = ast::MatchArm::cast(parent) {
476                        if node.kind() == SyntaxKind::MATCH_GUARD {
477                            cov_mark::hit!(test_extract_var_in_match_guard);
478                        } else {
479                            cov_mark::hit!(test_extract_var_in_match_arm_no_block);
480                            return parent.expr().map(Anchor::WrapInBlock);
481                        }
482                    }
483                }
484
485                if let Some(stmt) = ast::Stmt::cast(node.clone()) {
486                    if let ast::Stmt::ExprStmt(stmt) = stmt
487                        && stmt.expr().is_some_and(|it| it.syntax() == place)
488                    {
489                        return Some(Anchor::Replace(stmt));
490                    }
491                    return Some(Anchor::Before(node));
492                }
493                None
494            });
495
496        match kind {
497            ExtractionKind::Constant | ExtractionKind::Static if result.is_none() => {
498                place.ancestors().find_map(|node| {
499                    let item = ast::Item::cast(node.clone())?;
500                    let parent = item.syntax().parent()?;
501                    match parent.kind() {
502                        SyntaxKind::ITEM_LIST
503                        | SyntaxKind::SOURCE_FILE
504                        | SyntaxKind::ASSOC_ITEM_LIST
505                        | SyntaxKind::STMT_LIST => Some(Anchor::Before(node)),
506                        _ => None,
507                    }
508                })
509            }
510            _ => result,
511        }
512    }
513}
514
515fn like_const_value(ctx: &AssistContext<'_, '_>, path_resolution: hir::PathResolution) -> bool {
516    let db = ctx.db();
517    let adt_like_const_value = |adt: Option<hir::Adt>| matches!(adt, Some(hir::Adt::Struct(s)) if s.kind(db) == hir::StructKind::Unit);
518    match path_resolution {
519        hir::PathResolution::Def(def) => match def {
520            hir::ModuleDef::Adt(adt) => adt_like_const_value(Some(adt)),
521            hir::ModuleDef::EnumVariant(variant) => variant.kind(db) == hir::StructKind::Unit,
522            hir::ModuleDef::TypeAlias(ty) => adt_like_const_value(ty.ty(db).as_adt()),
523            hir::ModuleDef::Const(_) | hir::ModuleDef::Static(_) => true,
524            hir::ModuleDef::Trait(_)
525            | hir::ModuleDef::BuiltinType(_)
526            | hir::ModuleDef::Macro(_)
527            | hir::ModuleDef::Module(_) => false,
528            hir::ModuleDef::Function(_) => false, // no extract named function
529        },
530        hir::PathResolution::SelfType(ty) => adt_like_const_value(ty.self_ty(db).as_adt()),
531        hir::PathResolution::ConstParam(_) => true,
532        hir::PathResolution::Local(_)
533        | hir::PathResolution::TypeParam(_)
534        | hir::PathResolution::BuiltinAttr(_)
535        | hir::PathResolution::ToolModule(_)
536        | hir::PathResolution::DeriveHelper(_) => false,
537    }
538}
539
540#[cfg(test)]
541mod tests {
542    // NOTE: We use check_assist_by_label, but not check_assist_not_applicable_by_label
543    // because all of our not-applicable tests should behave that way for both assists
544    // extract_variable offers, and check_assist_not_applicable ensures neither is offered
545    use crate::tests::{
546        check_assist_by_label, check_assist_not_applicable, check_assist_not_applicable_by_label,
547        check_assist_target,
548    };
549
550    use super::*;
551
552    #[test]
553    fn extract_var_simple_without_select() {
554        check_assist_by_label(
555            extract_variable,
556            r#"
557fn main() -> i32 {
558    if$0 true {
559        1
560    } else {
561        2
562    }
563}
564"#,
565            r#"
566fn main() -> i32 {
567    let $0var_name = if true {
568        1
569    } else {
570        2
571    };
572    var_name
573}
574"#,
575            "Extract into variable",
576        );
577
578        check_assist_by_label(
579            extract_variable,
580            r#"
581fn foo() -> i32 { 1 }
582fn main() {
583    foo();$0
584}
585"#,
586            r#"
587fn foo() -> i32 { 1 }
588fn main() {
589    let $0foo = foo();
590}
591"#,
592            "Extract into variable",
593        );
594
595        check_assist_by_label(
596            extract_variable,
597            r#"
598fn main() {
599    let a = Some(2);
600    a.is_some();$0
601}
602"#,
603            r#"
604fn main() {
605    let a = Some(2);
606    let $0is_some = a.is_some();
607}
608"#,
609            "Extract into variable",
610        );
611
612        check_assist_by_label(
613            extract_variable,
614            r#"
615fn main() {
616    "hello"$0;
617}
618"#,
619            r#"
620fn main() {
621    let $0hello = "hello";
622}
623"#,
624            "Extract into variable",
625        );
626
627        check_assist_by_label(
628            extract_variable,
629            r#"
630fn main() {
631    1  + 2$0;
632}
633"#,
634            r#"
635fn main() {
636    let $0var_name = 1  + 2;
637}
638"#,
639            "Extract into variable",
640        );
641
642        check_assist_by_label(
643            extract_variable,
644            r#"
645fn main() {
646    match () {
647        () if true => 1,
648        _ => 2,
649    };$0
650}
651"#,
652            r#"
653fn main() {
654    let $0var_name = match () {
655        () if true => 1,
656        _ => 2,
657    };
658}
659"#,
660            "Extract into variable",
661        );
662    }
663
664    #[test]
665    fn extract_const_simple_without_select() {
666        check_assist_by_label(
667            extract_variable,
668            r#"
669fn main() -> i32 {
670    if$0 true {
671        1
672    } else {
673        2
674    }
675}
676"#,
677            r#"
678fn main() -> i32 {
679    const $0VAR_NAME: i32 = if true {
680        1
681    } else {
682        2
683    };
684    VAR_NAME
685}
686"#,
687            "Extract into constant",
688        );
689
690        check_assist_by_label(
691            extract_variable,
692            r#"
693const fn foo() -> i32 { 1 }
694fn main() {
695    foo();$0
696}
697"#,
698            r#"
699const fn foo() -> i32 { 1 }
700fn main() {
701    const $0FOO: i32 = foo();
702}
703"#,
704            "Extract into constant",
705        );
706
707        check_assist_by_label(
708            extract_variable,
709            r#"
710fn main() {
711    "hello"$0;
712}
713"#,
714            r#"
715fn main() {
716    const $0HELLO: &'static str = "hello";
717}
718"#,
719            "Extract into constant",
720        );
721
722        check_assist_by_label(
723            extract_variable,
724            r#"
725fn main() {
726    1  + 2$0;
727}
728"#,
729            r#"
730fn main() {
731    const $0VAR_NAME: i32 = 1  + 2;
732}
733"#,
734            "Extract into constant",
735        );
736
737        check_assist_by_label(
738            extract_variable,
739            r#"
740fn main() {
741    match () {
742        () if true => 1,
743        _ => 2,
744    };$0
745}
746"#,
747            r#"
748fn main() {
749    const $0VAR_NAME: i32 = match () {
750        () if true => 1,
751        _ => 2,
752    };
753}
754"#,
755            "Extract into constant",
756        );
757    }
758
759    #[test]
760    fn extract_static_simple_without_select() {
761        check_assist_by_label(
762            extract_variable,
763            r#"
764fn main() -> i32 {
765    if$0 true {
766        1
767    } else {
768        2
769    }
770}
771"#,
772            r#"
773fn main() -> i32 {
774    static $0VAR_NAME: i32 = if true {
775        1
776    } else {
777        2
778    };
779    VAR_NAME
780}
781"#,
782            "Extract into static",
783        );
784
785        check_assist_by_label(
786            extract_variable,
787            r#"
788const fn foo() -> i32 { 1 }
789fn main() {
790    foo();$0
791}
792"#,
793            r#"
794const fn foo() -> i32 { 1 }
795fn main() {
796    static $0FOO: i32 = foo();
797}
798"#,
799            "Extract into static",
800        );
801
802        check_assist_by_label(
803            extract_variable,
804            r#"
805fn main() {
806    "hello"$0;
807}
808"#,
809            r#"
810fn main() {
811    static $0HELLO: &'static str = "hello";
812}
813"#,
814            "Extract into static",
815        );
816
817        check_assist_by_label(
818            extract_variable,
819            r#"
820fn main() {
821    1  + 2$0;
822}
823"#,
824            r#"
825fn main() {
826    static $0VAR_NAME: i32 = 1  + 2;
827}
828"#,
829            "Extract into static",
830        );
831
832        check_assist_by_label(
833            extract_variable,
834            r#"
835fn main() {
836    match () {
837        () if true => 1,
838        _ => 2,
839    };$0
840}
841"#,
842            r#"
843fn main() {
844    static $0VAR_NAME: i32 = match () {
845        () if true => 1,
846        _ => 2,
847    };
848}
849"#,
850            "Extract into static",
851        );
852    }
853
854    #[test]
855    fn dont_extract_unit_expr_without_select() {
856        check_assist_not_applicable(
857            extract_variable,
858            r#"
859fn foo() {}
860fn main() {
861    foo()$0;
862}
863"#,
864        );
865
866        check_assist_not_applicable(
867            extract_variable,
868            r#"
869fn foo() {
870    let mut i = 3;
871    if i >= 0 {
872        i += 1;
873    } else {
874        i -= 1;
875    }$0
876}"#,
877        );
878    }
879
880    #[test]
881    fn extract_var_simple() {
882        check_assist_by_label(
883            extract_variable,
884            r#"
885fn foo() {
886    foo($01 + 1$0);
887}"#,
888            r#"
889fn foo() {
890    let $0var_name = 1 + 1;
891    foo(var_name);
892}"#,
893            "Extract into variable",
894        );
895    }
896
897    #[test]
898    fn extract_const_simple() {
899        check_assist_by_label(
900            extract_variable,
901            r#"
902fn foo() {
903    foo($01 + 1$0);
904}"#,
905            r#"
906fn foo() {
907    const $0VAR_NAME: i32 = 1 + 1;
908    foo(VAR_NAME);
909}"#,
910            "Extract into constant",
911        );
912    }
913
914    #[test]
915    fn extract_static_simple() {
916        check_assist_by_label(
917            extract_variable,
918            r#"
919fn foo() {
920    foo($01 + 1$0);
921}"#,
922            r#"
923fn foo() {
924    static $0VAR_NAME: i32 = 1 + 1;
925    foo(VAR_NAME);
926}"#,
927            "Extract into static",
928        );
929    }
930
931    #[test]
932    fn dont_extract_in_comment() {
933        cov_mark::check!(extract_var_in_comment_is_not_applicable);
934        check_assist_not_applicable(extract_variable, r#"fn main() { 1 + /* $0comment$0 */ 1; }"#);
935    }
936
937    #[test]
938    fn extract_var_expr_stmt() {
939        cov_mark::check!(test_extract_var_expr_stmt);
940        check_assist_by_label(
941            extract_variable,
942            r#"
943fn foo() {
944  $0  1 + 1$0;
945}"#,
946            r#"
947fn foo() {
948    let $0var_name = 1 + 1;
949}"#,
950            "Extract into variable",
951        );
952        check_assist_by_label(
953            extract_variable,
954            r#"
955fn foo() {
956    $0{ let x = 0; x }$0;
957    something_else();
958}"#,
959            r#"
960fn foo() {
961    let $0var_name = { let x = 0; x };
962    something_else();
963}"#,
964            "Extract into variable",
965        );
966    }
967
968    #[test]
969    fn extract_const_expr_stmt() {
970        cov_mark::check!(test_extract_var_expr_stmt);
971        check_assist_by_label(
972            extract_variable,
973            r#"
974fn foo() {
975  $0  1 + 1$0;
976}"#,
977            r#"
978fn foo() {
979    const $0VAR_NAME: i32 = 1 + 1;
980}"#,
981            "Extract into constant",
982        );
983        // This is hilarious but as far as I know, it's valid
984        check_assist_by_label(
985            extract_variable,
986            r#"
987fn foo() {
988    $0{ let x = 0; x }$0;
989    something_else();
990}"#,
991            r#"
992fn foo() {
993    const $0VAR_NAME: i32 = { let x = 0; x };
994    something_else();
995}"#,
996            "Extract into constant",
997        );
998    }
999
1000    #[test]
1001    fn extract_static_expr_stmt() {
1002        cov_mark::check!(test_extract_var_expr_stmt);
1003        check_assist_by_label(
1004            extract_variable,
1005            r#"
1006fn foo() {
1007  $0  1 + 1$0;
1008}"#,
1009            r#"
1010fn foo() {
1011    static $0VAR_NAME: i32 = 1 + 1;
1012}"#,
1013            "Extract into static",
1014        );
1015        // This is hilarious but as far as I know, it's valid
1016        check_assist_by_label(
1017            extract_variable,
1018            r#"
1019fn foo() {
1020    $0{ let x = 0; x }$0;
1021    something_else();
1022}"#,
1023            r#"
1024fn foo() {
1025    static $0VAR_NAME: i32 = { let x = 0; x };
1026    something_else();
1027}"#,
1028            "Extract into static",
1029        );
1030    }
1031
1032    #[test]
1033    fn extract_var_part_of_expr_stmt() {
1034        check_assist_by_label(
1035            extract_variable,
1036            r#"
1037fn foo() {
1038    $01$0 + 1;
1039}"#,
1040            r#"
1041fn foo() {
1042    let $0var_name = 1;
1043    var_name + 1;
1044}"#,
1045            "Extract into variable",
1046        );
1047    }
1048
1049    #[test]
1050    fn extract_const_part_of_expr_stmt() {
1051        check_assist_by_label(
1052            extract_variable,
1053            r#"
1054fn foo() {
1055    $01$0 + 1;
1056}"#,
1057            r#"
1058fn foo() {
1059    const $0VAR_NAME: i32 = 1;
1060    VAR_NAME + 1;
1061}"#,
1062            "Extract into constant",
1063        );
1064    }
1065
1066    #[test]
1067    fn extract_static_part_of_expr_stmt() {
1068        check_assist_by_label(
1069            extract_variable,
1070            r#"
1071fn foo() {
1072    $01$0 + 1;
1073}"#,
1074            r#"
1075fn foo() {
1076    static $0VAR_NAME: i32 = 1;
1077    VAR_NAME + 1;
1078}"#,
1079            "Extract into static",
1080        );
1081    }
1082
1083    #[test]
1084    fn extract_var_last_expr() {
1085        cov_mark::check!(test_extract_var_last_expr);
1086        check_assist_by_label(
1087            extract_variable,
1088            r#"
1089fn foo() {
1090    bar($01 + 1$0)
1091}
1092"#,
1093            r#"
1094fn foo() {
1095    let $0var_name = 1 + 1;
1096    bar(var_name)
1097}
1098"#,
1099            "Extract into variable",
1100        );
1101        check_assist_by_label(
1102            extract_variable,
1103            r#"
1104fn foo() -> i32 {
1105    $0bar(1 + 1)$0
1106}
1107
1108fn bar(i: i32) -> i32 {
1109    i
1110}
1111"#,
1112            r#"
1113fn foo() -> i32 {
1114    let $0bar = bar(1 + 1);
1115    bar
1116}
1117
1118fn bar(i: i32) -> i32 {
1119    i
1120}
1121"#,
1122            "Extract into variable",
1123        )
1124    }
1125
1126    #[test]
1127    fn extract_const_last_expr() {
1128        cov_mark::check!(test_extract_var_last_expr);
1129        check_assist_by_label(
1130            extract_variable,
1131            r#"
1132fn foo() {
1133    bar($01 + 1$0)
1134}
1135"#,
1136            r#"
1137fn foo() {
1138    const $0VAR_NAME: i32 = 1 + 1;
1139    bar(VAR_NAME)
1140}
1141"#,
1142            "Extract into constant",
1143        );
1144        check_assist_by_label(
1145            extract_variable,
1146            r#"
1147fn foo() -> i32 {
1148    $0bar(1 + 1)$0
1149}
1150
1151const fn bar(i: i32) -> i32 {
1152    i
1153}
1154"#,
1155            r#"
1156fn foo() -> i32 {
1157    const $0BAR: i32 = bar(1 + 1);
1158    BAR
1159}
1160
1161const fn bar(i: i32) -> i32 {
1162    i
1163}
1164"#,
1165            "Extract into constant",
1166        )
1167    }
1168
1169    #[test]
1170    fn extract_static_last_expr() {
1171        cov_mark::check!(test_extract_var_last_expr);
1172        check_assist_by_label(
1173            extract_variable,
1174            r#"
1175fn foo() {
1176    bar($01 + 1$0)
1177}
1178"#,
1179            r#"
1180fn foo() {
1181    static $0VAR_NAME: i32 = 1 + 1;
1182    bar(VAR_NAME)
1183}
1184"#,
1185            "Extract into static",
1186        );
1187        check_assist_by_label(
1188            extract_variable,
1189            r#"
1190fn foo() -> i32 {
1191    $0bar(1 + 1)$0
1192}
1193
1194const fn bar(i: i32) -> i32 {
1195    i
1196}
1197"#,
1198            r#"
1199fn foo() -> i32 {
1200    static $0BAR: i32 = bar(1 + 1);
1201    BAR
1202}
1203
1204const fn bar(i: i32) -> i32 {
1205    i
1206}
1207"#,
1208            "Extract into static",
1209        )
1210    }
1211
1212    #[test]
1213    fn extract_var_in_match_arm_no_block() {
1214        cov_mark::check!(test_extract_var_in_match_arm_no_block);
1215        check_assist_by_label(
1216            extract_variable,
1217            r#"
1218fn main() {
1219    let x = true;
1220    let tuple = match x {
1221        true => ($02 + 2$0, true)
1222        _ => (0, false)
1223    };
1224}
1225"#,
1226            r#"
1227fn main() {
1228    let x = true;
1229    let tuple = match x {
1230        true => {
1231            let $0var_name = 2 + 2;
1232            (var_name, true)
1233        }
1234        _ => (0, false)
1235    };
1236}
1237"#,
1238            "Extract into variable",
1239        );
1240    }
1241
1242    #[test]
1243    fn extract_var_in_match_arm_with_block() {
1244        check_assist_by_label(
1245            extract_variable,
1246            r#"
1247fn main() {
1248    let x = true;
1249    let tuple = match x {
1250        true => {
1251            let y = 1;
1252            ($02 + y$0, true)
1253        }
1254        _ => (0, false)
1255    };
1256}
1257"#,
1258            r#"
1259fn main() {
1260    let x = true;
1261    let tuple = match x {
1262        true => {
1263            let y = 1;
1264            let $0var_name = 2 + y;
1265            (var_name, true)
1266        }
1267        _ => (0, false)
1268    };
1269}
1270"#,
1271            "Extract into variable",
1272        );
1273    }
1274
1275    #[test]
1276    fn extract_var_in_match_guard() {
1277        cov_mark::check!(test_extract_var_in_match_guard);
1278        check_assist_by_label(
1279            extract_variable,
1280            r#"
1281fn main() {
1282    match () {
1283        () if $010 > 0$0 => 1
1284        _ => 2
1285    };
1286}
1287"#,
1288            r#"
1289fn main() {
1290    let $0var_name = 10 > 0;
1291    match () {
1292        () if var_name => 1
1293        _ => 2
1294    };
1295}
1296"#,
1297            "Extract into variable",
1298        );
1299    }
1300
1301    #[test]
1302    fn extract_var_in_closure_no_block() {
1303        cov_mark::check!(test_extract_var_in_closure_no_block);
1304        check_assist_by_label(
1305            extract_variable,
1306            r#"
1307fn main() {
1308    let lambda = |x: u32| $0x * 2$0;
1309}
1310"#,
1311            r#"
1312fn main() {
1313    let lambda = |x: u32| {
1314        let $0var_name = x * 2;
1315        var_name
1316    };
1317}
1318"#,
1319            "Extract into variable",
1320        );
1321    }
1322
1323    #[test]
1324    fn extract_var_in_closure_with_block() {
1325        check_assist_by_label(
1326            extract_variable,
1327            r#"
1328fn main() {
1329    let lambda = |x: u32| { $0x * 2$0 };
1330}
1331"#,
1332            r#"
1333fn main() {
1334    let lambda = |x: u32| { let $0var_name = x * 2; var_name };
1335}
1336"#,
1337            "Extract into variable",
1338        );
1339    }
1340
1341    #[test]
1342    fn extract_var_in_arglist_with_comma() {
1343        check_assist_by_label(
1344            extract_variable,
1345            r#"
1346fn main() {
1347    let x = 2;
1348    foo(
1349        x + x,
1350        $0x - x,$0
1351    )
1352}
1353"#,
1354            r#"
1355fn main() {
1356    let x = 2;
1357    let $0var_name = x - x;
1358    foo(
1359        x + x,
1360        var_name,
1361    )
1362}
1363"#,
1364            "Extract into variable",
1365        );
1366    }
1367
1368    #[test]
1369    fn extract_var_path_simple() {
1370        check_assist_by_label(
1371            extract_variable,
1372            r#"
1373fn main() {
1374    let o = $0Some(true)$0;
1375}
1376"#,
1377            r#"
1378fn main() {
1379    let $0var_name = Some(true);
1380    let o = var_name;
1381}
1382"#,
1383            "Extract into variable",
1384        );
1385    }
1386
1387    #[test]
1388    fn extract_var_path_method() {
1389        check_assist_by_label(
1390            extract_variable,
1391            r#"
1392fn main() {
1393    let v = $0bar.foo()$0;
1394}
1395"#,
1396            r#"
1397fn main() {
1398    let $0foo = bar.foo();
1399    let v = foo;
1400}
1401"#,
1402            "Extract into variable",
1403        );
1404    }
1405
1406    #[test]
1407    fn extract_var_return() {
1408        check_assist_by_label(
1409            extract_variable,
1410            r#"
1411fn foo() -> u32 {
1412    $0return 2 + 2$0;
1413}
1414"#,
1415            r#"
1416fn foo() -> u32 {
1417    let $0var_name = 2 + 2;
1418    return var_name;
1419}
1420"#,
1421            "Extract into variable",
1422        );
1423    }
1424
1425    #[test]
1426    fn extract_var_does_not_add_extra_whitespace() {
1427        check_assist_by_label(
1428            extract_variable,
1429            r#"
1430fn foo() -> u32 {
1431
1432
1433    $0return 2 + 2$0;
1434}
1435"#,
1436            r#"
1437fn foo() -> u32 {
1438
1439
1440    let $0var_name = 2 + 2;
1441    return var_name;
1442}
1443"#,
1444            "Extract into variable",
1445        );
1446
1447        check_assist_by_label(
1448            extract_variable,
1449            r#"
1450fn foo() -> u32 {
1451
1452        $0return 2 + 2$0;
1453}
1454"#,
1455            r#"
1456fn foo() -> u32 {
1457
1458        let $0var_name = 2 + 2;
1459        return var_name;
1460}
1461"#,
1462            "Extract into variable",
1463        );
1464
1465        check_assist_by_label(
1466            extract_variable,
1467            r#"
1468fn foo() -> u32 {
1469    let foo = 1;
1470
1471    // bar
1472
1473
1474    $0return 2 + 2$0;
1475}
1476"#,
1477            r#"
1478fn foo() -> u32 {
1479    let foo = 1;
1480
1481    // bar
1482
1483
1484    let $0var_name = 2 + 2;
1485    return var_name;
1486}
1487"#,
1488            "Extract into variable",
1489        );
1490    }
1491
1492    #[test]
1493    fn extract_var_break() {
1494        check_assist_by_label(
1495            extract_variable,
1496            r#"
1497fn main() {
1498    let result = loop {
1499        $0break 2 + 2$0;
1500    };
1501}
1502"#,
1503            r#"
1504fn main() {
1505    let result = loop {
1506        let $0var_name = 2 + 2;
1507        break var_name;
1508    };
1509}
1510"#,
1511            "Extract into variable",
1512        );
1513    }
1514
1515    #[test]
1516    fn extract_var_let_expr() {
1517        check_assist_by_label(
1518            extract_variable,
1519            r#"
1520fn main() {
1521    if $0let$0 Some(x) = Some(2+2) {}
1522}
1523"#,
1524            r#"
1525fn main() {
1526    let $0var_name = Some(2+2);
1527    if let Some(x) = var_name {}
1528}
1529"#,
1530            "Extract into variable",
1531        );
1532    }
1533
1534    #[test]
1535    fn extract_var_for_cast() {
1536        check_assist_by_label(
1537            extract_variable,
1538            r#"
1539fn main() {
1540    let v = $00f32 as u32$0;
1541}
1542"#,
1543            r#"
1544fn main() {
1545    let $0var_name = 0f32 as u32;
1546    let v = var_name;
1547}
1548"#,
1549            "Extract into variable",
1550        );
1551    }
1552
1553    #[test]
1554    fn extract_var_field_shorthand() {
1555        check_assist_by_label(
1556            extract_variable,
1557            r#"
1558struct S {
1559    foo: i32
1560}
1561
1562fn main() {
1563    S { foo: $01 + 1$0 }
1564}
1565"#,
1566            r#"
1567struct S {
1568    foo: i32
1569}
1570
1571fn main() {
1572    let $0foo = 1 + 1;
1573    S { foo }
1574}
1575"#,
1576            "Extract into variable",
1577        )
1578    }
1579
1580    #[test]
1581    fn extract_var_name_from_type() {
1582        check_assist_by_label(
1583            extract_variable,
1584            r#"
1585struct Test(i32);
1586
1587fn foo() -> Test {
1588    $0{ Test(10) }$0
1589}
1590"#,
1591            r#"
1592struct Test(i32);
1593
1594fn foo() -> Test {
1595    let $0test = { Test(10) };
1596    test
1597}
1598"#,
1599            "Extract into variable",
1600        )
1601    }
1602
1603    #[test]
1604    fn extract_var_name_from_parameter() {
1605        check_assist_by_label(
1606            extract_variable,
1607            r#"
1608fn bar(test: u32, size: u32)
1609
1610fn foo() {
1611    bar(1, $01+1$0);
1612}
1613"#,
1614            r#"
1615fn bar(test: u32, size: u32)
1616
1617fn foo() {
1618    let $0size = 1+1;
1619    bar(1, size);
1620}
1621"#,
1622            "Extract into variable",
1623        )
1624    }
1625
1626    #[test]
1627    fn extract_var_parameter_name_has_precedence_over_type() {
1628        check_assist_by_label(
1629            extract_variable,
1630            r#"
1631struct TextSize(u32);
1632fn bar(test: u32, size: TextSize)
1633
1634fn foo() {
1635    bar(1, $0{ TextSize(1+1) }$0);
1636}
1637"#,
1638            r#"
1639struct TextSize(u32);
1640fn bar(test: u32, size: TextSize)
1641
1642fn foo() {
1643    let $0size = { TextSize(1+1) };
1644    bar(1, size);
1645}
1646"#,
1647            "Extract into variable",
1648        )
1649    }
1650
1651    #[test]
1652    fn extract_var_name_from_function() {
1653        check_assist_by_label(
1654            extract_variable,
1655            r#"
1656fn is_required(test: u32, size: u32) -> bool
1657
1658fn foo() -> bool {
1659    $0is_required(1, 2)$0
1660}
1661"#,
1662            r#"
1663fn is_required(test: u32, size: u32) -> bool
1664
1665fn foo() -> bool {
1666    let $0is_required = is_required(1, 2);
1667    is_required
1668}
1669"#,
1670            "Extract into variable",
1671        )
1672    }
1673
1674    #[test]
1675    fn extract_var_name_from_method() {
1676        check_assist_by_label(
1677            extract_variable,
1678            r#"
1679struct S;
1680impl S {
1681    fn bar(&self, n: u32) -> u32 { n }
1682}
1683
1684fn foo() -> u32 {
1685    $0S.bar(1)$0
1686}
1687"#,
1688            r#"
1689struct S;
1690impl S {
1691    fn bar(&self, n: u32) -> u32 { n }
1692}
1693
1694fn foo() -> u32 {
1695    let $0bar = S.bar(1);
1696    bar
1697}
1698"#,
1699            "Extract into variable",
1700        )
1701    }
1702
1703    #[test]
1704    fn extract_var_name_from_method_param() {
1705        check_assist_by_label(
1706            extract_variable,
1707            r#"
1708struct S;
1709impl S {
1710    fn bar(&self, n: u32, size: u32) { n }
1711}
1712
1713fn foo() {
1714    S.bar($01 + 1$0, 2)
1715}
1716"#,
1717            r#"
1718struct S;
1719impl S {
1720    fn bar(&self, n: u32, size: u32) { n }
1721}
1722
1723fn foo() {
1724    let $0n = 1 + 1;
1725    S.bar(n, 2)
1726}
1727"#,
1728            "Extract into variable",
1729        )
1730    }
1731
1732    #[test]
1733    fn extract_var_name_from_ufcs_method_param() {
1734        check_assist_by_label(
1735            extract_variable,
1736            r#"
1737struct S;
1738impl S {
1739    fn bar(&self, n: u32, size: u32) { n }
1740}
1741
1742fn foo() {
1743    S::bar(&S, $01 + 1$0, 2)
1744}
1745"#,
1746            r#"
1747struct S;
1748impl S {
1749    fn bar(&self, n: u32, size: u32) { n }
1750}
1751
1752fn foo() {
1753    let $0n = 1 + 1;
1754    S::bar(&S, n, 2)
1755}
1756"#,
1757            "Extract into variable",
1758        )
1759    }
1760
1761    #[test]
1762    fn extract_var_parameter_name_has_precedence_over_function() {
1763        check_assist_by_label(
1764            extract_variable,
1765            r#"
1766fn bar(test: u32, size: u32)
1767
1768fn foo() {
1769    bar(1, $0symbol_size(1, 2)$0);
1770}
1771"#,
1772            r#"
1773fn bar(test: u32, size: u32)
1774
1775fn foo() {
1776    let $0size = symbol_size(1, 2);
1777    bar(1, size);
1778}
1779"#,
1780            "Extract into variable",
1781        )
1782    }
1783
1784    #[test]
1785    fn extract_macro_call() {
1786        check_assist_by_label(
1787            extract_variable,
1788            r#"
1789struct Vec;
1790macro_rules! vec {
1791    () => {Vec}
1792}
1793fn main() {
1794    let _ = $0vec![]$0;
1795}
1796"#,
1797            r#"
1798struct Vec;
1799macro_rules! vec {
1800    () => {Vec}
1801}
1802fn main() {
1803    let $0items = vec![];
1804    let _ = items;
1805}
1806"#,
1807            "Extract into variable",
1808        );
1809
1810        check_assist_by_label(
1811            extract_variable,
1812            r#"
1813struct Vec;
1814macro_rules! vec {
1815    () => {Vec}
1816}
1817fn main() {
1818    let _ = $0vec![]$0;
1819}
1820"#,
1821            r#"
1822struct Vec;
1823macro_rules! vec {
1824    () => {Vec}
1825}
1826fn main() {
1827    const $0ITEMS: Vec = vec![];
1828    let _ = ITEMS;
1829}
1830"#,
1831            "Extract into constant",
1832        );
1833
1834        check_assist_by_label(
1835            extract_variable,
1836            r#"
1837struct Vec;
1838macro_rules! vec {
1839    () => {Vec}
1840}
1841fn main() {
1842    let _ = $0vec![]$0;
1843}
1844"#,
1845            r#"
1846struct Vec;
1847macro_rules! vec {
1848    () => {Vec}
1849}
1850fn main() {
1851    static $0ITEMS: Vec = vec![];
1852    let _ = ITEMS;
1853}
1854"#,
1855            "Extract into static",
1856        );
1857    }
1858
1859    #[test]
1860    fn extract_non_local_path_expr() {
1861        check_assist_by_label(
1862            extract_variable,
1863            r#"
1864struct Foo;
1865fn foo() -> Foo {
1866    $0Foo$0
1867}
1868"#,
1869            r#"
1870struct Foo;
1871fn foo() -> Foo {
1872    let $0foo = Foo;
1873    foo
1874}
1875"#,
1876            "Extract into variable",
1877        );
1878    }
1879
1880    #[test]
1881    fn extract_var_for_return_not_applicable() {
1882        check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } ");
1883    }
1884
1885    #[test]
1886    fn extract_var_for_break_not_applicable() {
1887        check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }");
1888    }
1889
1890    #[test]
1891    fn extract_var_for_let_expr_not_applicable() {
1892        check_assist_not_applicable(
1893            extract_variable,
1894            "fn main() { if $0let Some(x) = Some(2+2) {} }",
1895        );
1896    }
1897
1898    #[test]
1899    fn extract_var_unit_expr_not_applicable() {
1900        check_assist_not_applicable(
1901            extract_variable,
1902            r#"
1903fn foo() {
1904    let mut i = 3;
1905    $0if i >= 0 {
1906        i += 1;
1907    } else {
1908        i -= 1;
1909    }$0
1910}"#,
1911        );
1912    }
1913
1914    // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
1915    #[test]
1916    fn extract_var_target() {
1917        check_assist_target(extract_variable, r#"fn foo() -> u32 { $0return 2 + 2$0; }"#, "2 + 2");
1918
1919        check_assist_target(
1920            extract_variable,
1921            r#"
1922fn main() {
1923    let x = true;
1924    let tuple = match x {
1925        true => ($02 + 2$0, true)
1926        _ => (0, false)
1927    };
1928}
1929"#,
1930            "2 + 2",
1931        );
1932    }
1933
1934    #[test]
1935    fn extract_var_no_block_body() {
1936        check_assist_not_applicable_by_label(
1937            extract_variable,
1938            r#"
1939const X: usize = $0100$0;
1940"#,
1941            "Extract into variable",
1942        );
1943    }
1944
1945    #[test]
1946    fn extract_const_no_block_body() {
1947        check_assist_by_label(
1948            extract_variable,
1949            r#"
1950const fn foo(x: i32) -> i32 {
1951    x
1952}
1953
1954const FOO: i32 = foo($0100$0);
1955"#,
1956            r#"
1957const fn foo(x: i32) -> i32 {
1958    x
1959}
1960
1961const $0X: i32 = 100;
1962const FOO: i32 = foo(X);
1963"#,
1964            "Extract into constant",
1965        );
1966
1967        check_assist_by_label(
1968            extract_variable,
1969            r#"
1970mod foo {
1971    enum Foo {
1972        Bar,
1973        Baz = $042$0,
1974    }
1975}
1976"#,
1977            r#"
1978mod foo {
1979    const $0VAR_NAME: isize = 42;
1980    enum Foo {
1981        Bar,
1982        Baz = VAR_NAME,
1983    }
1984}
1985"#,
1986            "Extract into constant",
1987        );
1988
1989        check_assist_by_label(
1990            extract_variable,
1991            r#"
1992const fn foo(x: i32) -> i32 {
1993    x
1994}
1995
1996trait Hello {
1997    const World: i32;
1998}
1999
2000struct Bar;
2001impl Hello for Bar {
2002    const World = foo($042$0);
2003}
2004"#,
2005            r#"
2006const fn foo(x: i32) -> i32 {
2007    x
2008}
2009
2010trait Hello {
2011    const World: i32;
2012}
2013
2014struct Bar;
2015impl Hello for Bar {
2016    const $0X: i32 = 42;
2017    const World = foo(X);
2018}
2019"#,
2020            "Extract into constant",
2021        );
2022
2023        check_assist_by_label(
2024            extract_variable,
2025            r#"
2026const fn foo(x: i32) -> i32 {
2027    x
2028}
2029
2030fn bar() {
2031    const BAZ: i32 = foo($042$0);
2032}
2033"#,
2034            r#"
2035const fn foo(x: i32) -> i32 {
2036    x
2037}
2038
2039fn bar() {
2040    const $0X: i32 = 42;
2041    const BAZ: i32 = foo(X);
2042}
2043"#,
2044            "Extract into constant",
2045        );
2046    }
2047
2048    #[test]
2049    fn extract_static_no_block_body() {
2050        check_assist_by_label(
2051            extract_variable,
2052            r#"
2053const fn foo(x: i32) -> i32 {
2054    x
2055}
2056
2057const FOO: i32 = foo($0100$0);
2058"#,
2059            r#"
2060const fn foo(x: i32) -> i32 {
2061    x
2062}
2063
2064static $0X: i32 = 100;
2065const FOO: i32 = foo(X);
2066"#,
2067            "Extract into static",
2068        );
2069
2070        check_assist_by_label(
2071            extract_variable,
2072            r#"
2073mod foo {
2074    enum Foo {
2075        Bar,
2076        Baz = $042$0,
2077    }
2078}
2079"#,
2080            r#"
2081mod foo {
2082    static $0VAR_NAME: isize = 42;
2083    enum Foo {
2084        Bar,
2085        Baz = VAR_NAME,
2086    }
2087}
2088"#,
2089            "Extract into static",
2090        );
2091
2092        check_assist_by_label(
2093            extract_variable,
2094            r#"
2095const fn foo(x: i32) -> i32 {
2096    x
2097}
2098
2099trait Hello {
2100    const World: i32;
2101}
2102
2103struct Bar;
2104impl Hello for Bar {
2105    const World = foo($042$0);
2106}
2107"#,
2108            r#"
2109const fn foo(x: i32) -> i32 {
2110    x
2111}
2112
2113trait Hello {
2114    const World: i32;
2115}
2116
2117struct Bar;
2118impl Hello for Bar {
2119    static $0X: i32 = 42;
2120    const World = foo(X);
2121}
2122"#,
2123            "Extract into static",
2124        );
2125
2126        check_assist_by_label(
2127            extract_variable,
2128            r#"
2129const fn foo(x: i32) -> i32 {
2130    x
2131}
2132
2133fn bar() {
2134    const BAZ: i32 = foo($042$0);
2135}
2136"#,
2137            r#"
2138const fn foo(x: i32) -> i32 {
2139    x
2140}
2141
2142fn bar() {
2143    static $0X: i32 = 42;
2144    const BAZ: i32 = foo(X);
2145}
2146"#,
2147            "Extract into static",
2148        );
2149    }
2150
2151    #[test]
2152    fn extract_var_mutable_reference_parameter() {
2153        check_assist_by_label(
2154            extract_variable,
2155            r#"
2156struct S {
2157    vec: Vec<u8>
2158}
2159
2160struct Vec<T>;
2161impl<T> Vec<T> {
2162    fn push(&mut self, _:usize) {}
2163}
2164
2165fn foo(s: &mut S) {
2166    $0s.vec$0.push(0);
2167}"#,
2168            r#"
2169struct S {
2170    vec: Vec<u8>
2171}
2172
2173struct Vec<T>;
2174impl<T> Vec<T> {
2175    fn push(&mut self, _:usize) {}
2176}
2177
2178fn foo(s: &mut S) {
2179    let $0items = &mut s.vec;
2180    items.push(0);
2181}"#,
2182            "Extract into variable",
2183        );
2184    }
2185
2186    #[test]
2187    fn dont_extract_const_mutable_reference_parameter() {
2188        check_assist_not_applicable_by_label(
2189            extract_variable,
2190            r#"
2191struct S {
2192    vec: Vec<u8>
2193}
2194
2195struct Vec<T>;
2196impl<T> Vec<T> {
2197    fn push(&mut self, _:usize) {}
2198}
2199
2200fn foo(s: &mut S) {
2201    $0s.vec$0.push(0);
2202}"#,
2203            "Extract into constant",
2204        );
2205    }
2206
2207    #[test]
2208    fn dont_extract_static_mutable_reference_parameter() {
2209        check_assist_not_applicable_by_label(
2210            extract_variable,
2211            r#"
2212struct S {
2213    vec: Vec<u8>
2214}
2215
2216struct Vec<T>;
2217impl<T> Vec<T> {
2218    fn push(&mut self, _:usize) {}
2219}
2220
2221fn foo(s: &mut S) {
2222    $0s.vec$0.push(0);
2223}"#,
2224            "Extract into static",
2225        );
2226    }
2227
2228    #[test]
2229    fn extract_var_mutable_reference_parameter_deep_nesting() {
2230        check_assist_by_label(
2231            extract_variable,
2232            r#"
2233struct Y {
2234    field: X
2235}
2236struct X {
2237    field: S
2238}
2239struct S {
2240    vec: Vec<u8>
2241}
2242struct Vec<T>;
2243impl<T> Vec<T> {
2244    fn push(&mut self, _:usize) {}
2245}
2246
2247fn foo(f: &mut Y) {
2248    $0f.field.field.vec$0.push(0);
2249}"#,
2250            r#"
2251struct Y {
2252    field: X
2253}
2254struct X {
2255    field: S
2256}
2257struct S {
2258    vec: Vec<u8>
2259}
2260struct Vec<T>;
2261impl<T> Vec<T> {
2262    fn push(&mut self, _:usize) {}
2263}
2264
2265fn foo(f: &mut Y) {
2266    let $0items = &mut f.field.field.vec;
2267    items.push(0);
2268}"#,
2269            "Extract into variable",
2270        );
2271    }
2272
2273    #[test]
2274    fn extract_var_reference_parameter() {
2275        check_assist_by_label(
2276            extract_variable,
2277            r#"
2278struct X;
2279
2280impl X {
2281    fn do_thing(&self) {
2282
2283    }
2284}
2285
2286struct S {
2287    sub: X
2288}
2289
2290fn foo(s: &S) {
2291    $0s.sub$0.do_thing();
2292}"#,
2293            r#"
2294struct X;
2295
2296impl X {
2297    fn do_thing(&self) {
2298
2299    }
2300}
2301
2302struct S {
2303    sub: X
2304}
2305
2306fn foo(s: &S) {
2307    let $0x = &s.sub;
2308    x.do_thing();
2309}"#,
2310            "Extract into variable",
2311        );
2312    }
2313
2314    #[test]
2315    fn extract_var_index_deref() {
2316        check_assist_by_label(
2317            extract_variable,
2318            r#"
2319//- minicore: index
2320struct X;
2321
2322impl core::ops::Index<usize> for X {
2323    type Output = i32;
2324    fn index(&self) -> &Self::Output { 0 }
2325}
2326
2327struct S {
2328    sub: X
2329}
2330
2331fn foo(s: &S) {
2332    $0s.sub$0[0];
2333}"#,
2334            r#"
2335struct X;
2336
2337impl core::ops::Index<usize> for X {
2338    type Output = i32;
2339    fn index(&self) -> &Self::Output { 0 }
2340}
2341
2342struct S {
2343    sub: X
2344}
2345
2346fn foo(s: &S) {
2347    let $0x = &s.sub;
2348    x[0];
2349}"#,
2350            "Extract into variable",
2351        );
2352    }
2353
2354    #[test]
2355    fn extract_var_reference_parameter_deep_nesting() {
2356        check_assist_by_label(
2357            extract_variable,
2358            r#"
2359struct Z;
2360impl Z {
2361    fn do_thing(&self) {
2362
2363    }
2364}
2365
2366struct Y {
2367    field: Z
2368}
2369
2370struct X {
2371    field: Y
2372}
2373
2374struct S {
2375    sub: X
2376}
2377
2378fn foo(s: &S) {
2379    $0s.sub.field.field$0.do_thing();
2380}"#,
2381            r#"
2382struct Z;
2383impl Z {
2384    fn do_thing(&self) {
2385
2386    }
2387}
2388
2389struct Y {
2390    field: Z
2391}
2392
2393struct X {
2394    field: Y
2395}
2396
2397struct S {
2398    sub: X
2399}
2400
2401fn foo(s: &S) {
2402    let $0z = &s.sub.field.field;
2403    z.do_thing();
2404}"#,
2405            "Extract into variable",
2406        );
2407    }
2408
2409    #[test]
2410    fn extract_var_regular_parameter() {
2411        check_assist_by_label(
2412            extract_variable,
2413            r#"
2414struct X;
2415
2416impl X {
2417    fn do_thing(&self) {
2418
2419    }
2420}
2421
2422struct S {
2423    sub: X
2424}
2425
2426fn foo(s: S) {
2427    $0s.sub$0.do_thing();
2428}"#,
2429            r#"
2430struct X;
2431
2432impl X {
2433    fn do_thing(&self) {
2434
2435    }
2436}
2437
2438struct S {
2439    sub: X
2440}
2441
2442fn foo(s: S) {
2443    let $0x = &s.sub;
2444    x.do_thing();
2445}"#,
2446            "Extract into variable",
2447        );
2448    }
2449
2450    #[test]
2451    fn extract_var_mutable_reference_local() {
2452        check_assist_by_label(
2453            extract_variable,
2454            r#"
2455struct X;
2456
2457struct S {
2458    sub: X
2459}
2460
2461impl S {
2462    fn new() -> S {
2463        S {
2464            sub: X::new()
2465        }
2466    }
2467}
2468
2469impl X {
2470    fn new() -> X {
2471        X { }
2472    }
2473    fn do_thing(&self) {
2474
2475    }
2476}
2477
2478
2479fn foo() {
2480    let local = &mut S::new();
2481    $0local.sub$0.do_thing();
2482}"#,
2483            r#"
2484struct X;
2485
2486struct S {
2487    sub: X
2488}
2489
2490impl S {
2491    fn new() -> S {
2492        S {
2493            sub: X::new()
2494        }
2495    }
2496}
2497
2498impl X {
2499    fn new() -> X {
2500        X { }
2501    }
2502    fn do_thing(&self) {
2503
2504    }
2505}
2506
2507
2508fn foo() {
2509    let local = &mut S::new();
2510    let $0x = &local.sub;
2511    x.do_thing();
2512}"#,
2513            "Extract into variable",
2514        );
2515    }
2516
2517    #[test]
2518    fn extract_var_reference_local() {
2519        check_assist_by_label(
2520            extract_variable,
2521            r#"
2522struct X;
2523
2524struct S {
2525    sub: X
2526}
2527
2528impl S {
2529    fn new() -> S {
2530        S {
2531            sub: X::new()
2532        }
2533    }
2534}
2535
2536impl X {
2537    fn new() -> X {
2538        X { }
2539    }
2540    fn do_thing(&self) {
2541
2542    }
2543}
2544
2545
2546fn foo() {
2547    let local = &S::new();
2548    $0local.sub$0.do_thing();
2549}"#,
2550            r#"
2551struct X;
2552
2553struct S {
2554    sub: X
2555}
2556
2557impl S {
2558    fn new() -> S {
2559        S {
2560            sub: X::new()
2561        }
2562    }
2563}
2564
2565impl X {
2566    fn new() -> X {
2567        X { }
2568    }
2569    fn do_thing(&self) {
2570
2571    }
2572}
2573
2574
2575fn foo() {
2576    let local = &S::new();
2577    let $0x = &local.sub;
2578    x.do_thing();
2579}"#,
2580            "Extract into variable",
2581        );
2582    }
2583
2584    #[test]
2585    fn extract_var_for_mutable_borrow() {
2586        check_assist_by_label(
2587            extract_variable,
2588            r#"
2589fn foo() {
2590    let v = &mut $00$0;
2591}"#,
2592            r#"
2593fn foo() {
2594    let mut $0var_name = 0;
2595    let v = &mut var_name;
2596}"#,
2597            "Extract into variable",
2598        );
2599    }
2600
2601    #[test]
2602    fn dont_extract_const_for_mutable_borrow() {
2603        check_assist_not_applicable_by_label(
2604            extract_variable,
2605            r#"
2606fn foo() {
2607    let v = &mut $00$0;
2608}"#,
2609            "Extract into constant",
2610        );
2611    }
2612
2613    #[test]
2614    fn dont_extract_static_for_mutable_borrow() {
2615        check_assist_not_applicable_by_label(
2616            extract_variable,
2617            r#"
2618fn foo() {
2619    let v = &mut $00$0;
2620}"#,
2621            "Extract into static",
2622        );
2623    }
2624
2625    #[test]
2626    fn generates_no_ref_on_calls() {
2627        check_assist_by_label(
2628            extract_variable,
2629            r#"
2630struct S;
2631impl S {
2632    fn do_work(&mut self) {}
2633}
2634fn bar() -> S { S }
2635fn foo() {
2636    $0bar()$0.do_work();
2637}"#,
2638            r#"
2639struct S;
2640impl S {
2641    fn do_work(&mut self) {}
2642}
2643fn bar() -> S { S }
2644fn foo() {
2645    let mut $0bar = bar();
2646    bar.do_work();
2647}"#,
2648            "Extract into variable",
2649        );
2650    }
2651
2652    #[test]
2653    fn generates_no_ref_for_deref() {
2654        check_assist_by_label(
2655            extract_variable,
2656            r#"
2657struct S;
2658impl S {
2659    fn do_work(&mut self) {}
2660}
2661fn bar() -> S { S }
2662fn foo() {
2663    let v = &mut &mut bar();
2664    $0(**v)$0.do_work();
2665}
2666"#,
2667            r#"
2668struct S;
2669impl S {
2670    fn do_work(&mut self) {}
2671}
2672fn bar() -> S { S }
2673fn foo() {
2674    let v = &mut &mut bar();
2675    let $0s = *v;
2676    s.do_work();
2677}
2678"#,
2679            "Extract into variable",
2680        );
2681    }
2682
2683    #[test]
2684    fn extract_string_literal() {
2685        check_assist_by_label(
2686            extract_variable,
2687            r#"
2688struct Entry<'a>(&'a str);
2689fn foo() {
2690    let entry = Entry($0"Hello"$0);
2691}
2692"#,
2693            r#"
2694struct Entry<'a>(&'a str);
2695fn foo() {
2696    let $0hello = "Hello";
2697    let entry = Entry(hello);
2698}
2699"#,
2700            "Extract into variable",
2701        );
2702
2703        check_assist_by_label(
2704            extract_variable,
2705            r#"
2706struct Entry<'a>(&'a str);
2707fn foo() {
2708    let entry = Entry($0"Hello"$0);
2709}
2710"#,
2711            r#"
2712struct Entry<'a>(&'a str);
2713fn foo() {
2714    const $0HELLO: &str = "Hello";
2715    let entry = Entry(HELLO);
2716}
2717"#,
2718            "Extract into constant",
2719        );
2720
2721        check_assist_by_label(
2722            extract_variable,
2723            r#"
2724struct Entry<'a>(&'a str);
2725fn foo() {
2726    let entry = Entry($0"Hello"$0);
2727}
2728"#,
2729            r#"
2730struct Entry<'a>(&'a str);
2731fn foo() {
2732    static $0HELLO: &str = "Hello";
2733    let entry = Entry(HELLO);
2734}
2735"#,
2736            "Extract into static",
2737        );
2738    }
2739
2740    #[test]
2741    fn extract_variable_string_literal_use_field_shorthand() {
2742        // When field shorthand is available, it should
2743        // only be used when extracting into a variable
2744        check_assist_by_label(
2745            extract_variable,
2746            r#"
2747struct Entry<'a> { message: &'a str }
2748fn foo() {
2749    let entry = Entry { message: $0"Hello"$0 };
2750}
2751"#,
2752            r#"
2753struct Entry<'a> { message: &'a str }
2754fn foo() {
2755    let $0message = "Hello";
2756    let entry = Entry { message };
2757}
2758"#,
2759            "Extract into variable",
2760        );
2761
2762        check_assist_by_label(
2763            extract_variable,
2764            r#"
2765struct Entry<'a> { message: &'a str }
2766fn foo() {
2767    let entry = Entry { message: $0"Hello"$0 };
2768}
2769"#,
2770            r#"
2771struct Entry<'a> { message: &'a str }
2772fn foo() {
2773    const $0HELLO: &str = "Hello";
2774    let entry = Entry { message: HELLO };
2775}
2776"#,
2777            "Extract into constant",
2778        );
2779
2780        check_assist_by_label(
2781            extract_variable,
2782            r#"
2783struct Entry<'a> { message: &'a str }
2784fn foo() {
2785    let entry = Entry { message: $0"Hello"$0 };
2786}
2787"#,
2788            r#"
2789struct Entry<'a> { message: &'a str }
2790fn foo() {
2791    static $0HELLO: &str = "Hello";
2792    let entry = Entry { message: HELLO };
2793}
2794"#,
2795            "Extract into static",
2796        );
2797    }
2798
2799    #[test]
2800    fn extract_variable_name_conflicts() {
2801        check_assist_by_label(
2802            extract_variable,
2803            r#"
2804struct S { x: i32 };
2805
2806fn main() {
2807    let s = 2;
2808    let t = $0S { x: 1 }$0;
2809    let t2 = t;
2810    let x = s;
2811}
2812"#,
2813            r#"
2814struct S { x: i32 };
2815
2816fn main() {
2817    let s = 2;
2818    let $0s1 = S { x: 1 };
2819    let t = s1;
2820    let t2 = t;
2821    let x = s;
2822}
2823"#,
2824            "Extract into variable",
2825        );
2826    }
2827
2828    #[test]
2829    fn extract_variable_in_token_tree() {
2830        // FIXME: Keep the original trivia instead of extracting macro expanded?
2831        check_assist_by_label(
2832            extract_variable,
2833            r#"
2834macro_rules! foo {
2835    (= $($t:tt)*) => {
2836        $($t)*
2837    };
2838}
2839
2840fn main() {
2841    let x = foo!(= $02 + 3$0 + 4);
2842}
2843"#,
2844            r#"
2845macro_rules! foo {
2846    (= $($t:tt)*) => {
2847        $($t)*
2848    };
2849}
2850
2851fn main() {
2852    let $0var_name = 2+3;
2853    let x = foo!(= var_name + 4);
2854}
2855"#,
2856            "Extract into variable",
2857        );
2858
2859        check_assist_by_label(
2860            extract_variable,
2861            r#"
2862macro_rules! foo {
2863    (= $($t:tt)*) => {
2864        $($t)*
2865    };
2866}
2867
2868fn main() {
2869    let x = foo!(= $02 +$0 3 + 4);
2870}
2871"#,
2872            r#"
2873macro_rules! foo {
2874    (= $($t:tt)*) => {
2875        $($t)*
2876    };
2877}
2878
2879fn main() {
2880    let $0var_name = 2+3;
2881    let x = foo!(= var_name + 4);
2882}
2883"#,
2884            "Extract into variable",
2885        );
2886
2887        check_assist_by_label(
2888            extract_variable,
2889            r#"
2890macro_rules! foo {
2891    (= $($t:tt)*) => {
2892        $($t)*
2893    };
2894}
2895
2896fn main() {
2897    let x = foo!(= $02 + 3 + 4$0);
2898}
2899"#,
2900            r#"
2901macro_rules! foo {
2902    (= $($t:tt)*) => {
2903        $($t)*
2904    };
2905}
2906
2907fn main() {
2908    let $0var_name = 2+3+4;
2909    let x = foo!(= var_name);
2910}
2911"#,
2912            "Extract into variable",
2913        );
2914
2915        // FIXME: Extract to inside the macro instead of outside the macro
2916        check_assist_by_label(
2917            extract_variable,
2918            r#"
2919macro_rules! foo {
2920    (= $($t:tt)*) => {
2921        $($t)*
2922    };
2923}
2924
2925fn main() {
2926    let x = foo!(= {
2927        $02 + 3 + 4$0
2928    });
2929}
2930"#,
2931            r#"
2932macro_rules! foo {
2933    (= $($t:tt)*) => {
2934        $($t)*
2935    };
2936}
2937
2938fn main() {
2939    let $0var_name = 2+3+4;
2940    let x = foo!(= {
2941        var_name
2942    });
2943}
2944"#,
2945            "Extract into variable",
2946        );
2947    }
2948
2949    #[test]
2950    fn extract_variable_in_token_tree_record_expr() {
2951        check_assist_by_label(
2952            extract_variable,
2953            r#"
2954macro_rules! foo {
2955    (= $($t:tt)*) => {
2956        $($t)*
2957    };
2958}
2959
2960fn main() {
2961    let x = foo!(= Foo { x: $02 + 3$0 });
2962}
2963"#,
2964            r#"
2965macro_rules! foo {
2966    (= $($t:tt)*) => {
2967        $($t)*
2968    };
2969}
2970
2971fn main() {
2972    let $0x = 2+3;
2973    let x = foo!(= Foo { x: x });
2974}
2975"#,
2976            "Extract into variable",
2977        );
2978
2979        check_assist_by_label(
2980            extract_variable,
2981            r#"
2982macro_rules! foo {
2983    (= $($t:tt)*) => {
2984        $($t)*
2985    };
2986}
2987
2988fn main() {
2989    let x = foo!(= Foo { x: $02 + 3$0 + 4 });
2990}
2991"#,
2992            r#"
2993macro_rules! foo {
2994    (= $($t:tt)*) => {
2995        $($t)*
2996    };
2997}
2998
2999fn main() {
3000    let $0var_name = 2+3;
3001    let x = foo!(= Foo { x: var_name + 4 });
3002}
3003"#,
3004            "Extract into variable",
3005        );
3006    }
3007}