@@ -29,6 +29,7 @@ pub(super) struct AnalysisResult {
2929 pub ( super ) analysis : CompletionAnalysis ,
3030 pub ( super ) expected : ( Option < Type > , Option < ast:: NameOrNameRef > ) ,
3131 pub ( super ) qualifier_ctx : QualifierCtx ,
32+ /// the original token of the expanded file
3233 pub ( super ) token : SyntaxToken ,
3334 pub ( super ) offset : TextSize ,
3435}
@@ -213,15 +214,6 @@ fn analyze(
213214 let _p = profile:: span ( "CompletionContext::analyze" ) ;
214215 let ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx } =
215216 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- }
225217
226218 // Overwrite the path kind for derives
227219 if let Some ( ( original_file, file_with_fake_ident, offset, origin_attr) ) = derive_ctx {
@@ -249,37 +241,35 @@ fn analyze(
249241 return None ;
250242 }
251243
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,
259265 }
260266 } 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 ( ) ) ) ;
282271 } ;
272+
283273 let expected = expected_type_and_name ( sema, self_token, & name_like) ;
284274 let mut qual_ctx = QualifierCtx :: default ( ) ;
285275 let analysis = match name_like {
@@ -290,6 +280,22 @@ fn analyze(
290280 let parent = name_ref. syntax ( ) . parent ( ) ?;
291281 let ( nameref_ctx, qualifier_ctx) =
292282 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+
293299 qual_ctx = qualifier_ctx;
294300 CompletionAnalysis :: NameRef ( nameref_ctx)
295301 }
@@ -323,16 +329,14 @@ fn expected_type_and_name(
323329 ast:: FieldExpr ( e) => e
324330 . syntax( )
325331 . 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( ) ,
329334 ast:: PathSegment ( e) => e
330335 . syntax( )
331336 . ancestors( )
332337 . skip( 1 )
333338 . 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( ) ) ) ,
336340 _ => None
337341 }
338342 } ;
@@ -1270,40 +1274,29 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
12701274 Some ( ( use_tree. path ( ) ?, true ) )
12711275}
12721276
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 {
12741278 // oh my ...
12751279 ( || {
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 ) ;
12831284 }
12841285 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- }
12891286 let next_sibl = next_non_trivia_sibling ( pat. syntax ( ) . clone ( ) . into ( ) ) ?;
12901287 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+ }
12961294 } )
12971295 } ) ( )
12981296 . unwrap_or ( false )
12991297}
13001298
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 {
13071300 node. ancestors ( )
13081301 . take_while ( |it| it. kind ( ) != SyntaxKind :: FN && it. kind ( ) != SyntaxKind :: CLOSURE_EXPR )
13091302 . find_map ( |it| {
0 commit comments