@@ -5,7 +5,7 @@ mod format_like;
5
5
use hir:: { Documentation , HasAttrs } ;
6
6
use ide_db:: { imports:: insert_use:: ImportScope , ty_filter:: TryEnum , SnippetCap } ;
7
7
use syntax:: {
8
- ast:: { self , AstNode , AstToken } ,
8
+ ast:: { self , make , AstNode , AstToken } ,
9
9
SyntaxKind :: { EXPR_STMT , STMT_LIST } ,
10
10
TextRange , TextSize ,
11
11
} ;
@@ -129,8 +129,10 @@ pub(crate) fn complete_postfix(
129
129
130
130
// The rest of the postfix completions create an expression that moves an argument,
131
131
// so it's better to consider references now to avoid breaking the compilation
132
- let dot_receiver = include_references ( dot_receiver) ;
133
- let receiver_text = get_receiver_text ( & dot_receiver, receiver_is_ambiguous_float_literal) ;
132
+
133
+ let ( dot_receiver, node_to_replace_with) = include_references ( dot_receiver) ;
134
+ let receiver_text =
135
+ get_receiver_text ( & node_to_replace_with, receiver_is_ambiguous_float_literal) ;
134
136
let postfix_snippet = match build_postfix_snippet_builder ( ctx, cap, & dot_receiver) {
135
137
Some ( it) => it,
136
138
None => return ,
@@ -210,23 +212,43 @@ fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal:
210
212
text. replace ( '\\' , "\\ \\ " ) . replace ( '$' , "\\ $" )
211
213
}
212
214
213
- fn include_references ( initial_element : & ast:: Expr ) -> ast:: Expr {
215
+ fn include_references ( initial_element : & ast:: Expr ) -> ( ast:: Expr , ast :: Expr ) {
214
216
let mut resulting_element = initial_element. clone ( ) ;
215
- while let Some ( parent_ref_element ) =
216
- resulting_element. syntax ( ) . parent ( ) . and_then ( ast:: RefExpr :: cast)
217
+
218
+ while let Some ( field_expr ) = resulting_element. syntax ( ) . parent ( ) . and_then ( ast:: FieldExpr :: cast)
217
219
{
218
- resulting_element = ast:: Expr :: from ( parent_ref_element ) ;
220
+ resulting_element = ast:: Expr :: from ( field_expr ) ;
219
221
}
220
- resulting_element
222
+
223
+ let mut new_element_opt = initial_element. clone ( ) ;
224
+
225
+ if let Some ( first_ref_expr) = resulting_element. syntax ( ) . parent ( ) . and_then ( ast:: RefExpr :: cast) {
226
+ if let Some ( expr) = first_ref_expr. expr ( ) {
227
+ resulting_element = expr. clone ( ) ;
228
+ }
229
+
230
+ while let Some ( parent_ref_element) =
231
+ resulting_element. syntax ( ) . parent ( ) . and_then ( ast:: RefExpr :: cast)
232
+ {
233
+ resulting_element = ast:: Expr :: from ( parent_ref_element) ;
234
+
235
+ new_element_opt = make:: expr_ref ( new_element_opt, false ) ;
236
+ }
237
+ } else {
238
+ // If we do not find any ref expressions, restore
239
+ // all the progress of tree climbing
240
+ resulting_element = initial_element. clone ( ) ;
241
+ }
242
+
243
+ ( resulting_element, new_element_opt)
221
244
}
222
245
223
246
fn build_postfix_snippet_builder < ' ctx > (
224
247
ctx : & ' ctx CompletionContext < ' _ > ,
225
248
cap : SnippetCap ,
226
249
receiver : & ' ctx ast:: Expr ,
227
250
) -> Option < impl Fn ( & str , & str , & str ) -> Builder + ' ctx > {
228
- let receiver_syntax = receiver. syntax ( ) ;
229
- let receiver_range = ctx. sema . original_range_opt ( receiver_syntax) ?. range ;
251
+ let receiver_range = ctx. sema . original_range_opt ( receiver. syntax ( ) ) ?. range ;
230
252
if ctx. source_range ( ) . end ( ) < receiver_range. start ( ) {
231
253
// This shouldn't happen, yet it does. I assume this might be due to an incorrect token mapping.
232
254
return None ;
@@ -616,22 +638,55 @@ fn main() {
616
638
617
639
#[ test]
618
640
fn postfix_custom_snippets_completion_for_references ( ) {
641
+ // https://github.com/rust-lang/rust-analyzer/issues/7929
642
+
643
+ let snippet = Snippet :: new (
644
+ & [ ] ,
645
+ & [ "ok" . into ( ) ] ,
646
+ & [ "Ok(${receiver})" . into ( ) ] ,
647
+ "" ,
648
+ & [ ] ,
649
+ crate :: SnippetScope :: Expr ,
650
+ )
651
+ . unwrap ( ) ;
652
+
619
653
check_edit_with_config (
620
- CompletionConfig {
621
- snippets : vec ! [ Snippet :: new(
622
- & [ ] ,
623
- & [ "ok" . into( ) ] ,
624
- & [ "Ok(${receiver})" . into( ) ] ,
625
- "" ,
626
- & [ ] ,
627
- crate :: SnippetScope :: Expr ,
628
- )
629
- . unwrap( ) ] ,
630
- ..TEST_CONFIG
631
- } ,
654
+ CompletionConfig { snippets : vec ! [ snippet. clone( ) ] , ..TEST_CONFIG } ,
655
+ "ok" ,
656
+ r#"fn main() { &&42.o$0 }"# ,
657
+ r#"fn main() { Ok(&&42) }"# ,
658
+ ) ;
659
+
660
+ check_edit_with_config (
661
+ CompletionConfig { snippets : vec ! [ snippet. clone( ) ] , ..TEST_CONFIG } ,
632
662
"ok" ,
633
663
r#"fn main() { &&42.$0 }"# ,
634
664
r#"fn main() { Ok(&&42) }"# ,
635
665
) ;
666
+
667
+ check_edit_with_config (
668
+ CompletionConfig { snippets : vec ! [ snippet] , ..TEST_CONFIG } ,
669
+ "ok" ,
670
+ r#"
671
+ struct A {
672
+ a: i32,
673
+ }
674
+
675
+ fn main() {
676
+ let a = A {a :1};
677
+ &a.a.$0
678
+ }
679
+ "# ,
680
+ r#"
681
+ struct A {
682
+ a: i32,
683
+ }
684
+
685
+ fn main() {
686
+ let a = A {a :1};
687
+ Ok(&a.a)
688
+ }
689
+ "# ,
690
+ ) ;
636
691
}
637
692
}
0 commit comments