Skip to content

Commit bfc0da6

Browse files
committed
Add cfg(not()) to else branch
1 parent 477388d commit bfc0da6

File tree

1 file changed

+249
-17
lines changed

1 file changed

+249
-17
lines changed

crates/ide-assists/src/handlers/convert_attr_cfg_to_if.rs

Lines changed: 249 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use either::Either::{self, Left, Right};
22
use ide_db::assists::AssistId;
3+
use itertools::Itertools;
4+
use syntax::NodeOrToken::{Node, Token};
5+
use syntax::SyntaxNode;
36
use syntax::ast::edit_in_place::Indent;
47
use syntax::syntax_editor::SyntaxEditor;
58
use syntax::{
@@ -30,13 +33,14 @@ use crate::assist_context::{AssistContext, Assists};
3033
// }
3134
// ```
3235
pub(crate) fn convert_attr_cfg_to_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
33-
let (origin, cfg, stmt) = find_stmt(ctx)?;
36+
let (origin, cfg, stmt) = find_stmt(ctx.find_node_at_offset::<ast::Attr>()?)?;
3437

35-
if cfg.path()?.as_single_name_ref()?.text() != "cfg" {
38+
if !is_cfg(&cfg) {
3639
return None;
3740
}
3841

39-
let cfg_expr = make::expr_macro(cfg.path()?, cfg.token_tree()?).into();
42+
let cfg_tt = cfg.token_tree()?;
43+
let cfg_expr = make::expr_macro(cfg.path()?, cfg_tt.clone()).into();
4044

4145
acc.add(
4246
AssistId::refactor_rewrite("convert_attr_cfg_to_if"),
@@ -45,14 +49,10 @@ pub(crate) fn convert_attr_cfg_to_if(acc: &mut Assists, ctx: &AssistContext<'_>)
4549
|builder| {
4650
let mut edit = builder.make_editor(origin.syntax());
4751

48-
let block = match stmt {
49-
Left(stmt_list) => {
50-
remove_cfg(&cfg, &mut edit);
51-
make_block(stmt_list.statements(), stmt_list.tail_expr())
52-
}
53-
Right(stmt) => make_block([stmt], None),
54-
};
55-
let if_expr = make::expr_if(cfg_expr, block, None).clone_for_update();
52+
let block = process_stmt(&cfg, stmt, &mut edit);
53+
let else_branch = take_else_branch(&origin, cfg_tt, &mut edit);
54+
let if_expr = make::expr_if(cfg_expr, block, else_branch).clone_for_update();
55+
5656
if_expr.indent(origin.indent_level());
5757
edit.replace(origin.syntax(), if_expr.syntax());
5858

@@ -61,6 +61,21 @@ pub(crate) fn convert_attr_cfg_to_if(acc: &mut Assists, ctx: &AssistContext<'_>)
6161
)
6262
}
6363

64+
fn take_else_branch(
65+
origin: &impl AstNode,
66+
cfg_tt: ast::TokenTree,
67+
edit: &mut SyntaxEditor,
68+
) -> Option<ast::ElseBranch> {
69+
let attr_cfg_not = next_attr_cfg_not(origin, &cfg_tt)?;
70+
let (else_origin, else_cfg, else_stmt) = find_stmt(attr_cfg_not)?;
71+
let block = process_stmt(&else_cfg, else_stmt, edit);
72+
73+
remove_next_ws(origin.syntax(), edit);
74+
edit.delete(else_origin.syntax());
75+
76+
Some(ast::ElseBranch::Block(block))
77+
}
78+
6479
fn make_block(
6580
stmts: impl IntoIterator<Item = ast::Stmt>,
6681
tail_expr: Option<ast::Expr>,
@@ -71,15 +86,27 @@ fn make_block(
7186
make::block_expr(stmts, tail_expr)
7287
}
7388

89+
fn process_stmt(
90+
cfg: &ast::Attr,
91+
stmt: Either<ast::StmtList, ast::Stmt>,
92+
edit: &mut SyntaxEditor,
93+
) -> ast::BlockExpr {
94+
match stmt {
95+
Left(stmt_list) => {
96+
remove_cfg(cfg, edit);
97+
make_block(stmt_list.statements(), stmt_list.tail_expr())
98+
}
99+
Right(stmt) => make_block([stmt], None),
100+
}
101+
}
102+
74103
fn find_stmt(
75-
ctx: &AssistContext<'_>,
104+
attr: ast::Attr,
76105
) -> Option<(impl Indent, ast::Attr, Either<ast::StmtList, ast::Stmt>)> {
77-
let attr = ctx.find_node_at_offset::<ast::Attr>()?;
78-
79106
if let Some(stmt_list) = find_stmt_list(&attr) {
80107
let new_stmt_list = stmt_list.clone_for_update();
81108
new_stmt_list.dedent(stmt_list.indent_level());
82-
return Some((Left(stmt_list), attr, Left(new_stmt_list)));
109+
return Some((Left(stmt_list), attr.clone(), Left(new_stmt_list)));
83110
}
84111

85112
let node =
@@ -115,13 +142,49 @@ fn find_stmt_list(attr: &ast::Attr) -> Option<ast::StmtList> {
115142
AstNode::cast(node)
116143
}
117144

145+
fn next_attr_cfg_not(node: &impl AstNode, cond: &ast::TokenTree) -> Option<ast::Attr> {
146+
let attr = node
147+
.syntax()
148+
.ancestors()
149+
.filter_map(|node| node.next_sibling())
150+
.flat_map(|x| x.descendants())
151+
.filter_map(ast::Attr::cast)
152+
.find(is_cfg)?;
153+
154+
let tts = attr
155+
.token_tree()?
156+
.token_trees_and_tokens()
157+
.filter(|tt| tt.as_token().is_none_or(|t| !t.kind().is_trivia()))
158+
.collect_array()?;
159+
if let [Token(_lparen), Token(not), Node(not_cond), Token(_rparen)] = tts
160+
&& not.text() == "not"
161+
&& not_cond.syntax().text() == cond.syntax().text()
162+
{
163+
Some(attr)
164+
} else {
165+
None
166+
}
167+
}
168+
118169
fn remove_cfg(cfg: &ast::Attr, edit: &mut SyntaxEditor) {
119-
if let Some(next) = cfg.syntax().last_token().and_then(|it| it.next_token())
170+
remove_next_ws(cfg.syntax(), edit);
171+
edit.delete(cfg.syntax());
172+
}
173+
174+
fn remove_next_ws(node: &SyntaxNode, edit: &mut SyntaxEditor) {
175+
if let Some(Token(next)) = node.next_sibling_or_token()
120176
&& next.kind() == SyntaxKind::WHITESPACE
121177
{
122178
edit.delete(next);
179+
} else if let Some(parent) = node.parent()
180+
&& parent.last_child().is_some_and(|it| it == *node)
181+
{
182+
remove_next_ws(&parent, edit);
123183
}
124-
edit.delete(cfg.syntax());
184+
}
185+
186+
fn is_cfg(attr: &ast::Attr) -> bool {
187+
attr.path().and_then(|p| p.as_single_name_ref()).is_some_and(|name| name.text() == "cfg")
125188
}
126189

127190
#[cfg(test)]
@@ -154,6 +217,38 @@ fn foo() {
154217
);
155218
}
156219

220+
#[test]
221+
fn test_stmt_list_else() {
222+
check_assist(
223+
convert_attr_cfg_to_if,
224+
r#"
225+
fn foo() {
226+
$0#[cfg(feature = "foo")]
227+
{
228+
let x = 2;
229+
let _ = x+1;
230+
}
231+
#[cfg(not(feature = "foo"))]
232+
{
233+
let _ = 3;
234+
}
235+
// needless comment
236+
}
237+
"#,
238+
r#"
239+
fn foo() {
240+
if cfg!(feature = "foo") {
241+
let x = 2;
242+
let _ = x+1;
243+
} else {
244+
let _ = 3;
245+
}
246+
// needless comment
247+
}
248+
"#,
249+
);
250+
}
251+
157252
#[test]
158253
fn test_expr_stmt() {
159254
check_assist(
@@ -218,6 +313,35 @@ fn foo() {
218313
#[allow(unused)]
219314
bar();
220315
}
316+
}
317+
"#,
318+
);
319+
320+
check_assist(
321+
convert_attr_cfg_to_if,
322+
r#"
323+
fn bar() {}
324+
fn baz() {}
325+
fn foo() {
326+
#[allow(unused)]
327+
$0#[cfg(feature = "foo")]
328+
bar();
329+
#[allow(unused)]
330+
#[cfg(not(feature = "foo"))]
331+
baz();
332+
}
333+
"#,
334+
r#"
335+
fn bar() {}
336+
fn baz() {}
337+
fn foo() {
338+
if cfg!(feature = "foo") {
339+
#[allow(unused)]
340+
bar();
341+
} else {
342+
#[allow(unused)]
343+
baz();
344+
}
221345
}
222346
"#,
223347
);
@@ -264,6 +388,73 @@ mod a {
264388
}
265389
}
266390
}
391+
}
392+
"#,
393+
);
394+
395+
check_assist(
396+
convert_attr_cfg_to_if,
397+
r#"
398+
mod a {
399+
fn foo() {
400+
#[allow(unused)]
401+
$0#[cfg(feature = "foo")]
402+
{
403+
let _ = match () {
404+
() => {
405+
todo!()
406+
},
407+
};
408+
match () {
409+
() => {
410+
todo!()
411+
},
412+
}
413+
}
414+
#[cfg(not(feature = "foo"))]
415+
{
416+
let _ = match () {
417+
() => {
418+
todo!("")
419+
},
420+
};
421+
match () {
422+
() => {
423+
todo!("")
424+
},
425+
}
426+
}
427+
}
428+
}
429+
"#,
430+
r#"
431+
mod a {
432+
fn foo() {
433+
#[allow(unused)]
434+
if cfg!(feature = "foo") {
435+
let _ = match () {
436+
() => {
437+
todo!()
438+
},
439+
};
440+
match () {
441+
() => {
442+
todo!()
443+
},
444+
}
445+
} else {
446+
let _ = match () {
447+
() => {
448+
todo!("")
449+
},
450+
};
451+
match () {
452+
() => {
453+
todo!("")
454+
},
455+
}
456+
}
457+
}
267458
}
268459
"#,
269460
);
@@ -327,6 +518,47 @@ mod a {
327518
};
328519
}
329520
}
521+
}
522+
"#,
523+
);
524+
525+
check_assist(
526+
convert_attr_cfg_to_if,
527+
r#"
528+
mod a {
529+
fn foo() {
530+
$0#[cfg(feature = "foo")]
531+
match () {
532+
() => {
533+
todo!()
534+
},
535+
};
536+
#[cfg(not(feature = "foo"))]
537+
match () {
538+
() => {
539+
todo!("")
540+
},
541+
};
542+
}
543+
}
544+
"#,
545+
r#"
546+
mod a {
547+
fn foo() {
548+
if cfg!(feature = "foo") {
549+
match () {
550+
() => {
551+
todo!()
552+
},
553+
};
554+
} else {
555+
match () {
556+
() => {
557+
todo!("")
558+
},
559+
};
560+
}
561+
}
330562
}
331563
"#,
332564
);

0 commit comments

Comments
 (0)