@@ -60,107 +60,87 @@ pub struct InsertUseConfig {
60
60
}
61
61
62
62
#[ 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 {
64
70
File ( ast:: SourceFile ) ,
65
71
Module ( ast:: ItemList ) ,
66
72
Block ( ast:: StmtList ) ,
67
73
}
68
74
69
75
impl 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
-
96
76
/// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
97
77
/// Returns the original source node inside attributes.
98
78
pub fn find_insert_use_container (
99
79
position : & SyntaxNode ,
100
80
sema : & Semantics < ' _ , RootDatabase > ,
101
81
) -> 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 ( ) ;
106
85
// Walk up the ancestor tree searching for a suitable node to do insertions on
107
86
// with special handling on cfg-gated items, in which case we want to insert imports locally
108
87
// or FIXME: annotate inserted imports with the same cfg
109
88
for syntax in sema. ancestors_with_macros ( position. clone ( ) ) {
110
89
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 ( ) ;
120
104
}
121
- . stmt_list ( )
122
- . map ( ImportScope :: Block )
123
105
}
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
+ } ) ;
143
116
}
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
+ }
146
121
}
147
122
}
148
123
None
149
124
}
150
125
151
126
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 ( ) ,
156
131
}
157
132
}
158
133
159
134
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 ( ) ,
164
144
}
165
145
}
166
146
}
@@ -216,6 +196,11 @@ fn insert_use_with_alias_option(
216
196
use_tree. wrap_in_tree_list ( ) ;
217
197
}
218
198
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
+ }
219
204
220
205
// merge into existing imports if possible
221
206
if let Some ( mb) = mb {
@@ -229,7 +214,6 @@ fn insert_use_with_alias_option(
229
214
}
230
215
}
231
216
}
232
-
233
217
// either we weren't allowed to merge or there is no import that fits the merge conditions
234
218
// so look for the place we have to insert to
235
219
insert_use_ ( scope, use_item, cfg. group ) ;
@@ -316,10 +300,10 @@ fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess {
316
300
}
317
301
_ => None ,
318
302
} ;
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 ( ) ,
323
307
}
324
308
. filter_map ( use_stmt) ;
325
309
let mut res = ImportGranularityGuess :: Unknown ;
@@ -463,12 +447,12 @@ fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) {
463
447
}
464
448
}
465
449
466
- let l_curly = match scope {
467
- ImportScope :: File ( _) => None ,
450
+ let l_curly = match & scope. kind {
451
+ ImportScopeKind :: File ( _) => None ,
468
452
// 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 ( ) ,
470
454
// 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 ( ) ,
472
456
} ;
473
457
// there are no imports in this file at all
474
458
// so put the import after all inner module attributes and possible license header comments
0 commit comments