Skip to content

Commit abe79f1

Browse files
committed
Add ide-assist: convert_attr_cfg_to_if
Convert `#[cfg(...)] {}` to `if cfg!(...) {}`. ```rust fn foo() { $0#[cfg(feature = "foo")] { let _x = 2; } } ``` -> ```rust fn foo() { if cfg!(feature = "foo") { let _x = 2; } } ```
1 parent caef0f4 commit abe79f1

File tree

3 files changed

+158
-0
lines changed

3 files changed

+158
-0
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use ide_db::assists::AssistId;
2+
use syntax::ast::edit_in_place::Indent;
3+
use syntax::syntax_editor::SyntaxEditor;
4+
use syntax::{
5+
AstNode, SyntaxKind,
6+
ast::{self, make},
7+
};
8+
9+
use crate::assist_context::{AssistContext, Assists};
10+
11+
// Assist: convert_attr_cfg_to_if
12+
//
13+
// Convert `#[cfg(...)] {}` to `if cfg!(...) {}`.
14+
//
15+
// ```
16+
// fn foo() {
17+
// $0#[cfg(feature = "foo")]
18+
// {
19+
// let _x = 2;
20+
// }
21+
// }
22+
// ```
23+
// ->
24+
// ```
25+
// fn foo() {
26+
// if cfg!(feature = "foo") {
27+
// let _x = 2;
28+
// }
29+
// }
30+
// ```
31+
pub(crate) fn convert_attr_cfg_to_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
32+
let cfg = ctx.find_node_at_offset::<ast::Attr>()?;
33+
let stmt_list = find_stmt_list(&cfg)?;
34+
35+
if cfg.path()?.as_single_name_ref()?.text() != "cfg" {
36+
return None;
37+
}
38+
39+
let cfg_expr = make::expr_macro(cfg.path()?, cfg.token_tree()?).into();
40+
41+
acc.add(
42+
AssistId::refactor_rewrite("convert_attr_cfg_to_if"),
43+
"Convert `#[cfg()]` to `if cfg!()`",
44+
cfg.syntax().text_range(),
45+
|builder| {
46+
let mut edit = builder.make_editor(stmt_list.syntax());
47+
48+
remove_cfg(&cfg, &mut edit);
49+
50+
let block = make::block_expr(stmt_list.statements(), None);
51+
let if_expr = make::expr_if(cfg_expr, block, None).clone_for_update();
52+
if_expr.indent(stmt_list.indent_level());
53+
edit.replace(stmt_list.syntax(), if_expr.syntax());
54+
55+
builder.add_file_edits(ctx.vfs_file_id(), edit);
56+
},
57+
)
58+
}
59+
60+
fn find_stmt_list(attr: &ast::Attr) -> Option<ast::StmtList> {
61+
let mut node = attr.syntax().clone();
62+
63+
while node.kind().is_trivia() || node.kind() == SyntaxKind::ATTR {
64+
node = node.next_sibling()?;
65+
}
66+
67+
AstNode::cast(node)
68+
}
69+
70+
fn remove_cfg(cfg: &ast::Attr, edit: &mut SyntaxEditor) {
71+
if let Some(next) = cfg.syntax().last_token().and_then(|it| it.next_token())
72+
&& next.kind() == SyntaxKind::WHITESPACE
73+
{
74+
edit.delete(next);
75+
}
76+
edit.delete(cfg.syntax());
77+
}
78+
79+
#[cfg(test)]
80+
mod tests {
81+
use crate::tests::check_assist;
82+
83+
use super::*;
84+
85+
#[test]
86+
fn test_stmt_list() {
87+
check_assist(
88+
convert_attr_cfg_to_if,
89+
r#"
90+
fn foo() {
91+
$0#[cfg(feature = "foo")]
92+
{
93+
let x = 2;
94+
let _ = x+1;
95+
}
96+
}
97+
"#,
98+
r#"
99+
fn foo() {
100+
if cfg!(feature = "foo") {
101+
let x = 2;
102+
let _ = x+1;
103+
}
104+
}
105+
"#,
106+
);
107+
}
108+
109+
#[test]
110+
fn test_other_attr() {
111+
check_assist(
112+
convert_attr_cfg_to_if,
113+
r#"
114+
fn foo() {
115+
$0#[cfg(feature = "foo")]
116+
#[allow(unused)]
117+
{
118+
let x = 2;
119+
let _ = x+1;
120+
}
121+
}
122+
"#,
123+
r#"
124+
fn foo() {
125+
#[allow(unused)]
126+
if cfg!(feature = "foo") {
127+
let x = 2;
128+
let _ = x+1;
129+
}
130+
}
131+
"#,
132+
);
133+
}
134+
}

crates/ide-assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ mod handlers {
117117
mod auto_import;
118118
mod bind_unused_param;
119119
mod change_visibility;
120+
mod convert_attr_cfg_to_if;
120121
mod convert_bool_then;
121122
mod convert_bool_to_enum;
122123
mod convert_closure_to_fn;
@@ -254,6 +255,7 @@ mod handlers {
254255
convert_bool_then::convert_if_to_bool_then,
255256
convert_bool_to_enum::convert_bool_to_enum,
256257
convert_closure_to_fn::convert_closure_to_fn,
258+
convert_attr_cfg_to_if::convert_attr_cfg_to_if,
257259
convert_comment_block::convert_comment_block,
258260
convert_comment_from_or_to_doc::convert_comment_from_or_to_doc,
259261
convert_for_to_while_let::convert_for_loop_to_while_let,

crates/ide-assists/src/tests/generated.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,28 @@ fn doctest_comment_to_doc() {
355355
)
356356
}
357357

358+
#[test]
359+
fn doctest_convert_attr_cfg_to_if() {
360+
check_doc_test(
361+
"convert_attr_cfg_to_if",
362+
r#####"
363+
fn foo() {
364+
$0#[cfg(feature = "foo")]
365+
{
366+
let _x = 2;
367+
}
368+
}
369+
"#####,
370+
r#####"
371+
fn foo() {
372+
if cfg!(feature = "foo") {
373+
let _x = 2;
374+
}
375+
}
376+
"#####,
377+
)
378+
}
379+
358380
#[test]
359381
fn doctest_convert_bool_then_to_if() {
360382
check_doc_test(

0 commit comments

Comments
 (0)