@@ -29,6 +29,7 @@ pub(super) struct AnalysisResult {
29
29
pub ( super ) analysis : CompletionAnalysis ,
30
30
pub ( super ) expected : ( Option < Type > , Option < ast:: NameOrNameRef > ) ,
31
31
pub ( super ) qualifier_ctx : QualifierCtx ,
32
+ /// the original token of the expanded file
32
33
pub ( super ) token : SyntaxToken ,
33
34
pub ( super ) offset : TextSize ,
34
35
}
@@ -213,15 +214,6 @@ fn analyze(
213
214
let _p = profile:: span ( "CompletionContext::analyze" ) ;
214
215
let ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx } =
215
216
expansion_result;
216
- let syntax_element = NodeOrToken :: Token ( fake_ident_token) ;
217
- if is_in_token_of_for_loop ( syntax_element. clone ( ) ) {
218
- // for pat $0
219
- // there is nothing to complete here except `in` keyword
220
- // don't bother populating the context
221
- // FIXME: the completion calculations should end up good enough
222
- // such that this special case becomes unnecessary
223
- return None ;
224
- }
225
217
226
218
// Overwrite the path kind for derives
227
219
if let Some ( ( original_file, file_with_fake_ident, offset, origin_attr) ) = derive_ctx {
@@ -249,37 +241,35 @@ fn analyze(
249
241
return None ;
250
242
}
251
243
252
- let name_like = match find_node_at_offset ( & speculative_file, offset) {
253
- Some ( it) => it,
254
- None => {
255
- let analysis = if let Some ( original) = ast:: String :: cast ( original_token. clone ( ) ) {
256
- CompletionAnalysis :: String {
257
- original,
258
- expanded : ast:: String :: cast ( self_token. clone ( ) ) ,
244
+ let Some ( name_like) = find_node_at_offset ( & speculative_file, offset) else {
245
+ let analysis = if let Some ( original) = ast:: String :: cast ( original_token. clone ( ) ) {
246
+ CompletionAnalysis :: String {
247
+ original,
248
+ expanded : ast:: String :: cast ( self_token. clone ( ) ) ,
249
+ }
250
+ } else {
251
+ // Fix up trailing whitespace problem
252
+ // #[attr(foo = $0
253
+ let token = syntax:: algo:: skip_trivia_token ( self_token. clone ( ) , Direction :: Prev ) ?;
254
+ let p = token. parent ( ) ?;
255
+ if p. kind ( ) == SyntaxKind :: TOKEN_TREE
256
+ && p. ancestors ( ) . any ( |it| it. kind ( ) == SyntaxKind :: META )
257
+ {
258
+ let colon_prefix = previous_non_trivia_token ( self_token. clone ( ) )
259
+ . map_or ( false , |it| T ! [ : ] == it. kind ( ) ) ;
260
+ CompletionAnalysis :: UnexpandedAttrTT {
261
+ fake_attribute_under_caret : fake_ident_token
262
+ . parent_ancestors ( )
263
+ . find_map ( ast:: Attr :: cast) ,
264
+ colon_prefix,
259
265
}
260
266
} else {
261
- // Fix up trailing whitespace problem
262
- // #[attr(foo = $0
263
- let token = syntax:: algo:: skip_trivia_token ( self_token. clone ( ) , Direction :: Prev ) ?;
264
- let p = token. parent ( ) ?;
265
- if p. kind ( ) == SyntaxKind :: TOKEN_TREE
266
- && p. ancestors ( ) . any ( |it| it. kind ( ) == SyntaxKind :: META )
267
- {
268
- let colon_prefix = previous_non_trivia_token ( self_token. clone ( ) )
269
- . map_or ( false , |it| T ! [ : ] == it. kind ( ) ) ;
270
- CompletionAnalysis :: UnexpandedAttrTT {
271
- fake_attribute_under_caret : syntax_element
272
- . ancestors ( )
273
- . find_map ( ast:: Attr :: cast) ,
274
- colon_prefix,
275
- }
276
- } else {
277
- return None ;
278
- }
279
- } ;
280
- return Some ( ( analysis, ( None , None ) , QualifierCtx :: default ( ) ) ) ;
281
- }
267
+ return None ;
268
+ }
269
+ } ;
270
+ return Some ( ( analysis, ( None , None ) , QualifierCtx :: default ( ) ) ) ;
282
271
} ;
272
+
283
273
let expected = expected_type_and_name ( sema, self_token, & name_like) ;
284
274
let mut qual_ctx = QualifierCtx :: default ( ) ;
285
275
let analysis = match name_like {
@@ -290,6 +280,22 @@ fn analyze(
290
280
let parent = name_ref. syntax ( ) . parent ( ) ?;
291
281
let ( nameref_ctx, qualifier_ctx) =
292
282
classify_name_ref ( sema, & original_file, name_ref, parent) ?;
283
+
284
+ if let NameRefContext {
285
+ kind :
286
+ NameRefKind :: Path ( PathCompletionCtx { kind : PathKind :: Expr { .. } , path, .. } , ..) ,
287
+ ..
288
+ } = & nameref_ctx
289
+ {
290
+ if is_in_token_of_for_loop ( path) {
291
+ // for pat $0
292
+ // there is nothing to complete here except `in` keyword
293
+ // don't bother populating the context
294
+ // Ideally this special casing wouldn't be needed, but the parser recovers
295
+ return None ;
296
+ }
297
+ }
298
+
293
299
qual_ctx = qualifier_ctx;
294
300
CompletionAnalysis :: NameRef ( nameref_ctx)
295
301
}
@@ -323,16 +329,14 @@ fn expected_type_and_name(
323
329
ast:: FieldExpr ( e) => e
324
330
. syntax( )
325
331
. ancestors( )
326
- . map_while( ast:: FieldExpr :: cast)
327
- . last( )
328
- . map( |it| it. syntax( ) . clone( ) ) ,
332
+ . take_while( |it| ast:: FieldExpr :: can_cast( it. kind( ) ) )
333
+ . last( ) ,
329
334
ast:: PathSegment ( e) => e
330
335
. syntax( )
331
336
. ancestors( )
332
337
. skip( 1 )
333
338
. take_while( |it| ast:: Path :: can_cast( it. kind( ) ) || ast:: PathExpr :: can_cast( it. kind( ) ) )
334
- . find_map( ast:: PathExpr :: cast)
335
- . map( |it| it. syntax( ) . clone( ) ) ,
339
+ . find( |it| ast:: PathExpr :: can_cast( it. kind( ) ) ) ,
336
340
_ => None
337
341
}
338
342
} ;
@@ -1270,40 +1274,29 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
1270
1274
Some ( ( use_tree. path ( ) ?, true ) )
1271
1275
}
1272
1276
1273
- pub ( crate ) fn is_in_token_of_for_loop ( element : SyntaxElement ) -> bool {
1277
+ fn is_in_token_of_for_loop ( path : & ast :: Path ) -> bool {
1274
1278
// oh my ...
1275
1279
( || {
1276
- let syntax_token = element. into_token ( ) ?;
1277
- let range = syntax_token. text_range ( ) ;
1278
- let for_expr = syntax_token. parent_ancestors ( ) . find_map ( ast:: ForExpr :: cast) ?;
1279
-
1280
- // check if the current token is the `in` token of a for loop
1281
- if let Some ( token) = for_expr. in_token ( ) {
1282
- return Some ( syntax_token == token) ;
1280
+ let expr = path. syntax ( ) . parent ( ) . and_then ( ast:: PathExpr :: cast) ?;
1281
+ let for_expr = expr. syntax ( ) . parent ( ) . and_then ( ast:: ForExpr :: cast) ?;
1282
+ if for_expr. in_token ( ) . is_some ( ) {
1283
+ return Some ( false ) ;
1283
1284
}
1284
1285
let pat = for_expr. pat ( ) ?;
1285
- if range. end ( ) < pat. syntax ( ) . text_range ( ) . end ( ) {
1286
- // if we are inside or before the pattern we can't be at the `in` token position
1287
- return None ;
1288
- }
1289
1286
let next_sibl = next_non_trivia_sibling ( pat. syntax ( ) . clone ( ) . into ( ) ) ?;
1290
1287
Some ( match next_sibl {
1291
- // the loop body is some node, if our token is at the start we are at the `in` position,
1292
- // otherwise we could be in a recovered expression, we don't wanna ruin completions there
1293
- syntax:: NodeOrToken :: Node ( n) => n. text_range ( ) . start ( ) == range. start ( ) ,
1294
- // the loop body consists of a single token, if we are this we are certainly at the `in` token position
1295
- syntax:: NodeOrToken :: Token ( t) => t == syntax_token,
1288
+ syntax:: NodeOrToken :: Node ( n) => {
1289
+ n. text_range ( ) . start ( ) == path. syntax ( ) . text_range ( ) . start ( )
1290
+ }
1291
+ syntax:: NodeOrToken :: Token ( t) => {
1292
+ t. text_range ( ) . start ( ) == path. syntax ( ) . text_range ( ) . start ( )
1293
+ }
1296
1294
} )
1297
1295
} ) ( )
1298
1296
. unwrap_or ( false )
1299
1297
}
1300
1298
1301
- #[ test]
1302
- fn test_for_is_prev2 ( ) {
1303
- crate :: tests:: check_pattern_is_applicable ( r"fn __() { for i i$0 }" , is_in_token_of_for_loop) ;
1304
- }
1305
-
1306
- pub ( crate ) fn is_in_loop_body ( node : & SyntaxNode ) -> bool {
1299
+ fn is_in_loop_body ( node : & SyntaxNode ) -> bool {
1307
1300
node. ancestors ( )
1308
1301
. take_while ( |it| it. kind ( ) != SyntaxKind :: FN && it. kind ( ) != SyntaxKind :: CLOSURE_EXPR )
1309
1302
. find_map ( |it| {
0 commit comments