]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide/src/typing.rs
Rollup merge of #100228 - luqmana:suggestion-ice, r=estebank
[rust.git] / src / tools / rust-analyzer / crates / ide / src / typing.rs
1 //! This module handles auto-magic editing actions applied together with users
2 //! edits. For example, if the user typed
3 //!
4 //! ```text
5 //!     foo
6 //!         .bar()
7 //!         .baz()
8 //!     |   // <- cursor is here
9 //! ```
10 //!
11 //! and types `.` next, we want to indent the dot.
12 //!
13 //! Language server executes such typing assists synchronously. That is, they
14 //! block user's typing and should be pretty fast for this reason!
15
16 mod on_enter;
17
18 use ide_db::{
19     base_db::{FilePosition, SourceDatabase},
20     RootDatabase,
21 };
22 use syntax::{
23     algo::{ancestors_at_offset, find_node_at_offset},
24     ast::{self, edit::IndentLevel, AstToken},
25     AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize, T,
26 };
27
28 use text_edit::{Indel, TextEdit};
29
30 use crate::SourceChange;
31
32 pub(crate) use on_enter::on_enter;
33
34 // Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
35 pub(crate) const TRIGGER_CHARS: &str = ".=<>{";
36
37 struct ExtendedTextEdit {
38     edit: TextEdit,
39     is_snippet: bool,
40 }
41
42 // Feature: On Typing Assists
43 //
44 // Some features trigger on typing certain characters:
45 //
46 // - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
47 // - typing `=` between two expressions adds `;` when in statement position
48 // - typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
49 // - typing `.` in a chain method call auto-indents
50 // - typing `{` in front of an expression inserts a closing `}` after the expression
51 // - typing `{` in a use item adds a closing `}` in the right place
52 //
53 // VS Code::
54 //
55 // Add the following to `settings.json`:
56 // [source,json]
57 // ----
58 // "editor.formatOnType": true,
59 // ----
60 //
61 // image::https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif[]
62 // image::https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif[]
63 pub(crate) fn on_char_typed(
64     db: &RootDatabase,
65     position: FilePosition,
66     char_typed: char,
67 ) -> Option<SourceChange> {
68     if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
69         return None;
70     }
71     let file = &db.parse(position.file_id);
72     if !stdx::always!(file.tree().syntax().text().char_at(position.offset) == Some(char_typed)) {
73         return None;
74     }
75     let edit = on_char_typed_inner(file, position.offset, char_typed)?;
76     let mut sc = SourceChange::from_text_edit(position.file_id, edit.edit);
77     sc.is_snippet = edit.is_snippet;
78     Some(sc)
79 }
80
81 fn on_char_typed_inner(
82     file: &Parse<SourceFile>,
83     offset: TextSize,
84     char_typed: char,
85 ) -> Option<ExtendedTextEdit> {
86     if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
87         return None;
88     }
89     return match char_typed {
90         '.' => conv(on_dot_typed(&file.tree(), offset)),
91         '=' => conv(on_eq_typed(&file.tree(), offset)),
92         '<' => on_left_angle_typed(&file.tree(), offset),
93         '>' => conv(on_right_angle_typed(&file.tree(), offset)),
94         '{' => conv(on_opening_brace_typed(file, offset)),
95         _ => return None,
96     };
97
98     fn conv(text_edit: Option<TextEdit>) -> Option<ExtendedTextEdit> {
99         Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false })
100     }
101 }
102
103 /// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
104 /// block, or a part of a `use` item.
105 fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> {
106     if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) {
107         return None;
108     }
109
110     let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;
111     if brace_token.kind() != SyntaxKind::L_CURLY {
112         return None;
113     }
114
115     // Remove the `{` to get a better parse tree, and reparse.
116     let range = brace_token.text_range();
117     if !stdx::always!(range.len() == TextSize::of('{')) {
118         return None;
119     }
120     let file = file.reparse(&Indel::delete(range));
121
122     if let Some(edit) = brace_expr(&file.tree(), offset) {
123         return Some(edit);
124     }
125
126     if let Some(edit) = brace_use_path(&file.tree(), offset) {
127         return Some(edit);
128     }
129
130     return None;
131
132     fn brace_use_path(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
133         let segment: ast::PathSegment = find_node_at_offset(file.syntax(), offset)?;
134         if segment.syntax().text_range().start() != offset {
135             return None;
136         }
137
138         let tree: ast::UseTree = find_node_at_offset(file.syntax(), offset)?;
139
140         Some(TextEdit::insert(
141             tree.syntax().text_range().end() + TextSize::of("{"),
142             "}".to_string(),
143         ))
144     }
145
146     fn brace_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
147         let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?;
148         if expr.syntax().text_range().start() != offset {
149             return None;
150         }
151
152         // Enclose the outermost expression starting at `offset`
153         while let Some(parent) = expr.syntax().parent() {
154             if parent.text_range().start() != expr.syntax().text_range().start() {
155                 break;
156             }
157
158             match ast::Expr::cast(parent) {
159                 Some(parent) => expr = parent,
160                 None => break,
161             }
162         }
163
164         // If it's a statement in a block, we don't know how many statements should be included
165         if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) {
166             return None;
167         }
168
169         // Insert `}` right after the expression.
170         Some(TextEdit::insert(
171             expr.syntax().text_range().end() + TextSize::of("{"),
172             "}".to_string(),
173         ))
174     }
175 }
176
177 /// Returns an edit which should be applied after `=` was typed. Primarily,
178 /// this works when adding `let =`.
179 // FIXME: use a snippet completion instead of this hack here.
180 fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
181     if !stdx::always!(file.syntax().text().char_at(offset) == Some('=')) {
182         return None;
183     }
184
185     if let Some(edit) = let_stmt(file, offset) {
186         return Some(edit);
187     }
188     if let Some(edit) = assign_expr(file, offset) {
189         return Some(edit);
190     }
191     if let Some(edit) = assign_to_eq(file, offset) {
192         return Some(edit);
193     }
194
195     return None;
196
197     fn assign_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
198         let binop: ast::BinExpr = find_node_at_offset(file.syntax(), offset)?;
199         if !matches!(binop.op_kind(), Some(ast::BinaryOp::Assignment { op: None })) {
200             return None;
201         }
202
203         // Parent must be `ExprStmt` or `StmtList` for `;` to be valid.
204         if let Some(expr_stmt) = ast::ExprStmt::cast(binop.syntax().parent()?) {
205             if expr_stmt.semicolon_token().is_some() {
206                 return None;
207             }
208         } else {
209             if !ast::StmtList::can_cast(binop.syntax().parent()?.kind()) {
210                 return None;
211             }
212         }
213
214         let expr = binop.rhs()?;
215         let expr_range = expr.syntax().text_range();
216         if expr_range.contains(offset) && offset != expr_range.start() {
217             return None;
218         }
219         if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
220             return None;
221         }
222         let offset = expr.syntax().text_range().end();
223         Some(TextEdit::insert(offset, ";".to_string()))
224     }
225
226     /// `a =$0 b;` removes the semicolon if an expression is valid in this context.
227     fn assign_to_eq(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
228         let binop: ast::BinExpr = find_node_at_offset(file.syntax(), offset)?;
229         if !matches!(binop.op_kind(), Some(ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated: false })))
230         {
231             return None;
232         }
233
234         let expr_stmt = ast::ExprStmt::cast(binop.syntax().parent()?)?;
235         let semi = expr_stmt.semicolon_token()?;
236
237         if expr_stmt.syntax().next_sibling().is_some() {
238             // Not the last statement in the list.
239             return None;
240         }
241
242         Some(TextEdit::delete(semi.text_range()))
243     }
244
245     fn let_stmt(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
246         let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
247         if let_stmt.semicolon_token().is_some() {
248             return None;
249         }
250         let expr = let_stmt.initializer()?;
251         let expr_range = expr.syntax().text_range();
252         if expr_range.contains(offset) && offset != expr_range.start() {
253             return None;
254         }
255         if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
256             return None;
257         }
258         let offset = let_stmt.syntax().text_range().end();
259         Some(TextEdit::insert(offset, ";".to_string()))
260     }
261 }
262
263 /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
264 fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
265     if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) {
266         return None;
267     }
268     let whitespace =
269         file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;
270
271     // if prior is fn call over multiple lines dont indent
272     // or if previous is method call over multiples lines keep that indent
273     let current_indent = {
274         let text = whitespace.text();
275         let (_prefix, suffix) = text.rsplit_once('\n')?;
276         suffix
277     };
278     let current_indent_len = TextSize::of(current_indent);
279
280     let parent = whitespace.syntax().parent()?;
281     // Make sure dot is a part of call chain
282     let receiver = if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
283         field_expr.expr()?
284     } else if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent.clone()) {
285         method_call_expr.receiver()?
286     } else {
287         return None;
288     };
289
290     let receiver_is_multiline = receiver.syntax().text().find_char('\n').is_some();
291     let target_indent = match (receiver, receiver_is_multiline) {
292         // if receiver is multiline field or method call, just take the previous `.` indentation
293         (ast::Expr::MethodCallExpr(expr), true) => {
294             expr.dot_token().as_ref().map(IndentLevel::from_token)
295         }
296         (ast::Expr::FieldExpr(expr), true) => {
297             expr.dot_token().as_ref().map(IndentLevel::from_token)
298         }
299         // if receiver is multiline expression, just keeps its indentation
300         (_, true) => Some(IndentLevel::from_node(&parent)),
301         _ => None,
302     };
303     let target_indent = match target_indent {
304         Some(x) => x,
305         // in all other cases, take previous indentation and indent once
306         None => IndentLevel::from_node(&parent) + 1,
307     }
308     .to_string();
309
310     if current_indent_len == TextSize::of(&target_indent) {
311         return None;
312     }
313
314     Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent))
315 }
316
317 /// Add closing `>` for generic arguments/parameters.
318 fn on_left_angle_typed(file: &SourceFile, offset: TextSize) -> Option<ExtendedTextEdit> {
319     let file_text = file.syntax().text();
320     if !stdx::always!(file_text.char_at(offset) == Some('<')) {
321         return None;
322     }
323
324     // Find the next non-whitespace char in the line.
325     let mut next_offset = offset + TextSize::of('<');
326     while file_text.char_at(next_offset) == Some(' ') {
327         next_offset += TextSize::of(' ')
328     }
329     if file_text.char_at(next_offset) == Some('>') {
330         return None;
331     }
332
333     let range = TextRange::at(offset, TextSize::of('<'));
334     if let Some(t) = file.syntax().token_at_offset(offset).left_biased() {
335         if T![impl] == t.kind() {
336             return Some(ExtendedTextEdit {
337                 edit: TextEdit::replace(range, "<$0>".to_string()),
338                 is_snippet: true,
339             });
340         }
341     }
342
343     if ancestors_at_offset(file.syntax(), offset)
344         .find(|n| {
345             ast::GenericParamList::can_cast(n.kind()) || ast::GenericArgList::can_cast(n.kind())
346         })
347         .is_some()
348     {
349         return Some(ExtendedTextEdit {
350             edit: TextEdit::replace(range, "<$0>".to_string()),
351             is_snippet: true,
352         });
353     }
354
355     None
356 }
357
358 /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
359 fn on_right_angle_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
360     let file_text = file.syntax().text();
361     if !stdx::always!(file_text.char_at(offset) == Some('>')) {
362         return None;
363     }
364     let after_arrow = offset + TextSize::of('>');
365     if file_text.char_at(after_arrow) != Some('{') {
366         return None;
367     }
368     if find_node_at_offset::<ast::RetType>(file.syntax(), offset).is_none() {
369         return None;
370     }
371
372     Some(TextEdit::insert(after_arrow, " ".to_string()))
373 }
374
375 #[cfg(test)]
376 mod tests {
377     use test_utils::{assert_eq_text, extract_offset};
378
379     use super::*;
380
381     impl ExtendedTextEdit {
382         fn apply(&self, text: &mut String) {
383             self.edit.apply(text);
384         }
385     }
386
387     fn do_type_char(char_typed: char, before: &str) -> Option<String> {
388         let (offset, mut before) = extract_offset(before);
389         let edit = TextEdit::insert(offset, char_typed.to_string());
390         edit.apply(&mut before);
391         let parse = SourceFile::parse(&before);
392         on_char_typed_inner(&parse, offset, char_typed).map(|it| {
393             it.apply(&mut before);
394             before.to_string()
395         })
396     }
397
398     fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
399         let actual = do_type_char(char_typed, ra_fixture_before)
400             .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
401
402         assert_eq_text!(ra_fixture_after, &actual);
403     }
404
405     fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
406         let file_change = do_type_char(char_typed, ra_fixture_before);
407         assert!(file_change.is_none())
408     }
409
410     #[test]
411     fn test_semi_after_let() {
412         //     do_check(r"
413         // fn foo() {
414         //     let foo =$0
415         // }
416         // ", r"
417         // fn foo() {
418         //     let foo =;
419         // }
420         // ");
421         type_char(
422             '=',
423             r#"
424 fn foo() {
425     let foo $0 1 + 1
426 }
427 "#,
428             r#"
429 fn foo() {
430     let foo = 1 + 1;
431 }
432 "#,
433         );
434         //     do_check(r"
435         // fn foo() {
436         //     let foo =$0
437         //     let bar = 1;
438         // }
439         // ", r"
440         // fn foo() {
441         //     let foo =;
442         //     let bar = 1;
443         // }
444         // ");
445     }
446
447     #[test]
448     fn test_semi_after_assign() {
449         type_char(
450             '=',
451             r#"
452 fn f() {
453     i $0 0
454 }
455 "#,
456             r#"
457 fn f() {
458     i = 0;
459 }
460 "#,
461         );
462         type_char(
463             '=',
464             r#"
465 fn f() {
466     i $0 0
467     i
468 }
469 "#,
470             r#"
471 fn f() {
472     i = 0;
473     i
474 }
475 "#,
476         );
477         type_char_noop(
478             '=',
479             r#"
480 fn f(x: u8) {
481     if x $0
482 }
483 "#,
484         );
485         type_char_noop(
486             '=',
487             r#"
488 fn f(x: u8) {
489     if x $0 {}
490 }
491 "#,
492         );
493         type_char_noop(
494             '=',
495             r#"
496 fn f(x: u8) {
497     if x $0 0 {}
498 }
499 "#,
500         );
501         type_char_noop(
502             '=',
503             r#"
504 fn f() {
505     g(i $0 0);
506 }
507 "#,
508         );
509     }
510
511     #[test]
512     fn assign_to_eq() {
513         type_char(
514             '=',
515             r#"
516 fn f(a: u8) {
517     a =$0 0;
518 }
519 "#,
520             r#"
521 fn f(a: u8) {
522     a == 0
523 }
524 "#,
525         );
526         type_char(
527             '=',
528             r#"
529 fn f(a: u8) {
530     a $0= 0;
531 }
532 "#,
533             r#"
534 fn f(a: u8) {
535     a == 0
536 }
537 "#,
538         );
539         type_char_noop(
540             '=',
541             r#"
542 fn f(a: u8) {
543     let e = a =$0 0;
544 }
545 "#,
546         );
547         type_char_noop(
548             '=',
549             r#"
550 fn f(a: u8) {
551     let e = a =$0 0;
552     e
553 }
554 "#,
555         );
556     }
557
558     #[test]
559     fn indents_new_chain_call() {
560         type_char(
561             '.',
562             r#"
563 fn main() {
564     xs.foo()
565     $0
566 }
567             "#,
568             r#"
569 fn main() {
570     xs.foo()
571         .
572 }
573             "#,
574         );
575         type_char_noop(
576             '.',
577             r#"
578 fn main() {
579     xs.foo()
580         $0
581 }
582             "#,
583         )
584     }
585
586     #[test]
587     fn indents_new_chain_call_with_semi() {
588         type_char(
589             '.',
590             r"
591 fn main() {
592     xs.foo()
593     $0;
594 }
595             ",
596             r#"
597 fn main() {
598     xs.foo()
599         .;
600 }
601             "#,
602         );
603         type_char_noop(
604             '.',
605             r#"
606 fn main() {
607     xs.foo()
608         $0;
609 }
610             "#,
611         )
612     }
613
614     #[test]
615     fn indents_new_chain_call_with_let() {
616         type_char(
617             '.',
618             r#"
619 fn main() {
620     let _ = foo
621     $0
622     bar()
623 }
624 "#,
625             r#"
626 fn main() {
627     let _ = foo
628         .
629     bar()
630 }
631 "#,
632         );
633     }
634
635     #[test]
636     fn indents_continued_chain_call() {
637         type_char(
638             '.',
639             r#"
640 fn main() {
641     xs.foo()
642         .first()
643     $0
644 }
645             "#,
646             r#"
647 fn main() {
648     xs.foo()
649         .first()
650         .
651 }
652             "#,
653         );
654         type_char_noop(
655             '.',
656             r#"
657 fn main() {
658     xs.foo()
659         .first()
660         $0
661 }
662             "#,
663         );
664     }
665
666     #[test]
667     fn indents_middle_of_chain_call() {
668         type_char(
669             '.',
670             r#"
671 fn source_impl() {
672     let var = enum_defvariant_list().unwrap()
673     $0
674         .nth(92)
675         .unwrap();
676 }
677             "#,
678             r#"
679 fn source_impl() {
680     let var = enum_defvariant_list().unwrap()
681         .
682         .nth(92)
683         .unwrap();
684 }
685             "#,
686         );
687         type_char_noop(
688             '.',
689             r#"
690 fn source_impl() {
691     let var = enum_defvariant_list().unwrap()
692         $0
693         .nth(92)
694         .unwrap();
695 }
696             "#,
697         );
698     }
699
700     #[test]
701     fn dont_indent_freestanding_dot() {
702         type_char_noop(
703             '.',
704             r#"
705 fn main() {
706     $0
707 }
708             "#,
709         );
710         type_char_noop(
711             '.',
712             r#"
713 fn main() {
714 $0
715 }
716             "#,
717         );
718     }
719
720     #[test]
721     fn adds_space_after_return_type() {
722         type_char(
723             '>',
724             r#"
725 fn foo() -$0{ 92 }
726 "#,
727             r#"
728 fn foo() -> { 92 }
729 "#,
730         );
731     }
732
733     #[test]
734     fn adds_closing_brace_for_expr() {
735         type_char(
736             '{',
737             r#"
738 fn f() { match () { _ => $0() } }
739             "#,
740             r#"
741 fn f() { match () { _ => {()} } }
742             "#,
743         );
744         type_char(
745             '{',
746             r#"
747 fn f() { $0() }
748             "#,
749             r#"
750 fn f() { {()} }
751             "#,
752         );
753         type_char(
754             '{',
755             r#"
756 fn f() { let x = $0(); }
757             "#,
758             r#"
759 fn f() { let x = {()}; }
760             "#,
761         );
762         type_char(
763             '{',
764             r#"
765 fn f() { let x = $0a.b(); }
766             "#,
767             r#"
768 fn f() { let x = {a.b()}; }
769             "#,
770         );
771         type_char(
772             '{',
773             r#"
774 const S: () = $0();
775 fn f() {}
776             "#,
777             r#"
778 const S: () = {()};
779 fn f() {}
780             "#,
781         );
782         type_char(
783             '{',
784             r#"
785 const S: () = $0a.b();
786 fn f() {}
787             "#,
788             r#"
789 const S: () = {a.b()};
790 fn f() {}
791             "#,
792         );
793         type_char(
794             '{',
795             r#"
796 fn f() {
797     match x {
798         0 => $0(),
799         1 => (),
800     }
801 }
802             "#,
803             r#"
804 fn f() {
805     match x {
806         0 => {()},
807         1 => (),
808     }
809 }
810             "#,
811         );
812     }
813
814     #[test]
815     fn noop_in_string_literal() {
816         // Regression test for #9351
817         type_char_noop(
818             '{',
819             r##"
820 fn check_with(ra_fixture: &str, expect: Expect) {
821     let base = r#"
822 enum E { T(), R$0, C }
823 use self::E::X;
824 const Z: E = E::C;
825 mod m {}
826 asdasdasdasdasdasda
827 sdasdasdasdasdasda
828 sdasdasdasdasd
829 "#;
830     let actual = completion_list(&format!("{}\n{}", base, ra_fixture));
831     expect.assert_eq(&actual)
832 }
833             "##,
834         );
835     }
836
837     #[test]
838     fn noop_in_item_position_with_macro() {
839         type_char_noop('{', r#"$0println!();"#);
840         type_char_noop(
841             '{',
842             r#"
843 fn main() $0println!("hello");
844 }"#,
845         );
846     }
847
848     #[test]
849     fn adds_closing_brace_for_use_tree() {
850         type_char(
851             '{',
852             r#"
853 use some::$0Path;
854             "#,
855             r#"
856 use some::{Path};
857             "#,
858         );
859         type_char(
860             '{',
861             r#"
862 use some::{Path, $0Other};
863             "#,
864             r#"
865 use some::{Path, {Other}};
866             "#,
867         );
868         type_char(
869             '{',
870             r#"
871 use some::{$0Path, Other};
872             "#,
873             r#"
874 use some::{{Path}, Other};
875             "#,
876         );
877         type_char(
878             '{',
879             r#"
880 use some::path::$0to::Item;
881             "#,
882             r#"
883 use some::path::{to::Item};
884             "#,
885         );
886         type_char(
887             '{',
888             r#"
889 use some::$0path::to::Item;
890             "#,
891             r#"
892 use some::{path::to::Item};
893             "#,
894         );
895         type_char(
896             '{',
897             r#"
898 use $0some::path::to::Item;
899             "#,
900             r#"
901 use {some::path::to::Item};
902             "#,
903         );
904         type_char(
905             '{',
906             r#"
907 use some::path::$0to::{Item};
908             "#,
909             r#"
910 use some::path::{to::{Item}};
911             "#,
912         );
913         type_char(
914             '{',
915             r#"
916 use $0Thing as _;
917             "#,
918             r#"
919 use {Thing as _};
920             "#,
921         );
922
923         type_char_noop(
924             '{',
925             r#"
926 use some::pa$0th::to::Item;
927             "#,
928         );
929     }
930
931     #[test]
932     fn adds_closing_angle_bracket_for_generic_args() {
933         type_char(
934             '<',
935             r#"
936 fn foo() {
937     bar::$0
938 }
939             "#,
940             r#"
941 fn foo() {
942     bar::<$0>
943 }
944             "#,
945         );
946
947         type_char(
948             '<',
949             r#"
950 fn foo(bar: &[u64]) {
951     bar.iter().collect::$0();
952 }
953             "#,
954             r#"
955 fn foo(bar: &[u64]) {
956     bar.iter().collect::<$0>();
957 }
958             "#,
959         );
960     }
961
962     #[test]
963     fn adds_closing_angle_bracket_for_generic_params() {
964         type_char(
965             '<',
966             r#"
967 fn foo$0() {}
968             "#,
969             r#"
970 fn foo<$0>() {}
971             "#,
972         );
973         type_char(
974             '<',
975             r#"
976 fn foo$0
977             "#,
978             r#"
979 fn foo<$0>
980             "#,
981         );
982         type_char(
983             '<',
984             r#"
985 struct Foo$0 {}
986             "#,
987             r#"
988 struct Foo<$0> {}
989             "#,
990         );
991         type_char(
992             '<',
993             r#"
994 struct Foo$0();
995             "#,
996             r#"
997 struct Foo<$0>();
998             "#,
999         );
1000         type_char(
1001             '<',
1002             r#"
1003 struct Foo$0
1004             "#,
1005             r#"
1006 struct Foo<$0>
1007             "#,
1008         );
1009         type_char(
1010             '<',
1011             r#"
1012 enum Foo$0
1013             "#,
1014             r#"
1015 enum Foo<$0>
1016             "#,
1017         );
1018         type_char(
1019             '<',
1020             r#"
1021 trait Foo$0
1022             "#,
1023             r#"
1024 trait Foo<$0>
1025             "#,
1026         );
1027         type_char(
1028             '<',
1029             r#"
1030 type Foo$0 = Bar;
1031             "#,
1032             r#"
1033 type Foo<$0> = Bar;
1034             "#,
1035         );
1036         type_char(
1037             '<',
1038             r#"
1039 impl$0 Foo {}
1040             "#,
1041             r#"
1042 impl<$0> Foo {}
1043             "#,
1044         );
1045         type_char(
1046             '<',
1047             r#"
1048 impl<T> Foo$0 {}
1049             "#,
1050             r#"
1051 impl<T> Foo<$0> {}
1052             "#,
1053         );
1054         type_char(
1055             '<',
1056             r#"
1057 impl Foo$0 {}
1058             "#,
1059             r#"
1060 impl Foo<$0> {}
1061             "#,
1062         );
1063     }
1064
1065     #[test]
1066     fn dont_add_closing_angle_bracket_for_comparison() {
1067         type_char_noop(
1068             '<',
1069             r#"
1070 fn main() {
1071     42$0
1072 }
1073             "#,
1074         );
1075         type_char_noop(
1076             '<',
1077             r#"
1078 fn main() {
1079     42 $0
1080 }
1081             "#,
1082         );
1083         type_char_noop(
1084             '<',
1085             r#"
1086 fn main() {
1087     let foo = 42;
1088     foo $0
1089 }
1090             "#,
1091         );
1092     }
1093
1094     #[test]
1095     fn dont_add_closing_angle_bracket_if_it_is_already_there() {
1096         type_char_noop(
1097             '<',
1098             r#"
1099 fn foo() {
1100     bar::$0>
1101 }
1102             "#,
1103         );
1104         type_char_noop(
1105             '<',
1106             r#"
1107 fn foo(bar: &[u64]) {
1108     bar.iter().collect::$0   >();
1109 }
1110             "#,
1111         );
1112         type_char_noop(
1113             '<',
1114             r#"
1115 fn foo$0>() {}
1116             "#,
1117         );
1118         type_char_noop(
1119             '<',
1120             r#"
1121 fn foo$0>
1122             "#,
1123         );
1124         type_char_noop(
1125             '<',
1126             r#"
1127 struct Foo$0> {}
1128             "#,
1129         );
1130         type_char_noop(
1131             '<',
1132             r#"
1133 struct Foo$0>();
1134             "#,
1135         );
1136         type_char_noop(
1137             '<',
1138             r#"
1139 struct Foo$0>
1140             "#,
1141         );
1142         type_char_noop(
1143             '<',
1144             r#"
1145 enum Foo$0>
1146             "#,
1147         );
1148         type_char_noop(
1149             '<',
1150             r#"
1151 trait Foo$0>
1152             "#,
1153         );
1154         type_char_noop(
1155             '<',
1156             r#"
1157 type Foo$0> = Bar;
1158             "#,
1159         );
1160         type_char_noop(
1161             '<',
1162             r#"
1163 impl$0> Foo {}
1164             "#,
1165         );
1166         type_char_noop(
1167             '<',
1168             r#"
1169 impl<T> Foo$0> {}
1170             "#,
1171         );
1172         type_char_noop(
1173             '<',
1174             r#"
1175 impl Foo$0> {}
1176             "#,
1177         );
1178     }
1179
1180     #[test]
1181     fn regression_629() {
1182         type_char_noop(
1183             '.',
1184             r#"
1185 fn foo() {
1186     CompletionItem::new(
1187         CompletionKind::Reference,
1188         ctx.source_range(),
1189         field.name().to_string(),
1190     )
1191     .foo()
1192     $0
1193 }
1194 "#,
1195         );
1196         type_char_noop(
1197             '.',
1198             r#"
1199 fn foo() {
1200     CompletionItem::new(
1201         CompletionKind::Reference,
1202         ctx.source_range(),
1203         field.name().to_string(),
1204     )
1205     $0
1206 }
1207 "#,
1208         );
1209     }
1210 }