@@ -5,7 +5,7 @@ mod format_like;
55use hir:: { Documentation , HasAttrs } ;
66use ide_db:: { imports:: insert_use:: ImportScope , ty_filter:: TryEnum , SnippetCap } ;
77use syntax:: {
8- ast:: { self , AstNode , AstToken } ,
8+ ast:: { self , make , AstNode , AstToken } ,
99 SyntaxKind :: { EXPR_STMT , STMT_LIST } ,
1010 TextRange , TextSize ,
1111} ;
@@ -129,8 +129,10 @@ pub(crate) fn complete_postfix(
129129
130130 // The rest of the postfix completions create an expression that moves an argument,
131131 // 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) ;
134136 let postfix_snippet = match build_postfix_snippet_builder ( ctx, cap, & dot_receiver) {
135137 Some ( it) => it,
136138 None => return ,
@@ -210,23 +212,43 @@ fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal:
210212 text. replace ( '\\' , "\\ \\ " ) . replace ( '$' , "\\ $" )
211213}
212214
213- fn include_references ( initial_element : & ast:: Expr ) -> ast:: Expr {
215+ fn include_references ( initial_element : & ast:: Expr ) -> ( ast:: Expr , ast :: Expr ) {
214216 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)
217219 {
218- resulting_element = ast:: Expr :: from ( parent_ref_element ) ;
220+ resulting_element = ast:: Expr :: from ( field_expr ) ;
219221 }
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)
221244}
222245
223246fn build_postfix_snippet_builder < ' ctx > (
224247 ctx : & ' ctx CompletionContext < ' _ > ,
225248 cap : SnippetCap ,
226249 receiver : & ' ctx ast:: Expr ,
227250) -> 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 ;
230252 if ctx. source_range ( ) . end ( ) < receiver_range. start ( ) {
231253 // This shouldn't happen, yet it does. I assume this might be due to an incorrect token mapping.
232254 return None ;
@@ -616,22 +638,55 @@ fn main() {
616638
617639 #[ test]
618640 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+
619653 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 } ,
632662 "ok" ,
633663 r#"fn main() { &&42.$0 }"# ,
634664 r#"fn main() { Ok(&&42) }"# ,
635665 ) ;
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+ ) ;
636691 }
637692}
0 commit comments