@@ -60,107 +60,87 @@ pub struct InsertUseConfig {
6060}
6161
6262#[ derive( Debug , Clone ) ]
63- pub enum ImportScope {
63+ pub struct ImportScope {
64+ pub kind : ImportScopeKind ,
65+ pub required_cfgs : Vec < ast:: Attr > ,
66+ }
67+
68+ #[ derive( Debug , Clone ) ]
69+ pub enum ImportScopeKind {
6470 File ( ast:: SourceFile ) ,
6571 Module ( ast:: ItemList ) ,
6672 Block ( ast:: StmtList ) ,
6773}
6874
6975impl ImportScope {
70- // FIXME: Remove this?
71- #[ cfg( test) ]
72- fn from ( syntax : SyntaxNode ) -> Option < Self > {
73- use syntax:: match_ast;
74- fn contains_cfg_attr ( attrs : & dyn HasAttrs ) -> bool {
75- attrs. attrs ( ) . any ( |attr| attr. as_simple_call ( ) . is_some_and ( |( ident, _) | ident == "cfg" ) )
76- }
77- match_ast ! {
78- match syntax {
79- ast:: Module ( module) => module. item_list( ) . map( ImportScope :: Module ) ,
80- ast:: SourceFile ( file) => Some ( ImportScope :: File ( file) ) ,
81- ast:: Fn ( func) => contains_cfg_attr( & func) . then( || func. body( ) . and_then( |it| it. stmt_list( ) . map( ImportScope :: Block ) ) ) . flatten( ) ,
82- ast:: Const ( konst) => contains_cfg_attr( & konst) . then( || match konst. body( ) ? {
83- ast:: Expr :: BlockExpr ( block) => Some ( block) ,
84- _ => None ,
85- } ) . flatten( ) . and_then( |it| it. stmt_list( ) . map( ImportScope :: Block ) ) ,
86- ast:: Static ( statik) => contains_cfg_attr( & statik) . then( || match statik. body( ) ? {
87- ast:: Expr :: BlockExpr ( block) => Some ( block) ,
88- _ => None ,
89- } ) . flatten( ) . and_then( |it| it. stmt_list( ) . map( ImportScope :: Block ) ) ,
90- _ => None ,
91-
92- }
93- }
94- }
95-
9676 /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
9777 /// Returns the original source node inside attributes.
9878 pub fn find_insert_use_container (
9979 position : & SyntaxNode ,
10080 sema : & Semantics < ' _ , RootDatabase > ,
10181 ) -> Option < Self > {
102- fn contains_cfg_attr ( attrs : & dyn HasAttrs ) -> bool {
103- attrs. attrs ( ) . any ( |attr| attr. as_simple_call ( ) . is_some_and ( |( ident, _) | ident == "cfg" ) )
104- }
105-
82+ // The closest block expression ancestor
83+ let mut block = None ;
84+ let mut required_cfgs = Vec :: new ( ) ;
10685 // Walk up the ancestor tree searching for a suitable node to do insertions on
10786 // with special handling on cfg-gated items, in which case we want to insert imports locally
10887 // or FIXME: annotate inserted imports with the same cfg
10988 for syntax in sema. ancestors_with_macros ( position. clone ( ) ) {
11089 if let Some ( file) = ast:: SourceFile :: cast ( syntax. clone ( ) ) {
111- return Some ( ImportScope :: File ( file) ) ;
112- } else if let Some ( item) = ast:: Item :: cast ( syntax) {
113- return match item {
114- ast:: Item :: Const ( konst) if contains_cfg_attr ( & konst) => {
115- // FIXME: Instead of bailing out with None, we should note down that
116- // this import needs an attribute added
117- match sema. original_ast_node ( konst) ?. body ( ) ? {
118- ast:: Expr :: BlockExpr ( block) => block,
119- _ => return None ,
90+ return Some ( ImportScope { kind : ImportScopeKind :: File ( file) , required_cfgs } ) ;
91+ } else if let Some ( module) = ast:: Module :: cast ( syntax. clone ( ) ) {
92+ // early return is important here, if we can't find the original module
93+ // in the input there is no way for us to insert an import anywhere.
94+ return sema
95+ . original_ast_node ( module) ?
96+ . item_list ( )
97+ . map ( ImportScopeKind :: Module )
98+ . map ( |kind| ImportScope { kind, required_cfgs } ) ;
99+ } else if let Some ( has_attrs) = ast:: AnyHasAttrs :: cast ( syntax) {
100+ if block. is_none ( ) {
101+ if let Some ( b) = ast:: BlockExpr :: cast ( has_attrs. syntax ( ) . clone ( ) ) {
102+ if let Some ( b) = sema. original_ast_node ( b) {
103+ block = b. stmt_list ( ) ;
120104 }
121- . stmt_list ( )
122- . map ( ImportScope :: Block )
123105 }
124- ast:: Item :: Fn ( func) if contains_cfg_attr ( & func) => {
125- // FIXME: Instead of bailing out with None, we should note down that
126- // this import needs an attribute added
127- sema. original_ast_node ( func) ?. body ( ) ?. stmt_list ( ) . map ( ImportScope :: Block )
128- }
129- ast:: Item :: Static ( statik) if contains_cfg_attr ( & statik) => {
130- // FIXME: Instead of bailing out with None, we should note down that
131- // this import needs an attribute added
132- match sema. original_ast_node ( statik) ?. body ( ) ? {
133- ast:: Expr :: BlockExpr ( block) => block,
134- _ => return None ,
135- }
136- . stmt_list ( )
137- . map ( ImportScope :: Block )
138- }
139- ast:: Item :: Module ( module) => {
140- // early return is important here, if we can't find the original module
141- // in the input there is no way for us to insert an import anywhere.
142- sema. original_ast_node ( module) ?. item_list ( ) . map ( ImportScope :: Module )
106+ }
107+ if has_attrs
108+ . attrs ( )
109+ . any ( |attr| attr. as_simple_call ( ) . is_some_and ( |( ident, _) | ident == "cfg" ) )
110+ {
111+ if let Some ( b) = block {
112+ return Some ( ImportScope {
113+ kind : ImportScopeKind :: Block ( b) ,
114+ required_cfgs,
115+ } ) ;
143116 }
144- _ => continue ,
145- } ;
117+ required_cfgs. extend ( has_attrs. attrs ( ) . filter ( |attr| {
118+ attr. as_simple_call ( ) . is_some_and ( |( ident, _) | ident == "cfg" )
119+ } ) ) ;
120+ }
146121 }
147122 }
148123 None
149124 }
150125
151126 pub fn as_syntax_node ( & self ) -> & SyntaxNode {
152- match self {
153- ImportScope :: File ( file) => file. syntax ( ) ,
154- ImportScope :: Module ( item_list) => item_list. syntax ( ) ,
155- ImportScope :: Block ( block) => block. syntax ( ) ,
127+ match & self . kind {
128+ ImportScopeKind :: File ( file) => file. syntax ( ) ,
129+ ImportScopeKind :: Module ( item_list) => item_list. syntax ( ) ,
130+ ImportScopeKind :: Block ( block) => block. syntax ( ) ,
156131 }
157132 }
158133
159134 pub fn clone_for_update ( & self ) -> Self {
160- match self {
161- ImportScope :: File ( file) => ImportScope :: File ( file. clone_for_update ( ) ) ,
162- ImportScope :: Module ( item_list) => ImportScope :: Module ( item_list. clone_for_update ( ) ) ,
163- ImportScope :: Block ( block) => ImportScope :: Block ( block. clone_for_update ( ) ) ,
135+ Self {
136+ kind : match & self . kind {
137+ ImportScopeKind :: File ( file) => ImportScopeKind :: File ( file. clone_for_update ( ) ) ,
138+ ImportScopeKind :: Module ( item_list) => {
139+ ImportScopeKind :: Module ( item_list. clone_for_update ( ) )
140+ }
141+ ImportScopeKind :: Block ( block) => ImportScopeKind :: Block ( block. clone_for_update ( ) ) ,
142+ } ,
143+ required_cfgs : self . required_cfgs . iter ( ) . map ( |attr| attr. clone_for_update ( ) ) . collect ( ) ,
164144 }
165145 }
166146}
@@ -216,6 +196,11 @@ fn insert_use_with_alias_option(
216196 use_tree. wrap_in_tree_list ( ) ;
217197 }
218198 let use_item = make:: use_ ( None , use_tree) . clone_for_update ( ) ;
199+ for attr in
200+ scope. required_cfgs . iter ( ) . map ( |attr| attr. syntax ( ) . clone_subtree ( ) . clone_for_update ( ) )
201+ {
202+ ted:: insert ( ted:: Position :: first_child_of ( use_item. syntax ( ) ) , attr) ;
203+ }
219204
220205 // merge into existing imports if possible
221206 if let Some ( mb) = mb {
@@ -229,7 +214,6 @@ fn insert_use_with_alias_option(
229214 }
230215 }
231216 }
232-
233217 // either we weren't allowed to merge or there is no import that fits the merge conditions
234218 // so look for the place we have to insert to
235219 insert_use_ ( scope, use_item, cfg. group ) ;
@@ -316,10 +300,10 @@ fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess {
316300 }
317301 _ => None ,
318302 } ;
319- let mut use_stmts = match scope {
320- ImportScope :: File ( f) => f. items ( ) ,
321- ImportScope :: Module ( m) => m. items ( ) ,
322- ImportScope :: Block ( b) => b. items ( ) ,
303+ let mut use_stmts = match & scope. kind {
304+ ImportScopeKind :: File ( f) => f. items ( ) ,
305+ ImportScopeKind :: Module ( m) => m. items ( ) ,
306+ ImportScopeKind :: Block ( b) => b. items ( ) ,
323307 }
324308 . filter_map ( use_stmt) ;
325309 let mut res = ImportGranularityGuess :: Unknown ;
@@ -463,12 +447,12 @@ fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) {
463447 }
464448 }
465449
466- let l_curly = match scope {
467- ImportScope :: File ( _) => None ,
450+ let l_curly = match & scope. kind {
451+ ImportScopeKind :: File ( _) => None ,
468452 // don't insert the imports before the item list/block expr's opening curly brace
469- ImportScope :: Module ( item_list) => item_list. l_curly_token ( ) ,
453+ ImportScopeKind :: Module ( item_list) => item_list. l_curly_token ( ) ,
470454 // don't insert the imports before the item list's opening curly brace
471- ImportScope :: Block ( block) => block. l_curly_token ( ) ,
455+ ImportScopeKind :: Block ( block) => block. l_curly_token ( ) ,
472456 } ;
473457 // there are no imports in this file at all
474458 // so put the import after all inner module attributes and possible license header comments
0 commit comments