@@ -148,7 +148,11 @@ pub(crate) struct CompletionContext<'a> {
148
148
pub ( super ) krate : hir:: Crate ,
149
149
/// The module of the `scope`.
150
150
pub ( super ) module : hir:: Module ,
151
+
152
+ /// The expected name of what we are completing.
153
+ /// This is usually the parameter name of the function argument we are completing.
151
154
pub ( super ) expected_name : Option < NameOrNameRef > ,
155
+ /// The expected type of what we are completing.
152
156
pub ( super ) expected_type : Option < Type > ,
153
157
154
158
/// The parent function of the cursor position if it exists.
@@ -157,6 +161,7 @@ pub(crate) struct CompletionContext<'a> {
157
161
pub ( super ) impl_def : Option < ast:: Impl > ,
158
162
/// The NameLike under the cursor in the original file if it exists.
159
163
pub ( super ) name_syntax : Option < ast:: NameLike > ,
164
+ /// Are we completing inside a let statement with a missing semicolon?
160
165
pub ( super ) incomplete_let : bool ,
161
166
162
167
pub ( super ) completion_location : Option < ImmediateLocation > ,
@@ -424,6 +429,7 @@ impl<'a> CompletionContext<'a> {
424
429
let scope = sema. scope_at_offset ( & token. parent ( ) ?, offset) ?;
425
430
let krate = scope. krate ( ) ;
426
431
let module = scope. module ( ) ;
432
+
427
433
let mut locals = FxHashMap :: default ( ) ;
428
434
scope. process_all_names ( & mut |name, scope| {
429
435
if let ScopeDef :: Local ( local) = scope {
@@ -467,8 +473,9 @@ impl<'a> CompletionContext<'a> {
467
473
Some ( ctx)
468
474
}
469
475
470
- /// Do the attribute expansion at the current cursor position for both original file and fake file
471
- /// as long as possible. As soon as one of the two expansions fail we stop to stay in sync.
476
+ /// Expand attributes and macro calls at the current cursor position for both the original file
477
+ /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
478
+ /// and speculative states stay in sync.
472
479
fn expand_and_fill (
473
480
& mut self ,
474
481
mut original_file : SyntaxNode ,
@@ -489,7 +496,9 @@ impl<'a> CompletionContext<'a> {
489
496
) ,
490
497
|( a, b) | parent_item ( a) . zip ( parent_item ( b) ) ,
491
498
) ;
492
- for ( actual_item, item_with_fake_ident) in ancestor_items {
499
+
500
+ // first try to expand attributes as these are always the outermost macro calls
501
+ ' ancestors: for ( actual_item, item_with_fake_ident) in ancestor_items {
493
502
match (
494
503
self . sema . expand_attr_macro ( & actual_item) ,
495
504
self . sema . speculative_expand_attr_macro (
@@ -498,12 +507,14 @@ impl<'a> CompletionContext<'a> {
498
507
fake_ident_token. clone ( ) ,
499
508
) ,
500
509
) {
501
- // maybe parent items have attributes
502
- ( None , None ) => ( ) ,
510
+ // maybe parent items have attributes, so continue walking the ancestors
511
+ ( None , None ) => continue ' ancestors ,
503
512
// successful expansions
504
513
( Some ( actual_expansion) , Some ( ( fake_expansion, fake_mapped_token) ) ) => {
505
514
let new_offset = fake_mapped_token. text_range ( ) . start ( ) ;
506
515
if new_offset > actual_expansion. text_range ( ) . end ( ) {
516
+ // offset outside of bounds from the original expansion,
517
+ // stop here to prevent problems from happening
507
518
break ' expansion;
508
519
}
509
520
original_file = actual_expansion;
@@ -516,40 +527,39 @@ impl<'a> CompletionContext<'a> {
516
527
_ => break ' expansion,
517
528
}
518
529
}
530
+
531
+ // No attributes have been expanded, so look for macro_call! token trees or derive token trees
519
532
let orig_tt = match find_node_at_offset :: < ast:: TokenTree > ( & original_file, offset) {
520
533
Some ( it) => it,
521
- None => break ,
534
+ None => break ' expansion ,
522
535
} ;
523
536
let spec_tt = match find_node_at_offset :: < ast:: TokenTree > ( & speculative_file, offset) {
524
537
Some ( it) => it,
525
- None => break ,
538
+ None => break ' expansion ,
526
539
} ;
527
540
528
541
// Expand pseudo-derive expansion
529
542
if let ( Some ( orig_attr) , Some ( spec_attr) ) = (
530
543
orig_tt. syntax ( ) . parent ( ) . and_then ( ast:: Meta :: cast) . and_then ( |it| it. parent_attr ( ) ) ,
531
544
spec_tt. syntax ( ) . parent ( ) . and_then ( ast:: Meta :: cast) . and_then ( |it| it. parent_attr ( ) ) ,
532
545
) {
533
- match (
546
+ if let ( Some ( actual_expansion ) , Some ( ( fake_expansion , fake_mapped_token ) ) ) = (
534
547
self . sema . expand_derive_as_pseudo_attr_macro ( & orig_attr) ,
535
548
self . sema . speculative_expand_derive_as_pseudo_attr_macro (
536
549
& orig_attr,
537
550
& spec_attr,
538
551
fake_ident_token. clone ( ) ,
539
552
) ,
540
553
) {
541
- // Clearly not a derive macro
542
- ( None , None ) => ( ) ,
543
- // successful expansions
544
- ( Some ( actual_expansion) , Some ( ( fake_expansion, fake_mapped_token) ) ) => {
545
- let new_offset = fake_mapped_token. text_range ( ) . start ( ) ;
546
- derive_ctx =
547
- Some ( ( actual_expansion, fake_expansion, new_offset, orig_attr) ) ;
548
- break ' expansion;
549
- }
550
- // exactly one expansion failed, inconsistent state so stop expanding completely
551
- _ => break ' expansion,
554
+ derive_ctx = Some ( (
555
+ actual_expansion,
556
+ fake_expansion,
557
+ fake_mapped_token. text_range ( ) . start ( ) ,
558
+ orig_attr,
559
+ ) ) ;
552
560
}
561
+ // at this point we won't have any more successful expansions, so stop
562
+ break ' expansion;
553
563
}
554
564
555
565
// Expand fn-like macro calls
@@ -560,12 +570,14 @@ impl<'a> CompletionContext<'a> {
560
570
let mac_call_path0 = actual_macro_call. path ( ) . as_ref ( ) . map ( |s| s. syntax ( ) . text ( ) ) ;
561
571
let mac_call_path1 =
562
572
macro_call_with_fake_ident. path ( ) . as_ref ( ) . map ( |s| s. syntax ( ) . text ( ) ) ;
573
+
574
+ // inconsistent state, stop expanding
563
575
if mac_call_path0 != mac_call_path1 {
564
- break ;
576
+ break ' expansion ;
565
577
}
566
578
let speculative_args = match macro_call_with_fake_ident. token_tree ( ) {
567
579
Some ( tt) => tt,
568
- None => break ,
580
+ None => break ' expansion ,
569
581
} ;
570
582
571
583
match (
@@ -580,24 +592,30 @@ impl<'a> CompletionContext<'a> {
580
592
( Some ( actual_expansion) , Some ( ( fake_expansion, fake_mapped_token) ) ) => {
581
593
let new_offset = fake_mapped_token. text_range ( ) . start ( ) ;
582
594
if new_offset > actual_expansion. text_range ( ) . end ( ) {
583
- break ;
595
+ // offset outside of bounds from the original expansion,
596
+ // stop here to prevent problems from happening
597
+ break ' expansion;
584
598
}
585
599
original_file = actual_expansion;
586
600
speculative_file = fake_expansion;
587
601
fake_ident_token = fake_mapped_token;
588
602
offset = new_offset;
589
- continue ;
603
+ continue ' expansion ;
590
604
}
591
- _ => break ,
605
+ // at least on expansion failed, we won't have anything to expand from this point
606
+ // onwards so break out
607
+ _ => break ' expansion,
592
608
}
593
609
}
594
610
595
- break ;
611
+ // none of our states have changed so stop the loop
612
+ break ' expansion;
596
613
}
597
614
598
615
self . fill ( & original_file, speculative_file, offset, derive_ctx) ;
599
616
}
600
617
618
+ /// Calculate the expected type and name of the cursor position.
601
619
fn expected_type_and_name ( & self ) -> ( Option < Type > , Option < NameOrNameRef > ) {
602
620
let mut node = match self . token . parent ( ) {
603
621
Some ( it) => it,
@@ -734,6 +752,8 @@ impl<'a> CompletionContext<'a> {
734
752
}
735
753
}
736
754
755
+ /// Fill the completion context, this is what does semantic reasoning about the surrounding context
756
+ /// of the completion location.
737
757
fn fill (
738
758
& mut self ,
739
759
original_file : & SyntaxNode ,
@@ -1067,14 +1087,16 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
1067
1087
}
1068
1088
}
1069
1089
1090
+ /// Attempts to find `node` inside `syntax` via `node`'s text range.
1070
1091
fn find_node_in_file < N : AstNode > ( syntax : & SyntaxNode , node : & N ) -> Option < N > {
1071
1092
let syntax_range = syntax. text_range ( ) ;
1072
1093
let range = node. syntax ( ) . text_range ( ) ;
1073
1094
let intersection = range. intersect ( syntax_range) ?;
1074
1095
syntax. covering_element ( intersection) . ancestors ( ) . find_map ( N :: cast)
1075
1096
}
1076
1097
1077
- /// Compensates for the offset introduced by the fake ident
1098
+ /// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
1099
+ /// for the offset introduced by the fake ident.
1078
1100
/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
1079
1101
fn find_node_in_file_compensated < N : AstNode > ( syntax : & SyntaxNode , node : & N ) -> Option < N > {
1080
1102
let syntax_range = syntax. text_range ( ) ;
@@ -1143,6 +1165,7 @@ const OP_TRAIT_LANG_NAMES: &[&str] = &[
1143
1165
"shr" ,
1144
1166
"sub" ,
1145
1167
] ;
1168
+
1146
1169
#[ cfg( test) ]
1147
1170
mod tests {
1148
1171
use expect_test:: { expect, Expect } ;
0 commit comments