aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2021-06-18 22:53:41 +0100
committerLukas Wirth <[email protected]>2021-06-18 22:56:43 +0100
commit344cb5e76a31b8ef7ae80ce0ef39c54248ad8df7 (patch)
tree26403f2e9927f32698009cad0d8995ffa9cb058b
parentd9666ce5098442ab0cbac4d4363435074eb12923 (diff)
Don't insert imports outside of cfg attributed items
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs59
-rw-r--r--crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs13
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs70
-rw-r--r--crates/syntax/src/ast/node_ext.rs8
4 files changed, 118 insertions, 32 deletions
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index 6c7348178..8df8b060d 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -103,6 +103,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
103 let scope = match scope.clone() { 103 let scope = match scope.clone() {
104 ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), 104 ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
105 ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), 105 ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
106 ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
106 }; 107 };
107 insert_use(&scope, mod_path_to_ast(&import.import_path), &ctx.config.insert_use); 108 insert_use(&scope, mod_path_to_ast(&import.import_path), &ctx.config.insert_use);
108 }, 109 },
@@ -994,4 +995,62 @@ const _: () = {
994"#, 995"#,
995 ); 996 );
996 } 997 }
998
999 #[test]
1000 fn respects_cfg_attr() {
1001 check_assist(
1002 auto_import,
1003 r#"
1004mod bar {
1005 pub struct Bar;
1006}
1007
1008#[cfg(test)]
1009fn foo() {
1010 Bar$0
1011}
1012"#,
1013 r#"
1014mod bar {
1015 pub struct Bar;
1016}
1017
1018#[cfg(test)]
1019fn foo() {
1020use bar::Bar;
1021
1022 Bar
1023}
1024"#,
1025 );
1026 }
1027
1028 #[test]
1029 fn respects_cfg_attr2() {
1030 check_assist(
1031 auto_import,
1032 r#"
1033mod bar {
1034 pub struct Bar;
1035}
1036
1037#[cfg(test)]
1038const FOO: Bar = {
1039 Bar$0
1040}
1041"#,
1042 r#"
1043mod bar {
1044 pub struct Bar;
1045}
1046
1047#[cfg(test)]
1048const FOO: Bar = {
1049use bar::Bar;
1050
1051 Bar
1052}
1053"#,
1054 );
1055 }
997} 1056}
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
index 26019c793..26778ee74 100644
--- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -32,7 +32,6 @@ pub(crate) fn replace_qualified_name_with_use(
32 32
33 let target = path.syntax().text_range(); 33 let target = path.syntax().text_range();
34 let scope = ImportScope::find_insert_use_container_with_macros(path.syntax(), &ctx.sema)?; 34 let scope = ImportScope::find_insert_use_container_with_macros(path.syntax(), &ctx.sema)?;
35 let syntax = scope.as_syntax_node();
36 acc.add( 35 acc.add(
37 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), 36 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
38 "Replace qualified path with use", 37 "Replace qualified path with use",
@@ -40,11 +39,13 @@ pub(crate) fn replace_qualified_name_with_use(
40 |builder| { 39 |builder| {
41 // Now that we've brought the name into scope, re-qualify all paths that could be 40 // Now that we've brought the name into scope, re-qualify all paths that could be
42 // affected (that is, all paths inside the node we added the `use` to). 41 // affected (that is, all paths inside the node we added the `use` to).
43 let syntax = builder.make_syntax_mut(syntax.clone()); 42 let scope = match scope {
44 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { 43 ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
45 shorten_paths(&syntax, &path.clone_for_update()); 44 ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
46 insert_use(import_scope, path, &ctx.config.insert_use); 45 ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
47 } 46 };
47 shorten_paths(scope.as_syntax_node(), &path.clone_for_update());
48 insert_use(&scope, path, &ctx.config.insert_use);
48 }, 49 },
49 ) 50 )
50} 51}
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 4da058cb2..e6b4832e7 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -5,7 +5,7 @@ use hir::Semantics;
5use syntax::{ 5use syntax::{
6 algo, 6 algo,
7 ast::{self, make, AstNode, AttrsOwner, ModuleItemOwner, PathSegmentKind, VisibilityOwner}, 7 ast::{self, make, AstNode, AttrsOwner, ModuleItemOwner, PathSegmentKind, VisibilityOwner},
8 ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken, 8 match_ast, ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken,
9}; 9};
10 10
11use crate::{ 11use crate::{
@@ -43,16 +43,32 @@ pub struct InsertUseConfig {
43pub enum ImportScope { 43pub enum ImportScope {
44 File(ast::SourceFile), 44 File(ast::SourceFile),
45 Module(ast::ItemList), 45 Module(ast::ItemList),
46 Block(ast::BlockExpr),
46} 47}
47 48
48impl ImportScope { 49impl ImportScope {
49 pub fn from(syntax: SyntaxNode) -> Option<Self> { 50 fn from(syntax: SyntaxNode) -> Option<Self> {
50 if let Some(module) = ast::Module::cast(syntax.clone()) { 51 fn contains_cfg_attr(attrs: &dyn AttrsOwner) -> bool {
51 module.item_list().map(ImportScope::Module) 52 attrs
52 } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) { 53 .attrs()
53 this.map(ImportScope::File) 54 .any(|attr| attr.as_simple_call().map_or(false, |(ident, _)| ident == "cfg"))
54 } else { 55 }
55 ast::ItemList::cast(syntax).map(ImportScope::Module) 56 match_ast! {
57 match syntax {
58 ast::Module(module) => module.item_list().map(ImportScope::Module),
59 ast::SourceFile(file) => Some(ImportScope::File(file)),
60 ast::Fn(func) => contains_cfg_attr(&func).then(|| func.body().map(ImportScope::Block)).flatten(),
61 ast::Const(konst) => contains_cfg_attr(&konst).then(|| match konst.body()? {
62 ast::Expr::BlockExpr(block) => Some(block),
63 _ => None,
64 }).flatten().map(ImportScope::Block),
65 ast::Static(statik) => contains_cfg_attr(&statik).then(|| match statik.body()? {
66 ast::Expr::BlockExpr(block) => Some(block),
67 _ => None,
68 }).flatten().map(ImportScope::Block),
69 _ => None,
70
71 }
56 } 72 }
57 } 73 }
58 74
@@ -73,6 +89,7 @@ impl ImportScope {
73 match self { 89 match self {
74 ImportScope::File(file) => file.syntax(), 90 ImportScope::File(file) => file.syntax(),
75 ImportScope::Module(item_list) => item_list.syntax(), 91 ImportScope::Module(item_list) => item_list.syntax(),
92 ImportScope::Block(block) => block.syntax(),
76 } 93 }
77 } 94 }
78 95
@@ -80,6 +97,7 @@ impl ImportScope {
80 match self { 97 match self {
81 ImportScope::File(file) => ImportScope::File(file.clone_for_update()), 98 ImportScope::File(file) => ImportScope::File(file.clone_for_update()),
82 ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()), 99 ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()),
100 ImportScope::Block(block) => ImportScope::Block(block.clone_for_update()),
83 } 101 }
84 } 102 }
85 103
@@ -96,6 +114,7 @@ impl ImportScope {
96 let mut use_stmts = match self { 114 let mut use_stmts = match self {
97 ImportScope::File(f) => f.items(), 115 ImportScope::File(f) => f.items(),
98 ImportScope::Module(m) => m.items(), 116 ImportScope::Module(m) => m.items(),
117 ImportScope::Block(b) => b.items(),
99 } 118 }
100 .filter_map(use_stmt); 119 .filter_map(use_stmt);
101 let mut res = ImportGranularityGuess::Unknown; 120 let mut res = ImportGranularityGuess::Unknown;
@@ -319,28 +338,29 @@ fn insert_use_(
319 ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline()); 338 ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline());
320 return; 339 return;
321 } 340 }
322 match scope { 341 let l_curly = match scope {
323 ImportScope::File(_) => { 342 ImportScope::File(_) => {
324 cov_mark::hit!(insert_group_empty_file); 343 cov_mark::hit!(insert_group_empty_file);
325 ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line()); 344 ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line());
326 ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()) 345 ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax());
346 return;
327 } 347 }
348 // don't insert the imports before the item list/block expr's opening curly brace
349 ImportScope::Module(item_list) => item_list.l_curly_token(),
328 // don't insert the imports before the item list's opening curly brace 350 // don't insert the imports before the item list's opening curly brace
329 ImportScope::Module(item_list) => match item_list.l_curly_token() { 351 ImportScope::Block(block) => block.l_curly_token(),
330 Some(b) => { 352 };
331 cov_mark::hit!(insert_group_empty_module); 353 match l_curly {
332 ted::insert(ted::Position::after(&b), make::tokens::single_newline()); 354 Some(b) => {
333 ted::insert(ted::Position::after(&b), use_item.syntax()); 355 cov_mark::hit!(insert_group_empty_module);
334 } 356 ted::insert(ted::Position::after(&b), make::tokens::single_newline());
335 None => { 357 ted::insert(ted::Position::after(&b), use_item.syntax());
336 // This should never happens, broken module syntax node 358 }
337 ted::insert( 359 None => {
338 ted::Position::first_child_of(scope_syntax), 360 // This should never happens, broken module syntax node
339 make::tokens::blank_line(), 361 ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line());
340 ); 362 ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax());
341 ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()); 363 }
342 }
343 },
344 } 364 }
345} 365}
346 366
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 3c92a486f..e33e5bb03 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -8,7 +8,7 @@ use parser::SyntaxKind;
8use rowan::{GreenNodeData, GreenTokenData}; 8use rowan::{GreenNodeData, GreenTokenData};
9 9
10use crate::{ 10use crate::{
11 ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, 11 ast::{self, support, AstChildren, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode},
12 NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T, 12 NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T,
13}; 13};
14 14
@@ -45,6 +45,12 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
45 } 45 }
46} 46}
47 47
48impl ast::BlockExpr {
49 pub fn items(&self) -> AstChildren<ast::Item> {
50 support::children(self.syntax())
51 }
52}
53
48#[derive(Debug, PartialEq, Eq, Clone)] 54#[derive(Debug, PartialEq, Eq, Clone)]
49pub enum Macro { 55pub enum Macro {
50 MacroRules(ast::MacroRules), 56 MacroRules(ast::MacroRules),