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