mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
feat: Add an assist to extract an expression into a static
This commit is contained in:
parent
135e71fcb3
commit
0cad614b3b
3 changed files with 682 additions and 116 deletions
|
@ -1,14 +1,12 @@
|
|||
use hir::{HirDisplay, TypeInfo};
|
||||
use ide_db::syntax_helpers::suggest_name;
|
||||
use ide_db::{assists::GroupLabel, syntax_helpers::suggest_name};
|
||||
use syntax::{
|
||||
ast::{
|
||||
self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
|
||||
AstNode,
|
||||
},
|
||||
syntax_editor::Position,
|
||||
NodeOrToken,
|
||||
SyntaxKind::{self},
|
||||
SyntaxNode, T,
|
||||
NodeOrToken, SyntaxKind, SyntaxNode, T,
|
||||
};
|
||||
|
||||
use crate::{utils::is_body_const, AssistContext, AssistId, AssistKind, Assists};
|
||||
|
@ -46,6 +44,23 @@ use crate::{utils::is_body_const, AssistContext, AssistId, AssistKind, Assists};
|
|||
// VAR_NAME * 4;
|
||||
// }
|
||||
// ```
|
||||
|
||||
// Assist: extract_static
|
||||
//
|
||||
// Extracts subexpression into a static.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// $0(1 + 2)$0 * 4;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// static $0VAR_NAME: i32 = 1 + 2;
|
||||
// VAR_NAME * 4;
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||
let node = if ctx.has_empty_selection() {
|
||||
if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) {
|
||||
|
@ -114,15 +129,20 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
|||
let Some(anchor) = Anchor::from(&to_extract, kind) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let ty_string = match kind {
|
||||
ExtractionKind::Constant => {
|
||||
ExtractionKind::Constant | ExtractionKind::Static => {
|
||||
let Some(ty) = ty.clone() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// We can't mutably reference a const, nor can we define
|
||||
// one using a non-const expression or one of unknown type
|
||||
if needs_mut || !is_body_const(&ctx.sema, &to_extract_no_ref) || ty.is_unknown() {
|
||||
if needs_mut
|
||||
|| !is_body_const(&ctx.sema, &to_extract_no_ref)
|
||||
|| ty.is_unknown()
|
||||
|| ty.is_mutable_reference()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -135,92 +155,111 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
|||
_ => "".to_owned(),
|
||||
};
|
||||
|
||||
acc.add(kind.assist_id(), kind.label(), target, |edit| {
|
||||
let (var_name, expr_replace) = kind.get_name_and_expr(ctx, &to_extract);
|
||||
acc.add_group(
|
||||
&GroupLabel("Extract into...".to_owned()),
|
||||
kind.assist_id(),
|
||||
kind.label(),
|
||||
target,
|
||||
|edit| {
|
||||
let (var_name, expr_replace) = kind.get_name_and_expr(ctx, &to_extract);
|
||||
|
||||
let make = SyntaxFactory::new();
|
||||
let mut editor = edit.make_editor(&expr_replace);
|
||||
let make = SyntaxFactory::new();
|
||||
let mut editor = edit.make_editor(&expr_replace);
|
||||
|
||||
let pat_name = make.name(&var_name);
|
||||
let name_expr = make.expr_path(make::ext::ident_path(&var_name));
|
||||
let pat_name = make.name(&var_name);
|
||||
let name_expr = make.expr_path(make::ext::ident_path(&var_name));
|
||||
|
||||
if let Some(cap) = ctx.config.snippet_cap {
|
||||
let tabstop = edit.make_tabstop_before(cap);
|
||||
editor.add_annotation(pat_name.syntax().clone(), tabstop);
|
||||
}
|
||||
|
||||
let initializer = match ty.as_ref().filter(|_| needs_ref) {
|
||||
Some(receiver_type) if receiver_type.is_mutable_reference() => {
|
||||
make.expr_ref(to_extract_no_ref.clone(), true)
|
||||
if let Some(cap) = ctx.config.snippet_cap {
|
||||
let tabstop = edit.make_tabstop_before(cap);
|
||||
editor.add_annotation(pat_name.syntax().clone(), tabstop);
|
||||
}
|
||||
Some(receiver_type) if receiver_type.is_reference() => {
|
||||
make.expr_ref(to_extract_no_ref.clone(), false)
|
||||
}
|
||||
_ => to_extract_no_ref.clone(),
|
||||
};
|
||||
|
||||
let new_stmt: ast::Stmt = match kind {
|
||||
ExtractionKind::Variable => {
|
||||
let ident_pat = make.ident_pat(false, needs_mut, pat_name);
|
||||
make.let_stmt(ident_pat.into(), None, Some(initializer)).into()
|
||||
}
|
||||
ExtractionKind::Constant => {
|
||||
let ast_ty = make.ty(&ty_string);
|
||||
ast::Item::Const(make.item_const(None, pat_name, ast_ty, initializer)).into()
|
||||
}
|
||||
};
|
||||
let initializer = match ty.as_ref().filter(|_| needs_ref) {
|
||||
Some(receiver_type) if receiver_type.is_mutable_reference() => {
|
||||
make.expr_ref(to_extract_no_ref.clone(), true)
|
||||
}
|
||||
Some(receiver_type) if receiver_type.is_reference() => {
|
||||
make.expr_ref(to_extract_no_ref.clone(), false)
|
||||
}
|
||||
_ => to_extract_no_ref.clone(),
|
||||
};
|
||||
|
||||
match &anchor {
|
||||
Anchor::Before(place) => {
|
||||
let prev_ws = place.prev_sibling_or_token().and_then(|it| it.into_token());
|
||||
let indent_to = IndentLevel::from_node(place);
|
||||
let new_stmt: ast::Stmt = match kind {
|
||||
ExtractionKind::Variable => {
|
||||
let ident_pat = make.ident_pat(false, needs_mut, pat_name);
|
||||
make.let_stmt(ident_pat.into(), None, Some(initializer)).into()
|
||||
}
|
||||
ExtractionKind::Constant => {
|
||||
let ast_ty = make.ty(&ty_string);
|
||||
ast::Item::Const(make.item_const(None, pat_name, ast_ty, initializer))
|
||||
.into()
|
||||
}
|
||||
ExtractionKind::Static => {
|
||||
let ast_ty = make.ty(&ty_string);
|
||||
ast::Item::Static(make.item_static(
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
pat_name,
|
||||
ast_ty,
|
||||
Some(initializer),
|
||||
))
|
||||
.into()
|
||||
}
|
||||
};
|
||||
|
||||
// Adjust ws to insert depending on if this is all inline or on separate lines
|
||||
let trailing_ws = if prev_ws.is_some_and(|it| it.text().starts_with('\n')) {
|
||||
format!("\n{indent_to}")
|
||||
} else {
|
||||
" ".to_owned()
|
||||
};
|
||||
match &anchor {
|
||||
Anchor::Before(place) => {
|
||||
let prev_ws = place.prev_sibling_or_token().and_then(|it| it.into_token());
|
||||
let indent_to = IndentLevel::from_node(place);
|
||||
|
||||
editor.insert_all(
|
||||
Position::before(place),
|
||||
vec![
|
||||
new_stmt.syntax().clone().into(),
|
||||
make::tokens::whitespace(&trailing_ws).into(),
|
||||
],
|
||||
);
|
||||
// Adjust ws to insert depending on if this is all inline or on separate lines
|
||||
let trailing_ws = if prev_ws.is_some_and(|it| it.text().starts_with('\n')) {
|
||||
format!("\n{indent_to}")
|
||||
} else {
|
||||
" ".to_owned()
|
||||
};
|
||||
|
||||
editor.replace(expr_replace, name_expr.syntax());
|
||||
}
|
||||
Anchor::Replace(stmt) => {
|
||||
cov_mark::hit!(test_extract_var_expr_stmt);
|
||||
editor.insert_all(
|
||||
Position::before(place),
|
||||
vec![
|
||||
new_stmt.syntax().clone().into(),
|
||||
make::tokens::whitespace(&trailing_ws).into(),
|
||||
],
|
||||
);
|
||||
|
||||
editor.replace(stmt.syntax(), new_stmt.syntax());
|
||||
}
|
||||
Anchor::WrapInBlock(to_wrap) => {
|
||||
let indent_to = to_wrap.indent_level();
|
||||
|
||||
let block = if to_wrap.syntax() == &expr_replace {
|
||||
// Since `expr_replace` is the same that needs to be wrapped in a block,
|
||||
// we can just directly replace it with a block
|
||||
make.block_expr([new_stmt], Some(name_expr))
|
||||
} else {
|
||||
// `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`.
|
||||
editor.replace(expr_replace, name_expr.syntax());
|
||||
make.block_expr([new_stmt], Some(to_wrap.clone()))
|
||||
};
|
||||
}
|
||||
Anchor::Replace(stmt) => {
|
||||
cov_mark::hit!(test_extract_var_expr_stmt);
|
||||
|
||||
editor.replace(to_wrap.syntax(), block.syntax());
|
||||
editor.replace(stmt.syntax(), new_stmt.syntax());
|
||||
}
|
||||
Anchor::WrapInBlock(to_wrap) => {
|
||||
let indent_to = to_wrap.indent_level();
|
||||
|
||||
// fixup indentation of block
|
||||
block.indent(indent_to);
|
||||
let block = if to_wrap.syntax() == &expr_replace {
|
||||
// Since `expr_replace` is the same that needs to be wrapped in a block,
|
||||
// we can just directly replace it with a block
|
||||
make.block_expr([new_stmt], Some(name_expr))
|
||||
} else {
|
||||
// `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`.
|
||||
editor.replace(expr_replace, name_expr.syntax());
|
||||
make.block_expr([new_stmt], Some(to_wrap.clone()))
|
||||
};
|
||||
|
||||
editor.replace(to_wrap.syntax(), block.syntax());
|
||||
|
||||
// fixup indentation of block
|
||||
block.indent(indent_to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editor.add_mappings(make.finish_with_mappings());
|
||||
edit.add_file_edits(ctx.file_id(), editor);
|
||||
edit.rename();
|
||||
});
|
||||
editor.add_mappings(make.finish_with_mappings());
|
||||
edit.add_file_edits(ctx.file_id(), editor);
|
||||
edit.rename();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Some(())
|
||||
|
@ -251,15 +290,18 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
|
|||
enum ExtractionKind {
|
||||
Variable,
|
||||
Constant,
|
||||
Static,
|
||||
}
|
||||
|
||||
impl ExtractionKind {
|
||||
const ALL: &'static [ExtractionKind] = &[ExtractionKind::Variable, ExtractionKind::Constant];
|
||||
const ALL: &'static [ExtractionKind] =
|
||||
&[ExtractionKind::Variable, ExtractionKind::Constant, ExtractionKind::Static];
|
||||
|
||||
fn assist_id(&self) -> AssistId {
|
||||
let s = match self {
|
||||
ExtractionKind::Variable => "extract_variable",
|
||||
ExtractionKind::Constant => "extract_constant",
|
||||
ExtractionKind::Static => "extract_static",
|
||||
};
|
||||
|
||||
AssistId(s, AssistKind::RefactorExtract)
|
||||
|
@ -269,6 +311,7 @@ impl ExtractionKind {
|
|||
match self {
|
||||
ExtractionKind::Variable => "Extract into variable",
|
||||
ExtractionKind::Constant => "Extract into constant",
|
||||
ExtractionKind::Static => "Extract into static",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,7 +334,7 @@ impl ExtractionKind {
|
|||
|
||||
let var_name = match self {
|
||||
ExtractionKind::Variable => var_name,
|
||||
ExtractionKind::Constant => var_name.to_uppercase(),
|
||||
ExtractionKind::Constant | ExtractionKind::Static => var_name.to_uppercase(),
|
||||
};
|
||||
|
||||
(var_name, expr_replace)
|
||||
|
@ -351,7 +394,7 @@ impl Anchor {
|
|||
});
|
||||
|
||||
match kind {
|
||||
ExtractionKind::Constant if result.is_none() => {
|
||||
ExtractionKind::Constant | ExtractionKind::Static if result.is_none() => {
|
||||
to_extract.syntax().ancestors().find_map(|node| {
|
||||
let item = ast::Item::cast(node.clone())?;
|
||||
let parent = item.syntax().parent()?;
|
||||
|
@ -381,21 +424,6 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn now_bad() {
|
||||
// unknown type
|
||||
check_assist_not_applicable_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn main() {
|
||||
let a = Some(2);
|
||||
a.is_some();$0
|
||||
}
|
||||
"#,
|
||||
"Extract into constant",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_var_simple_without_select() {
|
||||
check_assist_by_label(
|
||||
|
@ -604,7 +632,102 @@ fn main() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn extract_var_unit_expr_without_select_not_applicable() {
|
||||
fn extract_static_simple_without_select() {
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn main() -> i32 {
|
||||
if true {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}$0
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() -> i32 {
|
||||
static $0VAR_NAME: i32 = if true {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
};
|
||||
VAR_NAME
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
const fn foo() -> i32 { 1 }
|
||||
fn main() {
|
||||
foo();$0
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
const fn foo() -> i32 { 1 }
|
||||
fn main() {
|
||||
static $0FOO: i32 = foo();
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn main() {
|
||||
"hello"$0;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
static $0VAR_NAME: &str = "hello";
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn main() {
|
||||
1 + 2$0;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
static $0VAR_NAME: i32 = 1 + 2;
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn main() {
|
||||
match () {
|
||||
() if true => 1,
|
||||
_ => 2,
|
||||
};$0
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
static $0VAR_NAME: i32 = match () {
|
||||
() if true => 1,
|
||||
_ => 2,
|
||||
};
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_extract_unit_expr_without_select() {
|
||||
check_assist_not_applicable(
|
||||
extract_variable,
|
||||
r#"
|
||||
|
@ -664,7 +787,24 @@ fn foo() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn extract_var_in_comment_is_not_applicable() {
|
||||
fn extract_static_simple() {
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn foo() {
|
||||
foo($01 + 1$0);
|
||||
}"#,
|
||||
r#"
|
||||
fn foo() {
|
||||
static $0VAR_NAME: i32 = 1 + 1;
|
||||
foo(VAR_NAME);
|
||||
}"#,
|
||||
"Extract into static",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_extract_in_comment() {
|
||||
cov_mark::check!(extract_var_in_comment_is_not_applicable);
|
||||
check_assist_not_applicable(extract_variable, r#"fn main() { 1 + /* $0comment$0 */ 1; }"#);
|
||||
}
|
||||
|
@ -732,6 +872,38 @@ fn foo() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_static_expr_stmt() {
|
||||
cov_mark::check!(test_extract_var_expr_stmt);
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn foo() {
|
||||
$0 1 + 1$0;
|
||||
}"#,
|
||||
r#"
|
||||
fn foo() {
|
||||
static $0VAR_NAME: i32 = 1 + 1;
|
||||
}"#,
|
||||
"Extract into static",
|
||||
);
|
||||
// This is hilarious but as far as I know, it's valid
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn foo() {
|
||||
$0{ let x = 0; x }$0;
|
||||
something_else();
|
||||
}"#,
|
||||
r#"
|
||||
fn foo() {
|
||||
static $0VAR_NAME: i32 = { let x = 0; x };
|
||||
something_else();
|
||||
}"#,
|
||||
"Extract into static",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_var_part_of_expr_stmt() {
|
||||
check_assist_by_label(
|
||||
|
@ -766,6 +938,23 @@ fn foo() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_static_part_of_expr_stmt() {
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn foo() {
|
||||
$01$0 + 1;
|
||||
}"#,
|
||||
r#"
|
||||
fn foo() {
|
||||
static $0VAR_NAME: i32 = 1;
|
||||
VAR_NAME + 1;
|
||||
}"#,
|
||||
"Extract into static",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_var_last_expr() {
|
||||
cov_mark::check!(test_extract_var_last_expr);
|
||||
|
@ -852,6 +1041,49 @@ const fn bar(i: i32) -> i32 {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_static_last_expr() {
|
||||
cov_mark::check!(test_extract_var_last_expr);
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn foo() {
|
||||
bar($01 + 1$0)
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn foo() {
|
||||
static $0VAR_NAME: i32 = 1 + 1;
|
||||
bar(VAR_NAME)
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn foo() -> i32 {
|
||||
$0bar(1 + 1)$0
|
||||
}
|
||||
|
||||
const fn bar(i: i32) -> i32 {
|
||||
i
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn foo() -> i32 {
|
||||
static $0BAR: i32 = bar(1 + 1);
|
||||
BAR
|
||||
}
|
||||
|
||||
const fn bar(i: i32) -> i32 {
|
||||
i
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_var_in_match_arm_no_block() {
|
||||
cov_mark::check!(test_extract_var_in_match_arm_no_block);
|
||||
|
@ -1427,6 +1659,30 @@ fn main() {
|
|||
"#,
|
||||
"Extract into constant",
|
||||
);
|
||||
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
struct Vec;
|
||||
macro_rules! vec {
|
||||
() => {Vec}
|
||||
}
|
||||
fn main() {
|
||||
let _ = $0vec![]$0;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Vec;
|
||||
macro_rules! vec {
|
||||
() => {Vec}
|
||||
}
|
||||
fn main() {
|
||||
static $0VEC: Vec = vec![];
|
||||
let _ = VEC;
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1589,6 +1845,109 @@ fn bar() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_static_no_block_body() {
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
const fn foo(x: i32) -> i32 {
|
||||
x
|
||||
}
|
||||
|
||||
const FOO: i32 = foo($0100$0);
|
||||
"#,
|
||||
r#"
|
||||
const fn foo(x: i32) -> i32 {
|
||||
x
|
||||
}
|
||||
|
||||
static $0X: i32 = 100;
|
||||
const FOO: i32 = foo(X);
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
mod foo {
|
||||
enum Foo {
|
||||
Bar,
|
||||
Baz = $042$0,
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
mod foo {
|
||||
static $0VAR_NAME: isize = 42;
|
||||
enum Foo {
|
||||
Bar,
|
||||
Baz = VAR_NAME,
|
||||
}
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
const fn foo(x: i32) -> i32 {
|
||||
x
|
||||
}
|
||||
|
||||
trait Hello {
|
||||
const World: i32;
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
impl Hello for Bar {
|
||||
const World = foo($042$0);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
const fn foo(x: i32) -> i32 {
|
||||
x
|
||||
}
|
||||
|
||||
trait Hello {
|
||||
const World: i32;
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
impl Hello for Bar {
|
||||
static $0X: i32 = 42;
|
||||
const World = foo(X);
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
|
||||
check_assist_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
const fn foo(x: i32) -> i32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
const BAZ: i32 = foo($042$0);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
const fn foo(x: i32) -> i32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
static $0X: i32 = 42;
|
||||
const BAZ: i32 = foo(X);
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_var_mutable_reference_parameter() {
|
||||
check_assist_by_label(
|
||||
|
@ -1641,7 +2000,28 @@ impl<T> Vec<T> {
|
|||
fn foo(s: &mut S) {
|
||||
$0s.vec$0.push(0);
|
||||
}"#,
|
||||
"Extract into const",
|
||||
"Extract into constant",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_extract_static_mutable_reference_parameter() {
|
||||
check_assist_not_applicable_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
struct S {
|
||||
vec: Vec<u8>
|
||||
}
|
||||
|
||||
struct Vec<T>;
|
||||
impl<T> Vec<T> {
|
||||
fn push(&mut self, _:usize) {}
|
||||
}
|
||||
|
||||
fn foo(s: &mut S) {
|
||||
$0s.vec$0.push(0);
|
||||
}"#,
|
||||
"Extract into static",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2030,6 +2410,18 @@ fn foo() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_extract_static_for_mutable_borrow() {
|
||||
check_assist_not_applicable_by_label(
|
||||
extract_variable,
|
||||
r#"
|
||||
fn foo() {
|
||||
let v = &mut $00$0;
|
||||
}"#,
|
||||
"Extract into static",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generates_no_ref_on_calls() {
|
||||
check_assist_by_label(
|
||||
|
|
|
@ -362,8 +362,7 @@ pub fn test_some_range(a: int) -> bool {
|
|||
|
||||
expect![[r#"
|
||||
Convert integer base
|
||||
Extract into variable
|
||||
Extract into constant
|
||||
Extract into...
|
||||
Extract into function
|
||||
Replace if let with match
|
||||
"#]]
|
||||
|
@ -392,8 +391,7 @@ pub fn test_some_range(a: int) -> bool {
|
|||
|
||||
expect![[r#"
|
||||
Convert integer base
|
||||
Extract into variable
|
||||
Extract into constant
|
||||
Extract into...
|
||||
Extract into function
|
||||
Replace if let with match
|
||||
"#]]
|
||||
|
@ -407,8 +405,7 @@ pub fn test_some_range(a: int) -> bool {
|
|||
let expected = labels(&assists);
|
||||
|
||||
expect![[r#"
|
||||
Extract into variable
|
||||
Extract into constant
|
||||
Extract into...
|
||||
Extract into function
|
||||
"#]]
|
||||
.assert_eq(&expected);
|
||||
|
@ -443,7 +440,7 @@ pub fn test_some_range(a: int) -> bool {
|
|||
|
||||
{
|
||||
let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange.into());
|
||||
assert_eq!(3, assists.len());
|
||||
assert_eq!(4, assists.len());
|
||||
let mut assists = assists.into_iter();
|
||||
|
||||
let extract_into_variable_assist = assists.next().unwrap();
|
||||
|
@ -454,7 +451,11 @@ pub fn test_some_range(a: int) -> bool {
|
|||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into variable",
|
||||
group: None,
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: None,
|
||||
command: None,
|
||||
|
@ -470,7 +471,11 @@ pub fn test_some_range(a: int) -> bool {
|
|||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into constant",
|
||||
group: None,
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: None,
|
||||
command: None,
|
||||
|
@ -478,6 +483,26 @@ pub fn test_some_range(a: int) -> bool {
|
|||
"#]]
|
||||
.assert_debug_eq(&extract_into_constant_assist);
|
||||
|
||||
let extract_into_static_assist = assists.next().unwrap();
|
||||
expect![[r#"
|
||||
Assist {
|
||||
id: AssistId(
|
||||
"extract_static",
|
||||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into static",
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: None,
|
||||
command: None,
|
||||
}
|
||||
"#]]
|
||||
.assert_debug_eq(&extract_into_static_assist);
|
||||
|
||||
let extract_into_function_assist = assists.next().unwrap();
|
||||
expect![[r#"
|
||||
Assist {
|
||||
|
@ -505,7 +530,7 @@ pub fn test_some_range(a: int) -> bool {
|
|||
}),
|
||||
frange.into(),
|
||||
);
|
||||
assert_eq!(3, assists.len());
|
||||
assert_eq!(4, assists.len());
|
||||
let mut assists = assists.into_iter();
|
||||
|
||||
let extract_into_variable_assist = assists.next().unwrap();
|
||||
|
@ -516,7 +541,11 @@ pub fn test_some_range(a: int) -> bool {
|
|||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into variable",
|
||||
group: None,
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: None,
|
||||
command: None,
|
||||
|
@ -532,7 +561,11 @@ pub fn test_some_range(a: int) -> bool {
|
|||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into constant",
|
||||
group: None,
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: None,
|
||||
command: None,
|
||||
|
@ -540,6 +573,26 @@ pub fn test_some_range(a: int) -> bool {
|
|||
"#]]
|
||||
.assert_debug_eq(&extract_into_constant_assist);
|
||||
|
||||
let extract_into_static_assist = assists.next().unwrap();
|
||||
expect![[r#"
|
||||
Assist {
|
||||
id: AssistId(
|
||||
"extract_static",
|
||||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into static",
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: None,
|
||||
command: None,
|
||||
}
|
||||
"#]]
|
||||
.assert_debug_eq(&extract_into_static_assist);
|
||||
|
||||
let extract_into_function_assist = assists.next().unwrap();
|
||||
expect![[r#"
|
||||
Assist {
|
||||
|
@ -567,7 +620,7 @@ pub fn test_some_range(a: int) -> bool {
|
|||
}),
|
||||
frange.into(),
|
||||
);
|
||||
assert_eq!(3, assists.len());
|
||||
assert_eq!(4, assists.len());
|
||||
let mut assists = assists.into_iter();
|
||||
|
||||
let extract_into_variable_assist = assists.next().unwrap();
|
||||
|
@ -578,7 +631,11 @@ pub fn test_some_range(a: int) -> bool {
|
|||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into variable",
|
||||
group: None,
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: Some(
|
||||
SourceChange {
|
||||
|
@ -637,7 +694,11 @@ pub fn test_some_range(a: int) -> bool {
|
|||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into constant",
|
||||
group: None,
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: None,
|
||||
command: None,
|
||||
|
@ -645,6 +706,26 @@ pub fn test_some_range(a: int) -> bool {
|
|||
"#]]
|
||||
.assert_debug_eq(&extract_into_constant_assist);
|
||||
|
||||
let extract_into_static_assist = assists.next().unwrap();
|
||||
expect![[r#"
|
||||
Assist {
|
||||
id: AssistId(
|
||||
"extract_static",
|
||||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into static",
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: None,
|
||||
command: None,
|
||||
}
|
||||
"#]]
|
||||
.assert_debug_eq(&extract_into_static_assist);
|
||||
|
||||
let extract_into_function_assist = assists.next().unwrap();
|
||||
expect![[r#"
|
||||
Assist {
|
||||
|
@ -664,7 +745,7 @@ pub fn test_some_range(a: int) -> bool {
|
|||
|
||||
{
|
||||
let assists = assists(&db, &cfg, AssistResolveStrategy::All, frange.into());
|
||||
assert_eq!(3, assists.len());
|
||||
assert_eq!(4, assists.len());
|
||||
let mut assists = assists.into_iter();
|
||||
|
||||
let extract_into_variable_assist = assists.next().unwrap();
|
||||
|
@ -675,7 +756,11 @@ pub fn test_some_range(a: int) -> bool {
|
|||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into variable",
|
||||
group: None,
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: Some(
|
||||
SourceChange {
|
||||
|
@ -734,7 +819,11 @@ pub fn test_some_range(a: int) -> bool {
|
|||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into constant",
|
||||
group: None,
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: Some(
|
||||
SourceChange {
|
||||
|
@ -789,6 +878,73 @@ pub fn test_some_range(a: int) -> bool {
|
|||
"#]]
|
||||
.assert_debug_eq(&extract_into_constant_assist);
|
||||
|
||||
let extract_into_static_assist = assists.next().unwrap();
|
||||
expect![[r#"
|
||||
Assist {
|
||||
id: AssistId(
|
||||
"extract_static",
|
||||
RefactorExtract,
|
||||
),
|
||||
label: "Extract into static",
|
||||
group: Some(
|
||||
GroupLabel(
|
||||
"Extract into...",
|
||||
),
|
||||
),
|
||||
target: 59..60,
|
||||
source_change: Some(
|
||||
SourceChange {
|
||||
source_file_edits: {
|
||||
FileId(
|
||||
0,
|
||||
): (
|
||||
TextEdit {
|
||||
indels: [
|
||||
Indel {
|
||||
insert: "static",
|
||||
delete: 45..47,
|
||||
},
|
||||
Indel {
|
||||
insert: "VAR_NAME:",
|
||||
delete: 48..60,
|
||||
},
|
||||
Indel {
|
||||
insert: "i32",
|
||||
delete: 61..81,
|
||||
},
|
||||
Indel {
|
||||
insert: "=",
|
||||
delete: 82..86,
|
||||
},
|
||||
Indel {
|
||||
insert: "5;\n if let 2..6 = VAR_NAME {\n true\n } else {\n false\n }",
|
||||
delete: 87..108,
|
||||
},
|
||||
],
|
||||
},
|
||||
Some(
|
||||
SnippetEdit(
|
||||
[
|
||||
(
|
||||
0,
|
||||
52..52,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
file_system_edits: [],
|
||||
is_snippet: true,
|
||||
},
|
||||
),
|
||||
command: Some(
|
||||
Rename,
|
||||
),
|
||||
}
|
||||
"#]]
|
||||
.assert_debug_eq(&extract_into_static_assist);
|
||||
|
||||
let extract_into_function_assist = assists.next().unwrap();
|
||||
expect![[r#"
|
||||
Assist {
|
||||
|
|
|
@ -1024,6 +1024,24 @@ fn bar(name: i32) -> i32 {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_extract_static() {
|
||||
check_doc_test(
|
||||
"extract_static",
|
||||
r#####"
|
||||
fn main() {
|
||||
$0(1 + 2)$0 * 4;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
static $0VAR_NAME: i32 = 1 + 2;
|
||||
VAR_NAME * 4;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_extract_struct_from_enum_variant() {
|
||||
check_doc_test(
|
||||
|
|
Loading…
Reference in a new issue