diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-06-18 22:58:35 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-06-18 22:58:35 +0100 |
commit | 0d3bad85e60d011b35da0a8e5dd6611934f6816d (patch) | |
tree | 26403f2e9927f32698009cad0d8995ffa9cb058b /crates | |
parent | d9666ce5098442ab0cbac4d4363435074eb12923 (diff) | |
parent | 344cb5e76a31b8ef7ae80ce0ef39c54248ad8df7 (diff) |
Merge #9335
9335: feat: Don't insert imports outside of cfg attributed items r=Veykril a=Veykril
Closes #6939
Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide_assists/src/handlers/auto_import.rs | 59 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs | 13 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use.rs | 70 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 8 |
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#" | ||
1004 | mod bar { | ||
1005 | pub struct Bar; | ||
1006 | } | ||
1007 | |||
1008 | #[cfg(test)] | ||
1009 | fn foo() { | ||
1010 | Bar$0 | ||
1011 | } | ||
1012 | "#, | ||
1013 | r#" | ||
1014 | mod bar { | ||
1015 | pub struct Bar; | ||
1016 | } | ||
1017 | |||
1018 | #[cfg(test)] | ||
1019 | fn foo() { | ||
1020 | use 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#" | ||
1033 | mod bar { | ||
1034 | pub struct Bar; | ||
1035 | } | ||
1036 | |||
1037 | #[cfg(test)] | ||
1038 | const FOO: Bar = { | ||
1039 | Bar$0 | ||
1040 | } | ||
1041 | "#, | ||
1042 | r#" | ||
1043 | mod bar { | ||
1044 | pub struct Bar; | ||
1045 | } | ||
1046 | |||
1047 | #[cfg(test)] | ||
1048 | const FOO: Bar = { | ||
1049 | use 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; | |||
5 | use syntax::{ | 5 | use 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 | ||
11 | use crate::{ | 11 | use crate::{ |
@@ -43,16 +43,32 @@ pub struct InsertUseConfig { | |||
43 | pub enum ImportScope { | 43 | pub 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 | ||
48 | impl ImportScope { | 49 | impl 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; | |||
8 | use rowan::{GreenNodeData, GreenTokenData}; | 8 | use rowan::{GreenNodeData, GreenTokenData}; |
9 | 9 | ||
10 | use crate::{ | 10 | use 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 | ||
48 | impl 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)] |
49 | pub enum Macro { | 55 | pub enum Macro { |
50 | MacroRules(ast::MacroRules), | 56 | MacroRules(ast::MacroRules), |